C语言中static与extern关键字的深入解析

在这里插入图片描述
在C语言编程中,staticextern是两个非常重要的关键字,它们各自有着独特的用途。本文将深入探讨这两个关键字的工作原理、底层实现机制以及在实际开发中的应用。

static关键字

1. 原理与作用

static关键字用于声明变量或函数具有特定的作用域和生命周期。它可以应用于局部变量、全局变量以及函数。

局部变量
  • 作用域static局部变量的作用域限于声明它的函数或代码块。
  • 生命周期static局部变量在整个程序执行期间存在,即使函数调用结束之后也不会被销毁。
全局变量
  • 作用域static全局变量的作用域限于声明它的源文件。
  • 链接属性static全局变量默认具有内部链接属性,即只能在声明它的源文件内访问。
函数
  • 作用域static函数的作用域限于声明它的源文件。
  • 链接属性static函数默认具有内部链接属性,即只能在声明它的源文件内访问。

2. 底层实现

在底层实现上,static关键字通过改变变量的链接属性和存储位置来实现其功能。

存储位置
  • 静态存储区static变量通常被存储在静态存储区,而非堆栈或堆上。这意味着它们在整个程序运行期间一直存在,而不是随着函数调用的开始和结束而创建和销毁。
链接属性
  • 内部链接static变量和函数具有内部链接属性,意味着它们只能在声明它们的源文件内部被访问。这有助于减少链接时的冲突,同时也提高了代码的安全性和封装性。

3. 使用场景

  • 保持状态:使用static局部变量可以在多次函数调用之间保持状态。这对于需要在函数调用间保存计算结果的情况非常有用。
  • 隐藏实现:使用static函数可以隐藏实现细节,使其他源文件无法访问。这对于模块化编程和代码组织非常有用。

4. 示例代码

考虑以下示例,展示static局部变量的使用:

#include <stdio.h>void count_calls() {static int call_count = 0;call_count++;printf("Function called %d times.\n", call_count);
}int main() {count_calls();count_calls();count_calls();return 0;
}

5. 注意事项

  • 初始化static局部变量仅在第一次使用时初始化一次。这意味着在函数的后续调用中,static局部变量保留上次调用结束时的值。
  • 作用域限制static变量和函数的作用域仅限于声明它们的源文件或函数。

6. 更深层次的讨论

存储类别
  • 静态存储类别static关键字改变了变量的存储类别,使其成为静态存储类别,这意味着它在程序的整个生命周期内都存在。这与自动存储类别(如普通的局部变量)形成对比,后者在每次函数调用时创建并在返回时销毁。
内存布局
  • 静态数据段static变量在程序的静态数据段中分配内存。静态数据段是程序在启动时分配的内存区域,用于存放全局变量和静态局部变量。这些变量在程序的整个生命周期内都保留在内存中。
编译器优化
  • 编译器行为:编译器可以利用static变量的存在来做出更有效的优化决策。例如,如果一个static变量在某个函数中被频繁使用,编译器可能会选择将该变量保留在寄存器中,以减少内存访问次数。

7. 实现细节

汇编代码示例

考虑以下C代码:

#include <stdio.h>void count_calls() {static int call_count = 0;call_count++;printf("Function called %d times.\n", call_count);
}int main() {count_calls();count_calls();count_calls();return 0;
}

编译后的汇编代码可能会包含类似如下指令:

count_calls:movl    $1, %eaxleal    -4(%ebp), %edxincl    (%edx)movl    (%edx), %eaxmovl    %eax, %edxleal    .LC0(%rip), %eaxmovl    %edx, %esimovl    $0, %edicall    printfret

这里,incl指令用于递增call_count变量的值,而movl指令用于加载和存储变量值。编译器确保了每次调用count_calls函数时都会正确地更新call_count的值。

8. 性能影响

  • 内存访问:由于static变量在静态存储区中,访问这些变量通常比访问栈上的变量慢,但比访问堆上的变量快。
  • 优化机会:编译器可以根据static变量的特性进行更高效的优化,如寄存器分配和循环展开。

