C基础知识(存储类别)

简介: C基础知识(存储类别)

C语言在内存管理方面给我们提供了较为自由的操作,但我们在享有这些自由的同时,也要了解相关的规定,不然就会给程序带来很多漏洞,同时我们也要详细来了解存储类别,为以后的自由分配内存打好基础.(文章内容稍长,干货满满,可收藏后细细品看)

在了解存储类别之前,我先帮大家温习一遍相关的知识点


1.作用域

一个变量的作用域可以是块作用域,函数作用域,函数原型作用域及文件作用域

(其中函数作用域,函数原型作用域,不作为本文章重点,大家可以简单了解一下)

(1)块作用域:块就是用一对花括号括起来的区域,一般我们创建的变量都具有块作用域,具有块作用域的变量,有效区域是从定义处到包含该定义的块的末尾

看下面一段代码

int main()
{
int a=0;     //a的作用域开始
for(a=0;a<3;a++)
                     //注:for循环中的块,属于main函数块中的一个内层块
   {
     int b=0;       //b的作用域开始
   }       //b的作用域结束,即只有在for循环这个块中才能使用b
}          //a的作用域结束,可在整个main函数内使用a

需要注意的是在C99标准中,把块的概念扩展到循环和if语句所控制的代码中,即使没有被花括号括起来,它们仍属于这个块

看下面一段代码

int main()
{
  for(int a=0;a<3;a++)  //其中a属于for循环的一部分,a的作用域在for循环的块中
  {                      //离开for循环的块,a的作用域就会结束            
       int b=0; 
  }       
//到这里,a与b的作用域都结束了
}

同理,一个函数的形式参数,虽然定义在左花括号之前,但它仍属于这个函数,其作用域在这个函数块中

(2)函数作用域:仅用于goto语句的标签

这意味着即使一个标签首次出现在函数的内层块中,他的作用域可遍布至整个函数

参考下面一段代码

虽然标签flag定义在while循环块内,但它仍可作用于在它之前的if语句判断后的代码

(3)函数原型作用域:用于函数原型中的形参名(变量名)

函数原型作用域的范围从形参定义到函数原型声明结束(这里再帮大家温习一下函数的相关定义)

看如下代码

int add(int m, int n);   //函数原型,告诉编译器add函数的类型
int main()
{
  int a = 3, b = 4,c;
  c=add(a, b);        //函数调用,在此执行add函数
  printf("%d", c);
}
int add(int x,int y) //函数定义,指定了函数要做什么
{
  return x + y;
}

事实上,编译器在处理函数声明中的形参时,只关心其类型,形参名可有可无,即使有,其形参名也不必与函数定义中的形参名相同,但在变长数组中有个例外

void add( int x, int y ,int arr [x][y] );

//若前两个变量作为第三个数组的参数,其变量名是不能乱改和不写的,方括号内要写上x,y

(4)文件作用域:若一个变量具有文件作用域,则其作用域从定义处到所在文件末尾都有效

看如下代码

int a = 3;          //变量a具有文件作用域,该文件的函数都可以引用它
 void print(void)    //这样的变量也被称为全局变量
{
   printf("%d\n", a);
}
int main()
{
  printf("%d\n", a);
  print();
  return 0;
}

2.链接

介绍完作用域,我们来了解一下链接(往后涉及到文件的概念,忘记的同学可以复习一下再看)

先介绍一下翻译单元的概念,编译器会将一个源代码文件以及该源代码文件所包含的各种头文件看成一个单独文件,这个单独的文件被称为翻译单元,即一个翻译单元对应一个源代码文件及其所包含的各种头文件

如下图, 实现一个简易信息库程序,创建了一个test.c的源文件,两个头文件contact.c , contact.h以及我们包含的各种stdio.h等头文件,在程序运行时,编译器会将其看成一个翻译单元。

C语言有3种链接属性:外部链接,内部链接,无链接

无链接:具有块作用域,函数作用域或函数原型作用域的变量都是无链接的变量。

内部链接和外部链接是相对于文件作用域而言的,内部链接只能作用于定义它的那个翻译单元,而外部链接可以作用于所有的翻译单元(较复杂的程序,会有多个源文件,即多个翻译单元)


