C语言中的作用域、链接属性与存储属性

一、作用域(scope)

  1. 代码块作用域

    表示{}之间的区域,下例所示,a可以在不同的代码块里面定义。

    #include<stdio.h>
    
    int main()
    {
    int f(int g){
    return g;
    } int a = 0;
    {
    int a = 2;
    printf("inner: %d\n", a);
    }
    printf("outter: %d\n", a);
    printf("inline function:%d\n", f(10));
    }
    $ ./a.out
    inner: 2
    outter: 0
    inline function:10

C语言居然支持了内部函数,好神奇。

```c
//error: ‘i’ redeclared as different kind of symbol
int fun(int i)
{
int i = 0;
return i;
}
```
ANSI C中,形参的作用域为函数最外层的那个作用域,不能在函数体内声明同名的变量(K&R C可以,屏蔽形参)。
  1. 文件作用域

    任何在所有代码块之外声明的标识符都具有文件作用域(file scope)。并且,通过#include指令包含到其他文件中的声明就好像它们是直接写在那些文件中一样。它们的作用域并不局限于头文件的文件尾。

  2. 原型作用域

    int fun(int a);

    原型作用域(prototype scope)只适用于在函数原型中声明的参数名,将它独立出来只是为了这个名字不能与其他作用域的符号混淆,它其实没有什么用得,甚至都可以不写。事实上,唯一可能出现的冲突就是在同一个原型中不止一次使用同一个名字。

  3. 函数作用域

    它只适用于语句标签,用于goto语句。《C和指针》的作者说愿你永远不需要这方面的知识,哈哈!!!

二、链接属性

当组成一个程序的各个源文件分别被编译之后,所有的目标文件以及那些从一个或多个函数库中引用的函数链接在一起,形成可执行文件。问题是,如果相同的标识符出现在几个不同的源文件中时该怎么办?标识符的链接属性决定如何处理在不同文件中出现的标识符。标识符的作用域与它的链接属性有关,但这两者并不相同。

链接属性一共有3种:

  1. none(无)

    总是被当做单独的个体,也就是说该标识符的多个声明被当作独立不同的实体。

  2. internal(内部)

    在同一个源文件中的所有声明中都指向同一个实体,但位于不同源文件的多个声明则分属不同的实体。

  3. external(外部):

    不论声明多少次、位于几个源文件都表示同一个实体。

    extern和static两个关键字可以用于设定标识符的链接属性。当没有这两个关键字时,默认的链接属性与标识符的作用域相关。

    typedef char *a;
    int b;
    int c(int d)
    {
    int e;
    int f(int g);
    ...
    }

    上面的代码块中,b、c、f有external链接属性,f在本代码中被调用,定义在其他源文件或者库中,所以也是external属性。其他的标识符都是none属性。

    static关键字可以把一个默认为external属性的标识符改为internal,如上例中,可以把b、c的链接属性改为internal,使其在其他源文件中不可见。

    static int b;
    static int c(int d);

    extern 关键中可以把none属性改为external属性。

    // linkage_test1.c
    #include<stdio.h> extern int a; // 可选,因为默认就是external
    // 但是应该写上,增加程序可读性 int main() {
    printf("a = %d\n", a);
    extern int b; // 必需,默认为none
    printf("b = %d\n", b);
    }
    // linkage_test1.c
    int a = 1;
    int b = 2;

    最后,当extern关键字用于源文件中一个标识符的一次声明时,它指定该标识符具有external链接属性,但是,如果它用于该标识符的第2次或者以后的声明时,它并不会更改由第一次声明所指定的链接属性。如下例所示:

    static int i;
    int func()
    {
    extern int i; //i的链接属性仍然为static
    }

三、存储类型

变量的存储类型是指存储变量值的内存类型。变量的存储类型决定变量何时创建、何时销毁以及它的值将保持多久。有三个地方可以用于存储变量:普通内存、运行时堆栈、硬件寄存器。在这三个地方存储的变量具有不同的特性。

  1. 普通内存变量

    变量的缺省存储类型取决于它声明的位置(作用域),凡是在任何代码块之外声明的变量总是存储与静态内存中,也就是不属于堆栈的内存,这类变量成为静态变量(static),可以通过static关键字将一个代码块内部变量由堆栈类型变为静态类型。静态类型的标识符存在ELF文件的.data(已初始化)或者.bss段(未初始化,默认值为0)。这些变量在程序未运行之前(通过内存加载)已经存在。

  2. 堆栈变量

    在代码块内部声明的变量的默认存储类型是自动的(automatic),可以使用关键字auto指定,但它极少使用,因为完全没必要。这些自动变量存在堆栈中。

  3. 硬件寄存器变量

    你可以通过关键字register来指定,提示程序运行时用硬件寄存器来存储该变量,但是编译器可以不鸟你,因为它认为它比你更清楚那个变量该用寄存器那个该用堆栈。

变量的初始化

静态变量只能用常数进行初始化(其他静态变量都不行),如果未显性初始化,则默认值为0;

堆栈变量可以通过任何合法表达式初始化,因为它是在运行时创建,如果不初始化,其默认值为垃圾。

引用

--C和指针/ (美)里科(Reek, K. A.)著;徐波译. —北京:人民邮电出版社,2008. 4(2016. 5重印)