在这里插入图片描述

extern关键字

1. 原理与作用

extern关键字用于声明一个变量或函数是在另一个源文件中定义的。它主要用于解决变量和函数的可见性问题。

外部变量
  • 作用域extern变量可以在多个源文件中声明,但只能在一个源文件中定义。
  • 链接属性extern变量具有外部链接属性,可以在多个源文件中访问。
外部函数
  • 作用域extern函数可以在多个源文件中声明,但只能在一个源文件中定义。
  • 链接属性extern函数具有外部链接属性,可以在多个源文件中访问。

2. 底层实现

在底层实现上,extern关键字通过改变变量或函数的链接属性来实现其功能。

链接属性
  • 外部链接extern变量和函数具有外部链接属性,意味着它们可以在多个源文件之间共享。这意味着它们在链接时会被合并成一个单一的实例。

3. 使用场景

  • 跨文件共享:使用extern可以在不同源文件之间共享变量或函数。这对于构建大型项目时的模块化非常重要。
  • 模块化编程:使用extern可以将实现细节封装在一个源文件中,而其他源文件只需要知道接口即可。这样可以提高代码的可读性和可维护性。

4. 示例代码

考虑以下示例,展示extern变量和函数的使用:

// file1.c
#include <stdio.h>extern int global_var;
extern void print_global();int main() {global_var = 42;print_global();return 0;
}// file2.c
#include <stdio.h>int global_var;
void print_global() {printf("Global variable value: %d\n", global_var);
}// Makefile
CC=gcc
CFLAGS=-Wall -Wextraall: programprogram: file1.o file2.o$(CC) $(CFLAGS) -o program file1.o file2.ofile1.o: file1.c$(CC) $(CFLAGS) -c file1.cfile2.o: file2.c$(CC) $(CFLAGS) -c file2.cclean:rm -f *.o program

5. 注意事项

  • 定义与声明:必须确保extern变量或函数在一个源文件中有定义,在其他源文件中只有声明。这是为了避免链接错误。
  • 链接问题:如果extern变量或函数在多个源文件中有定义,可能会导致链接错误。这是因为链接器不允许相同的符号出现在多个位置。

6. 更深层次的讨论

链接过程
  • 合并定义:在链接过程中,extern变量和函数的定义和声明会被合并。如果一个符号在多个源文件中有定义,链接器会报错,指出重复定义的问题。
  • 符号解析:链接器负责解析所有的符号引用,确保每个符号都有一个唯一的定义。
动态链接
  • 动态链接库:在动态链接环境下,extern变量和函数的定义可以位于动态链接库中,这使得它们可以在运行时被加载和使用。这种方式适用于需要在多个程序间共享代码的情况。

7. 实现细节

汇编代码示例

考虑以下C代码:

// file1.c
#include <stdio.h>extern int global_var;
extern void print_global();int main() {global_var = 42;print_global();return 0;
}// file2.c
#include <stdio.h>int global_var;
void print_global() {printf("Global variable value: %d\n", global_var);
}

编译后的汇编代码可能会包含类似如下指令:

// 文件 file1.c 的汇编代码
main:movl    $42, %eaxmovl    %eax, -4(%ebp)leal    .LC0(%rip), %eaxmovl    -4(%ebp), %edxmovl    %edx, %esimovl    $0, %edicall    print_globalmovl    $0, %eaxret// 文件 file2.c 的汇编代码
print_global:movl    -4(%ebp), %eaxleal    .LC0(%rip), %edxmovl    %eax, %esimovl    $0, %edicall    printfret

这里,main函数中的movl指令用于将值42存储到global_var中,而print_global函数中的movl指令用于加载global_var的值并打印出来。

8. 性能影响

  • 链接时间开销:使用extern变量或函数可能会增加链接时间开销,因为在链接时需要解析所有的外部引用。
  • 动态链接开销:在动态链接环境下,使用extern变量或函数可能会导致额外的运行时开销,因为链接库可能需要在运行时动态加载。

总结

