深度解析C语言中的变量作用域、链接和存储期的含义

在c中变量有三种性质:
1、存储期限:变量的存储期限决定了变量占用的内存空间什么时候会被释放,具有动态存储期限的变量会在所属的程序块被执行时获得内存空间,在结束时释放内存空间。具有静态存储期限的变量在程序运行的整个期间都会占用内存空间。
2、作用域:变量有块作用域也有文件作用域,结合序章第一张图可以明白块作用域是在某些程序块内起作用,文件作用域是在整个c文件之内起作用。
3、链接:链接是各个文件之间的关系,具有内部链接的变量只在本文件内起作用,具有外部链接的变量可以在不同文件内起作用。具有无链接的变量只在某一个文件的某个函数内起作用。

C变量作用域、链接和存储期的含义

错误的定义C语言变量的作用域、链接和存储区在某种程度上会直接影响程序的设计,我们通过这篇博文介绍如何正确合理的定义一个C变量。

从定义一个C变量开始成为一个C高手

一、基本术语

1.1 什么是对象(object)

C语言中的对象含义与C++的完全不同,C语言中的对象指的是存储数据的一块内存。对象可以存储一个或多个值,一个对象可能并未存储实际的值,但是在存储适当的值时一定具有相应的大小。对象可以存在于程序的执行期,也可以仅存在于它所在函数的执行期。

1.2 什么是标识符(identifier)

标识符就是一个名称,通过这个标识符可以修改对象的内容。标识符可以在源代码的多文件中共享、可用于特定文件的任意函数中、可仅限于特定函数中使用,甚至只在函数中的某部分使用。

举个例子,我们定义一个int型变量point:

int point;

对象与标识符的关系如下图

二、作用域、链接和存储期三者关系

存储期用来描述对象,所谓的存储期就是指对象在内存中可以保留多长时间。

标识符用于访问对象,使用作用域(scope)和链接(linkage)描述标识符,标识符的作用域和链接表明了程序的哪些部分可以使用它。

三、作用域详解

作用域描述程序中可访问标识符的区域
一个C变量的作用域可以是块作用域、函数作用域、函数原型作用域和文件作用域,下面我们详细讲解每个作用域的含义。

3.1 块作用域

块是用一对花括号括起来的代码区域,定义在块中的变量具有块作用域块作用域变量的可见范围是从定义处到包含该定义的块的末尾

另外,虽然函数的形式参数声明在函数的左花括号之前,但它们也具有块作用域,属于函数体这个块。

int Star_CongShanRuoShui(int user_id)
{
	int res = 0;
	......
	return
}

👆上面code中“user_id”和“res”都具有块作用域

int Star_CongShanRuoShui(int user_id[] , int n)
{
	int res = 0;
	for(int i=0;i<n;++i)
	{
		......
		int q=0;   //q的作用域开始

		......
	}              //q的作用域结束
	......
	return
}

👆上面code中变量“q”的作用域仅限于for循环的循环体中

3.1.1 块概念的扩展

C99将块概念扩展到包括for循环、while循环、do while循环和if语句所控制的代码,即使这些代码没有使用花括号括起来,也算是块的一部分,下面的code👇

#include<stdio.h>

int main()
{
	int Star_CSRS = 8;
	printf("[main] value of Star_CSRS is %d | address of Star_CSRS:%p\n",
			Star_CSRS ,&Star_CSRS);

	for(int Star_CSRS=0;Star_CSRS<4;++Star_CSRS)
	{
		printf("[for index] value of Star_CSRS is %d | address of Star_CSRS:%p\n",
			Star_CSRS ,&Star_CSRS);

		int Star_CSRS = 6;
		printf("[for] value of Star_CSRS is %d | address of Star_CSRS:%p\n",
			Star_CSRS ,&Star_CSRS);

		++Star_CSRS;
	}

	printf("[main] value of Star_CSRS is %d | address of Star_CSRS:%p\n",
			Star_CSRS ,&Star_CSRS);

	return 0;
} 

输出如下:

  • 在main中定义了变量Star_CSRS,在for循环头中定义的变量Star_CSRS 隐藏了main中的Star_CSRS
  • for的循环体中的Star_CSRS又隐藏了for循环头中的Star_CSRS

3.2 函数作用域