举个例子,如下图

图中全局变量a就具有外部链接属性,源文件a组成的翻译单元与源文件b组成的翻译单元都可以使用变量a

但是变量b被static修饰,具有内部链接属性,只有源文件a这个翻译单元可以使用

如果源文件b想要引用变量a,一般要在引用时声明 extern int a; 表明这个变量来自其他文件,需要注意一点就是变量a的赋值只能是常量表达式,如果不给a赋初始值,a会被自动赋值为0,并且在b文件声明引用a时,不能给a赋值,例如extern int a=10;这是错误的。

注:具有文件作用域的变量都存储在内存的静态区中,直到程序结束才会将空间返还给内存,这与具有块作用域的变量不同,具有块作用域的变量在声明处被创建,出了包含它的块后,会自动将空间返还给内存。因此文件作用域的变量具有静态存储期,块作用域的变量具有自动存储期

(存储期:可简单理解为通过标识符访问的对象的生存期,静态存储期在程序执行期会一直存在,自动存储期会根据其作用域来判断返还超出作用域的变量的空间)

简单来说:作用域和链接是描述相应标识符的可用范围,而存储期是描述相应标识符的生存期


寄存器变量:

寄存器是在CPU上,不属于内存,因此,不可以取声明在寄存器上的变量的地址,在寄存器上声明变量需要用关键字register,并且不一定能够申请到,其作用域属于块作用域。

综上,我们可以得到以下一些存储类类别

(默认情况下,声明在块或函数头中的任何变量都属于自动存储类别)

存储类别 存储期 作用域 链接 声明方式
自动 自动 块内
寄存器 自动 块内,用关键字register
静态外部链接 静态 文件 外部 所有函数外
静态内部链接 静态 文件 内部 所有函数外,用关键字static
静态无链接 静态 块内,用关键字static


目录
相关文章
|
15天前
|
机器学习/深度学习 传感器 运维
使用机器学习技术进行时间序列缺失数据填充:基础方法与入门案例
本文探讨了时间序列分析中数据缺失的问题,并通过实际案例展示了如何利用机器学习技术进行缺失值补充。文章构建了一个模拟的能源生产数据集,采用线性回归和决策树回归两种方法进行缺失值补充,并从统计特征、自相关性、趋势和季节性等多个维度进行了详细评估。结果显示,决策树方法在处理复杂非线性模式和保持数据局部特征方面表现更佳,而线性回归方法则适用于简单的线性趋势数据。文章最后总结了两种方法的优劣,并给出了实际应用建议。
50 7
使用机器学习技术进行时间序列缺失数据填充:基础方法与入门案例
|
3月前
|
存储 固态存储 Linux
存储学习
存储学习
|
6月前
|
存储 C++
C primer plus 学习笔记 第12章 存储类别、链接和内存管理
C primer plus 学习笔记 第12章 存储类别、链接和内存管理
|
7月前
|
机器学习/深度学习
R语言使用 LOWESS技术图分析逻辑回归中的函数形式
R语言使用 LOWESS技术图分析逻辑回归中的函数形式
|
7月前
|
存储 Shell 索引
零基础学会Python编程——数据也分类:常见数据类型
零基础学会Python编程——数据也分类:常见数据类型
90 0
|
7月前
|
机器学习/深度学习 数据挖掘 BI
【数据挖掘】回归分析定义、概念、分类、过程讲解(图文解释 超详细)
【数据挖掘】回归分析定义、概念、分类、过程讲解(图文解释 超详细)
345 0
|
机器学习/深度学习 数据采集 搜索推荐
特征构造:从原始数据中创造出高效信息
特征构造:从原始数据中创造出高效信息
166 0
|
数据挖掘 Linux 数据处理
R语言笔记丨数据的创建和转换
R语言笔记丨数据的创建和转换
R语言笔记丨数据的创建和转换
|
存储 文件存储 对象存储
|
数据采集 机器学习/深度学习 自然语言处理
实现文本数据数值化、方便后续进行回归分析等目的,需要对文本数据进行多标签分类和关系抽取
实现文本数据数值化、方便后续进行回归分析等目的,需要对文本数据进行多标签分类和关系抽取
214 0