staticextern虽然都是用来修饰变量和函数的关键字,但它们的作用完全不同。static关注的是变量或函数的作用域和生命周期,而extern则关注变量或函数的可见性和链接属性。在实际编程中,合理使用这两个关键字可以显著提升代码的模块化程度和可维护性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/52174.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【自考zt】【数据结构】【22.04】

一、单选 二、填空 三、解答 四、算法阅读 五、算法设计

Tensorflow常见激活函数 -- Tensorflow自学笔记10

激活函数 激活函数是用来加入非线性因素的&#xff0c;因为线性模型的表达能力不够。引入非线性激活函数&#xff0c;可使深层神经网络的表达能力更加强大。 一. 什么是优秀的激活函数&#xff1f; 优秀的激活函数应满足: 1. 非线性: 激活函数非线性时&#xff0c;多层神经网…

第四届摩纳哥智能化可持续发展码头交流会

第四届摩纳哥智能化可持续发展码头交流会 摩纳哥游艇码头顾问公司&#xff08;M3&#xff09;认为游艇行业的绿色转型需要做到从游艇本身到游艇码头的360度全方位可持续化发展&#xff0c;因此&#xff0c;继今年3月的摩纳哥智能游艇交流会后&#xff0c;他们将于2024年9月22日…

<数据集>二维码识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;1601张 标注数量(xml文件个数)&#xff1a;1601 标注数量(txt文件个数)&#xff1a;1601 标注类别数&#xff1a;1 标注类别名称&#xff1a;[QR] 序号类别名称图片数框数1QR16016286 使用标注工具&#xff1a;l…

swagger简单使用学习

注意 一下基于spring-boot 3.0.2版本&#xff0c;该版本不支持springfox-swagger2 2.9.2会报错&#xff0c;无法访问swagger 安装 在pomx文件中添加对应的依赖 <!-- swagger --><dependency><groupId>org.springdoc</groupId><artifactId>spr…

C++_多态详解

多态的概念 概念&#xff1a;需要去完成某个行为时&#xff0c;当 不同的对象去完成 会产生出不同的状态。通俗点说就是 不同类型的对象去做同一个行为&#xff0c;产生的结果不同。 多态的定义及实现 虚函数 定义&#xff1a;即被virtual修饰的类成员函数称为虚函数 虚函…

【论文分享】sNPU: Trusted Execution Environments on Integrated NPUs 24‘ISCA

目录 AbstractINTRODUCTIONBACKGROUND AND RELATED WORKTrusted Execution Environment (TEE)Neural Processing Unit (NPU)Integrated NPU v.s. Discrete NPU Multi-tasking Requirements for NPUsLow NPU utilization for a single ML workloadSimultaneous execution of bot…

代码审计总结

代码审计总结 概述 一、代码审计 1.1什么是代码审计&#xff1f; 1.2为什么要执行代码审核&#xff1f; 1.3代码审计的好处 二、代码审计流程 2.1代码检查方法 2.2代码检查项目 2.3编码规范 2.4代码检查规范 2.5缺陷检查表 2.6代码审计复查 2.7代码审计结果总结 三…

Redis高级----五种数据结构及其底层实现

目前已更新系列&#xff1a; 当前&#xff1a;Redis高级---面试总结5种数据结构的底层实现 Redis高级----主从、哨兵、分片、脑裂原理-CSDN博客 Redis高级---面试总结内存过期策略及其淘汰策略 计算机网络--面试知识总结一 计算机网络-----面试知识总结二 计算机网络--面…

初始MYSQL数据库(2)——创建、查询、更新、删除数据表的相关操作

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; MYSQL 前面我们学习了创建、删除数据库以及创建、查看、删除数据表的相关操作。 我们知道数据库中所存储的数据其实就是数据表中一条一条的记…

前端技术(六)—— AJAX详解

一、原生 AJAX 1. AJAX 简介 AJAX 全称为 Asynchronous JavaScript And XML&#xff0c;就是异步的 JS 和 XML。 通过 AJAX 可以在浏览器中向服务器发送异步请求&#xff0c;最大的优势&#xff1a;无刷新获取数据。 AJAX 不是新的编程语言&#xff0c;而是一种将现有的标准组…