函数作用域的概念仅限于goto语句的标签。这意味着即使一个标签首次出现在函数的内层块中,它的作用域也延伸至整个函数中(详细可以参考我的这篇博文)。

3.3 函数原型作用域

用于函数原型的形参变量名,如下所示:

int Star_CongShanRuoShui(int user_id);

函数原型作用域的范围是从形参定义处到原型声明结束。对于函数原型中的形参编译器只关心形参类型,不关系具体的形参名即使有形参名也不必与函数定义中的形参名相匹配

只有在变长数组中,形参名才有意义,如下例(变长数组详细参考我的这篇博文):

int Star_CongShanRuoShui(int n ,int user_id[n]);

3.4 文件作用域

定义在所有函数外的变量具有文件作用域。具有文件作用域的变量,从它的定义处到该定义所在文件的末尾均可见(文件作用域变量也称为全局变量)。

翻译单元与文件

编译器将一个源代码文件和所有的头文件都看成一个包含信息的单独文件,这个文件被称为翻译单元一个翻译单元包括一个源代码文件和它所include的文件如果一个程序有多个源代码文件,那么这个程序也将有多个翻译单元一个文件作用域变量的可见范围其实是整个翻译单元(一个源代码文件+头文件)

四、链接详解

C变量有3中链接属性:无链接、内部链接、外部链接

4.1 无链接

具有块作用域、函数作用域和函数原型作用域的变量都是无链接变量。这意味着这些变量属于定义它们的块、函数或原型私有。

4.2 内部链接

内部链接变量只能在一个翻译单元中使用,该变量使用存储类别说明符static修饰,如下:

int Star_CSDN=1;                     //文件作用域,外部链接
static int Star_CongShanRuoShui=2;   //文件作用域,内部链接

int main()
{
	......
	return 0
}

4.3 外部链接

外部链接变量可以在多个文件中使用。外部链接变量的声明分为“定义性声明”和“引用性声明”。C编译器要求一个变量只能定义一次,重复定义编译器会报错。如果需要在其它文件中使用外部链接变量需要使用extern引用性声明这个变量,如下面cdoe:

//file a.c

//在文件a.c中定义一个外部链接变量Star_CongShanRuoShui
int Star_CongShanRuoShui = 2;

......
//file b.c

//文件b.c中使用extern引用性声明变量Star_CongShanRuoShui
extern int Star_CongShanRuoShui;

......

五、存储期详解

C对象有4种存储期:静态存储期、线程存储期、自动存储期、动态分配存储期。

5.1 静态存储期

具有静态存储期的对象,它在程序的执行期间一直存在。文件作用域变量具有静态存储期

注意,对于文件作用域变量,关键字static表明了其链接属性,而非存储期。以static声明的文件作用域变量具有内部链接属性,但是无论是内部链接还是外部链接,所有文件作用域变量都具有静态存储期。

5.2 线程存储期

线程存储期用于并发程序设计,程序执行可被分为多个线程。具有线程存储期的对象,从被声明时到线程结束一直存在。以关键字__Thread_local声明一个对象时,每个线程都获得这个变量的私有备份

5.3 自动存储期

块作用域的变量通常都具有自动存储期。当程序进入定义这些变量的块时,为这些变量分配内存;当退出这个块时,释放刚才为这些变量分配的内存。通过这种做法可以实现内存的重复利用。

变长数组稍有例外,它的存储期从声明处到块的末尾,而不是从块的开始处到块的末尾

我们上面说块作用域的变量通常都具有自动存储期,但是也能具有静态存储期。为了创建这样的变量,要把变量声明在块中,且在声明前加上关键字static,如下例:

int Star_CongShanRuoShui(int user_id)
{
	static int isStar = 0;
	......
	return
}

变量isStar存储在静态内存中,它从程序被载入到程序结束期间都存在。但是,它的作用域定义在Star_CongShanRuoShui()函数块中,只有在执行该函数时,程序才能使用isStar访问它所指定的对象(当然,也可以存储该变量的地址实现间接访问该对象)

5.4 动态分配存储期

程序运行时通过malloc()等内存分配函数分配的对象具有动态分配存储期,这样的对象需要使用free()函数进行销毁。

动态内存分配和变长数组在功能上有些重合,但是还是有所不同:

变长数组是自动存储类型用malloc函数创建的数组不必局限在一个函数中使用



