本文共 1686 字,大约阅读时间需要 5 分钟。
最近在研读《程序员的自我修养——链接、装载与库》时,脑海中不由自主地浮现出学习C语言时的那些摸索之路。尤其是当初对extern、static等关键字的理解,至今仍让人感到不自信。因此,我重新翻开了《C和指针》中关于作用域、链接属性和存储类型的相关内容,并结合自己的理解,整理出一篇技术博客。
作用域是程序中标识符可以被访问的区域。它的大小和范围,直接关系到代码的可维护性和调试难度。在编译原理中,编译器会在语义分析阶段检查变量的作用域是否符合规则。
代码块作用域是最常见的作用域类型。任何在花括号{ }之间的代码都属于一个代码块。代码块内的标识符可以在整个代码块内访问,但需要注意的是,若在代码块内再次声明相同名称的变量,则外层声明的变量会被隐藏。
例如:
int a; // 文件作用域{ int b; // 代码块作用域 a = 10; // 隐藏了外层的a} 文件作用域是指标识符在整个源文件范围内都可以访问的作用域。例如,int g;和int func(int x);都具有文件作用域。这也是我们在编写代码时需要将函数声明放在源文件开头的原因。
原型作用域仅适用于函数原型中的参数名。例如,在int func(int x);中,x的作用域仅限于函数原型本身。
函数作用域主要用于语句标签(用于goto语句),在实际编程中较少使用。
总的来说,代码块作用域和文件作用域是最值得关注的两种作用域类型。
链接属性决定了不同源文件中相同标识符的处理方式。它与作用域密切相关,但两者性质有所不同。
具有外部链接属性的标识符在所有源文件中都指同一个实体。缺省情况下,文件作用域的变量和函数具有external链接属性。
具有内部链接属性的标识符在同一源文件内的所有声明都指同一个实体,但在不同源文件中则视为不同的实体。
没有链接属性的标识符被视为独立实体,通常用于不希望标识符在其他文件中被引用。
extern关键字用于为标识符指定external链接属性。它对文件作用域的标识符是可选的,但对代码块作用域的标识符则强制设置为external。
static关键字用于将外部链接属性的标识符改为内部链接属性。需要注意的是,static只对缺省链接属性为external的声明有效。
存储类型决定了变量的内存分配方式和存储位置。
静态变量在程序运行前就已经分配好内存,存储在.data段或.bss段中。静态变量在整个程序执行过程中始终存在,除非程序结束。
缺省情况下,代码块内的变量存储在运行时堆栈中,称为自动变量。若在声明时使用static关键字,则变量将被存储在静态内存中。
寄存器变量提示编译器将变量存储在硬件寄存器中。需要注意的是,register关键字的使用更多是编译器的优化提示,实际存储位置取决于编译器的选择。
静态变量在编译时已经初始化为0,不需要额外开销。自动变量则需要在运行时进行初始化,编译器会在代码块开始处插入赋值语句。
static和extern在不同的上下文中具有不同的含义:
static:将外部链接属性改为内部链接属性。extern:可选项,用于明确标识符的链接属性。static:将变量的存储类型从自动变为静态,但链接属性仍为none。extern:将变量的链接属性从none改为external。通过对作用域、链接属性和存储类型的深入理解,我们可以更好地管理代码的私有化和信息的透明化。这不仅有助于提高代码的可维护性,也能为程序的性能优化提供重要依据。在实际编程中,合理使用static和extern关键字,可以让我们的代码更高效、更易于管理。
转载地址:http://derkz.baihongyu.com/