大语言模型(LLM)如何更好地继续预训练(Continue PreTraining)

预训练&#xff08;Pretraining&#xff09;是一个非常消耗资源的工作&#xff0c;尤其在 LLM 时代。随着LLama2的开源&#xff0c;越来越多人都开始尝试在这个强大的英文基座模型上进行中文增强。但&#xff0c;我们如何才能保证模型在既学到「中文知识」的情况下&#xff0c;…

灰光模块,彩光模块-介绍

1. 引用 知识分享系列一&#xff1a;5G基础知识-CSDN博客 5G前传的最新进展-CSDN博客 灰光和彩光_通信行业5G招标系列点评之二&#xff1a;一文读懂5G前传-光纤、灰光、彩光、CWDM、LWDM、MWDM...-CSDN博客 ADOP带你了解&#xff1a;CWDM、DWDM、MWDM、LWDM&#xff1a;快速…

网络编程day03(网络体系结构、调试命令、TCP/IP对比)

目录 1》网络的体系结构 1> OSI模型 2> TCP/IP模型 3> 常见网络协议 4> DNS域名解析协议 2》 网络调试命令 1> ping&#xff1a;测试网络连通性&#xff08;ICMP&#xff09; 2> netstat 3》Dos &#xff08;拒绝式服务&#xff09;攻击&#xff1f;…

【大模型-RAG】RAG最佳实践论文及项目解读

文章目录 论文概述RAG工作流程核心代码解读软件架构查询引擎构建数据加载与索引创建微调嵌入模型 项目应用结论 在人工智能领域&#xff0c;大型语言模型&#xff08;LLMs&#xff09;因其强大的文本生成能力而备受关注。然而&#xff0c;这些模型在生成信息时可能会产生过时的…

代码随想录:96. 不同的二叉搜索树

96. 不同的二叉搜索树 class Solution { public:int numTrees(int n) {int dp[30]{0};//由i个结点组成的二叉搜索树有多少种dp[0]1; for(int i1;i<n;i)for(int j0;j<i;j)//j表示根节点左子树有j个结点dp[i]dp[j]*dp[i-j-1];//对根节点左右子树结点数量遍历//数量有左子树…

什么是数据结构三要素?

目录 1.逻辑结构 2.数据的存储结构 3.数据的运算 1.逻辑结构 逻辑结构是指数据元素之间的逻辑关系&#xff0c;即从逻辑关系上描述数据。 它与数据的存储无关&#xff0c;是独立于计算机的。数据的逻辑结构非为线性结构和非线性结构&#xff0c;线性表是典型的线性结构&am…

ELK学习笔记——如何给Kibana新增用户和角色

Kibana新增用户和角色 首先用超管账号登录上Kibana&#xff0c;按照下面步骤操作 1、创建角色 按图操作 2、创建用户 按图操作 3、给用户分配角色 至此&#xff0c;角色和用户绑定成功&#xff1b; 最后&#xff0c;可以退出管理员账号&#xff0c;登录这个新…

【MATLAB】FIR滤波器的MATLAB实现

FIR滤波器的MATLAB实现 FIR滤波器的设计fir1函数fir2函数 与IIR滤波器相比&#xff0c;FIR滤波器既有其优势也有其局限性。FIR滤波器的主要优点包括&#xff1a; 精确的线性相位响应&#xff1b;永远保持稳定性&#xff1b;设计方法通常是线性的&#xff1b;在硬件实现中具有更…

Django学习实战篇二(适合略有基础的新手小白学习)(从0开发项目)

前言&#xff1a; 从这一章开始&#xff0c;我们来创建项目typeidea&#xff0c;我把它放到了GitHub上。强烈建议你也到GitHub上注册一个账号&#xff08;如果没有的话&#xff09;&#xff0c;然后创建这样的项目。当然&#xff0c;你也可以起一个属于自己的名称。这个项目就是…