C语言中的作用域、链接属性与存储属性的更多相关文章

  1. C语言中的作用域,链接属性和存储类型

    作用域 当变量在程序的某个部分被声明的时候,他只有在程序的一定渔区才能被访问,编译器可以确认4种不同类型的作用域:文件作用域,函数作用域,代码块作用域和原型作用域 1.代码块作用域:位于一对花括号之间 ...

  2. C/C++语言中变量作用域:局部变量,全局变量,文件级变量

    C/C++语言中的变量分为全局变量和局部变量. 这样的划分方式的根据是变量的可见范围或者叫做作用域. 1 局部变量 局部变量指的是定义在{}中的变量,其作用域也在这个范围内.尽管常见的局部变量都是定义 ...

  3. c语言中变量和函数作用域深究

    首先,函数的作用域和访问权限基本可以参考 C语言中的作用域,链接属性和存储类型 也存在例外情况,比如内联函数 static inline,使用static 修饰 inline之后外部文件也可以访问内联 ...

  4. C语言 变量的作用域和生命周期(转)

    转自 https://blog.csdn.net/u011616739/article/details/62052179 a.普通局部变量 属于某个{},在{}外部不能使用此变量,在{}内部是可以使用 ...

  5. 详解Go语言中的屏蔽现象

    在刚开始学习Go语言的过程中,难免会遇到一些问题,尤其是从其他语言转向Go开发的人员,面对语法及其内部实现的差异,在使用Go开发时也避免不了会踩"坑".本文主要针对Go设计中的屏蔽 ...

  6. ruby中的作用域

    作用域(scope)指的是变量的可达性或可见性.不同类型的变量有不同的作用域规则.与self类似,作用域在程序的执行过程中也在不断的变化,也可以根据上下文推断出"谁在什么作用域中" ...

  7. C语言中的传值调用

    在c语言中每个变量都有两个属性一个是值,一个是址. 比方: int a = 2; 变量a的值是2,变量a的地址,能够用&取地址操作符获取,即&a. 因此以C语言的函数传递中具备两种方式 ...

  8. Swift编程语言学习9—— 存储属性和计算属性

    属性将值跟特定的类.结构或枚举关联.存储属性存储常量或变量作为实例的一部分,计算属性计算(而不是存储)一个值.计算属性能够用于类.结构体和枚举里,存储属性仅仅能用于类和结构体. 存储属性和计算属性通经 ...

  9. 《从零开始学Swift》学习笔记(Day 31)——存储属性

    原创文章,欢迎转载.转载请注明:关东升的博客 Swift中的属性分为存储属性和计算属性,存储属性就是Objective-C中的数据成员,计算属性不存储数据,但可以通过计算其他属性返回数据. 存储属性可 ...

随机推荐

  1. C++之易混淆知识点四---虚函数与虚继承

    C++面向对象中,虚函数与虚继承是两个完全不同的概念. 一.虚函数 C++程序中只要类中含有虚拟函数,编译程序都会为此类生成一个对应的虚拟函数跳转表(vtbl),该虚拟函数跳转表是一个又若干个虚拟函数 ...

  2. ZBrush中如何清除画布中多余图像

    ZBrush是一款数字雕刻与绘画软件,它以强大的功能和直观的工作流程彻底改变了整个三维行业.它的简洁化.智能化和人性化的设计无不让众多用户所折服.刚接触它的用户可能会因为找不到相关命令或不熟悉而觉得它 ...

  3. Iterator与Asyc/Await的实现

    https://wanago.io/2018/04/23/demystifying-generators-implementing-async-await/

  4. 浅谈自底向上的Shell脚本编程及效率优化

    作者:沐星晨 出处:http://blog.csdn.net/sosodream/article/details/6276758 浅谈自底向上的Shell脚本编程及效率优化 小论文,大家多批评指导:) ...

  5. 前端JS 异常处理实践

    前端异常处理,常见的场景是在“异步请求”的操作过程当中,所谓“异常”---就是“不正常”,程序的运行不符合我们的预期. 程序“正常”的处理,是我们在开发过程当中的“重中之重”,是必要的“硬性指标”. ...

  6. 转载:CentOS查看本机公网IP命令

    icanhazip.com 使你在任何地方知道你的公网IP地址 icanhazip.com是一个网址,你在浏览器中输入这个网址,你就能得到你的公网IP地址了. 我在Linux下一般使用curl ica ...

  7. HTML 编码规范

    语法 使用 4 个空格做为一个缩进层级,不允许使用 2 个空格或 tab 字符 在属性上,使用双引号 "",不要使用单引号 '' 属性名 / 属性值全小写,用中划线 - 做分隔符 ...

  8. 如何成为资深的python专家

    相信很多人有这种感受,python很简单,几天就学会了:做了一段时间就觉得python没什么好玩的,就这样. 一种语言有火这么久,必有它存在的道理. 第一.我们要相信她,她就像你的新女朋友一样,她会给 ...

  9. 分库代价高的情况下,如何优化ES解决亿级数据量检索

    数据平台已迭代三个版本,从一开始遇到很多常见的难题,到现在终于有片段时间整理一些已完善的文档,在此分享以供所需朋友的实现参考,但愿能帮助大家少走些弯路,在此篇幅中偏重于ElasticSearch的优化 ...

  10. Redis介绍、安装部署、操作

    学习连接:http://www.runoob.com/redis/redis-tutorial.html 一.Redis介绍 Redis是NoSql的一种. NoSql,全名:Not Only Sql ...