以上就是深度解析C语言中的变量作用域、链接和存储期的含义的详细内容,更多关于C语言变量作用域、链接和存储期的资料请关注我们其它相关文章!

(0)

相关推荐

  • C语言基础知识变量的作用域和存储方式详细介绍

    变量的作用域和存储方式 1.简述变量按作用域的分类 变量按作用域分:分为全局变量和局部变量 全局变量:在所有函数外部定义的变量叫做全局变量 全局变量的使用范围:从定义位置开始到下面整个程序结束 局部变量:在一个函数内部定义的变量或者函数的形式参数统称为局部变量 局部变量的使用范围:在函数内部定义的变量只能在本函数内部进行使用 2.简述变量按存储方式的分类 静态变量 自动变量 寄存器变量[寄存器就是cpu内部可以存储数据的一些硬件东西] 3.简述全局变量和局部变量命名冲突的问题 1>在一个函数内部

  • 举例讲解C语言链接器的符号解析机制

    1. 符号分类 (1)全局符号:非静态全局变量,非静态函数 (2)外部符号:定义于其它模块,而被本模块引用的全局变量和函数 (3)本地符号:静态变量(包括全局和局部),静态函数 对于静态局部变量,编译器会为其生成唯一的名字.如x.fun1,x.fun2.本地符号对链接器来说是不可见的. 2. 符号决议 当编译器遇到一个不是本模块定义的符号时,会假设该函数由其它模块定义,并生成一个链接器符号表条目,交由链接器处理.如果链接器在它的任何输入模块都没有找到该符号,会给出一个类似undefined re

  • C语言中的链接编写教程

    链接   链接就是将不同部分的代码和数据收集和组合成为一个单一文件的过程,这个文件可被加载或拷贝到存储器执行.   链接可以执行与编译时(源代码被翻译成机器代码时),也可以执行与加载时(在程序被加载器加载到存储器并执行时),甚至执行与运行时,由应用程序来执行.在现代系统中,链接是由链接器自动执行的.   链接器分为:静态链接器和动态链接器两种. 静态链接器   静态链接器以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的可以加载和运行的可执行目标文件作为输出. 静态链接器主要完成两个

  • 使用Python向C语言的链接库传递数组、结构体、指针类型的数据

    使用python向C语言的链接库传递数组.结构体.指针类型的数据 由于最近的项目频繁使用python调用同事的C语言代码,在调用过程中踩了很多坑,一点一点写出来供大家参考,我们仍然是使用ctypes来调用C语言的代码库. 至于如何调用基础数据类型的数据,请大家参考我的另外一篇文章:Python使用ctypes调用C/C++的方法 1. 使用python给C语言函数传递数组类型的参数 想必很多时候,C语言会使用数组作为参数,在之前我们使用过ctypes的一些数据类型作为C语言参数类型,包括byte

  • 在C语言中调用C++做的动态链接库

    今天在做东西的时候遇到一个问题,就是如何在C语言中调用C++做的动态链接库so文件 如果你有一个c++做的动态链接库.so文件,而你只有一些相关类的声明, 那么你如何用c调用呢,别着急,本文通过一个小小的例子,让你能够很爽的搞定. 链接库头文件: head.h class A { public: A(); virtual ~A(); int gt(); int pt(); private: int s; }; firstso.cpp #include <iostream> #include &

  • 深度解析C语言中的变量作用域、链接和存储期的含义

    在c中变量有三种性质: 1.存储期限:变量的存储期限决定了变量占用的内存空间什么时候会被释放,具有动态存储期限的变量会在所属的程序块被执行时获得内存空间,在结束时释放内存空间.具有静态存储期限的变量在程序运行的整个期间都会占用内存空间. 2.作用域:变量有块作用域也有文件作用域,结合序章第一张图可以明白块作用域是在某些程序块内起作用,文件作用域是在整个c文件之内起作用. 3.链接:链接是各个文件之间的关系,具有内部链接的变量只在本文件内起作用,具有外部链接的变量可以在不同文件内起作用.具有无链接

  • 深度解析C语言中数据的存储

    目录 前言 数据类型介绍 类型的基本归类 整型家族 浮点数家族 构造类型 指针类型 空类型 前言 在VS编译器里有release和debug两种形式,debug包含调试信息,release不包含调试信息,并会对程序进行优化 int main() { int i = 0; int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; for (i = 0; i <= 12; i++) { arr[i] = 0; printf("hehe\n"); } return

  • 深度解密Go语言中字符串的使用

    目录 Go 字符串实现原理 字符串的截取 字符串和切片的转换 字符串和切片共享底层数组 什么是万能指针 字符串和其它数据结构的转化 整数和字符串相互转换 Parse 系列函数 Format 系列函数 小结 Go 字符串实现原理 Go 的字符串有个特性,不管长度是多少,大小都是固定的 16 字节. package main import (     "fmt"     "unsafe" ) func main() {     fmt.Println(         

  • Go语言中的变量声明和赋值

    1.变量声明和赋值语法 Go语言中的变量声明使用关键字var,例如 复制代码 代码如下: var name string //声明变量 name = "tom" //给变量赋值 这边var是定义变量的关键字,name是变量名称,string是变量类型,=是赋值符号,tom是值.上面的程序分两步,第一步声明变量,第二步给变量赋值.也可以将两步合到一起. 复制代码 代码如下: var name string = "tom" 如果在声明时同时赋值,可以省略变量类型,Go语

  • 深度解密 Go 语言中的 sync.map

    工作中,经常会碰到并发读写 map 而造成 panic 的情况,为什么在并发读写的时候,会 panic 呢?因为在并发读写的情况下,map 里的数据会被写乱,之后就是 Garbage in, garbage out,还不如直接 panic 了. 是什么 Go 语言原生 map 并不是线程安全的,对它进行并发读写操作的时候,需要加锁.而 sync.map 则是一种并发安全的 map,在 Go 1.9 引入. sync.map 是线程安全的,读取,插入,删除也都保持着常数级的时间复杂度. sync.

  • 深入解析JavaScript中的变量作用域

    在学习JavaScript的变量作用域之前,我们应当明确几点: •JavaScript的变量作用域是基于其特有的作用域链的. •JavaScript没有块级作用域. •函数中声明的变量在整个函数中都有定义. 1.JavaScript的作用域链首先看下下面这段代码: 复制代码 代码如下: <script type="text/javascript"> var rain = 1; function rainman(){ var man = 2; function inner()

  • 深度解密 Go 语言中的 sync.Pool

    最近在工作中碰到了 GC 的问题:项目中大量重复地创建许多对象,造成 GC 的工作量巨大,CPU 频繁掉底.准备使用 sync.Pool 来缓存对象,减轻 GC 的消耗.为了用起来更顺畅,我特地研究了一番,形成此文.本文从使用到源码解析,循序渐进,一一道来. 是什么 sync.Pool 是 sync 包下的一个组件,可以作为保存临时取还对象的一个"池子".个人觉得它的名字有一定的误导性,因为 Pool 里装的对象可以被无通知地被回收,可能 sync.Cache 是一个更合适的名字. 有

  • JavaScript中的变量作用域介绍

    对于变量的作用域(scope),C.Java等语言采取的是"block scope"的方式.与之不同,JavaScript所采取的是"function scope"的方式 - 变量的作用域仅由所处的function决定,与if.for等逻辑块无关.比如,以下这个例子展示了JavaScript中与C.Java等语言不一样的行为: 复制代码 代码如下: function(){   var s = 42;//s is visible throughout function

  • 深度理解C语言中的关键字static

    目录 一.函数和变量的多文件问题 1.1.为什么全局变量和函数需要跨文件访问 二.static修饰变量和函数 2.1.static修饰全局变量 2.2.static修饰局部变量 2.3.为什么局部变量具有临时性,全局变量具有全局性 总结 一.函数和变量的多文件问题 .h: 头文件,一般包含函数声明,变量声明,宏定义,头文件等内容(header) .c : 源文件,一般包含函数实现,变量定义等 (.c:c语言) 如果在一个源文件定义一个函数,然后再另一个源文件调用,这样的方式可行吗? 答案是可行的

  • 深入解析C语言中的内存分配相关问题

    C内存分配区域 程序代码区 存放函数体的二进制代码 全局数据区 全局变量和静态变量的存储是放在一起的.初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域.常量数据存放在另一个区域里.这些数据在程序结束后由系统释放.我们所说的BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域.BSS是英文Block Started by Symbol的简称 栈区 由编译器自动分配释放,存放函数的参数值,局部变量的值等.其操作方式类似

随机推荐