合肥网站建设制作,东莞图文短视频推广,wordpress 豆瓣插件,内部网站建设方案5. C语言变量的存储类别和生存期 我们知道#xff0c;变量是有数据类型的#xff0c;用以说明它占用多大的内存空间#xff0c;可以进行什么样的操作。除了数据类型#xff0c;变量还有一个属性#xff0c;称为“存储类别”。存储类别就是数据在内存中的存放区域。一个正在…5. C语言变量的存储类别和生存期 我们知道变量是有数据类型的用以说明它占用多大的内存空间可以进行什么样的操作。除了数据类型变量还有一个属性称为“存储类别”。存储类别就是数据在内存中的存放区域。一个正在运行的C程序的内存空间可以分为五个区域程序代码区、静态数据区、堆区、栈区和命令行参数区其中静态数据区和栈区可以用来存放变量的值。静态数据区的内存在程序启动时就已经由操作系统分配好占用的空间固定程序运行期间不再改变程序运行结束后才由操作系统释放它可以存放全局变量、静态变量、一般常量和字符串常量。栈区的内存在程序运行期间由操作系统根据需要来分配使用到变量才分配内存如果定义了变量但没有执行到该代码也不会分配内存占用的空间实时改变使用完毕后立即释放不必等到程序运行结束它可以存放局部变量、函数参数等。可以通过C语言中的关键字来控制变量的存放区域C语言共有 4 个关键字用来指明变量的存储类别auto自动的、static静态的、register寄存器的、extern外部的。知道了变量的存储类别就可以知道变量的生存期。通俗地讲生存期指的是在程序运行过程中变量从创建到销毁的一段时间生存期的长短取决于变量的存储类别也就是它所在的内存区域。 auto 变量 auto 是自动或默认的意思很少用到因为所有的变量默认就是 auto 的。也就是说定义变量时加不加 auto 都一样所以一般把它省略不必多此一举。例如 int n 10; 与 auto int n 10; 的效果完全一样。 6. C语言extern变量和函数 在所有的代码块函数、if 块、switch 块等之外定义的变量称为全局变量它的作用范围默认是整个程序也就是所有的源文件包括 .c 和 .h 文件。如果你一直在编写单个 .c 文件的程序那么请注意全局变量的作用范围不是从变量定义处到该文件结束在其他文件中也有效。虽然全局变量的作用范围是整个程序但是如果希望在 a.c 中使用 b.c 中的变量也必须先进行声明。声明使用 extern 关键字请看下面的代码。a.c 源码 1 #include stdio.h
2 #include stdlib.h
3 extern int num; // 必须对 num 进行声明
4 int main ()
5 {
6 printf(num %d, num);
7 system(pause);
8 return 0;
9 } b.c 源码 1 int num 100; // 对 num 进行定义 运行结果num 100我们在 b.c 中定义了一个全局变量 num在 a.c 中调用了它。extern int num; 的作用是告诉编译器 num 不在 a.c 中请到其他文件中查找。如果没有 extern编译器就会在当前文件中查找发现没有就会报错。提示编译是针对单个源文件的在编译 a.c 时编译器找不到 num链接时才会在 b.c 的目标代码.obj 文件中找到 num。与其他变量不同extern 变量有声明和定义之分。extern 变量的定义格式为 extern type name value; 不过 extern 可以省略我们通常就是这么做的全局变量默认就是 extern 的如 b.c 文件所示。声明格式为 extern type name; 注意 在定义 extern 变量时不能省略 value否则就变成了变量声明。声明 extern 变量时要指明数据类型必须和定义时的数据类型一致。声明可以有多次定义只能有一次。 在 a.c 中我们在所有代码块外部对 num 进行了声明这个时候 num 的作用范围是 a.c 整个文件确切的说是从声明开始处到文件结束。如果在代码块内部声明会怎样呢对 a.c 进行更改 1 #include stdio.h2 #include stdlib.h3 int main ()4 {5 {6 extern int num;7 printf(num %d, num);8 }9 printf(num %d, num);
10 system(pause);
11 return 0;
12 } 编译时报错第 9 行的 num 未声明。这说明 extern 变量的作用域跟它的声明位置有关在代码块内声明的 extern 变量在代码块外无效。 extern 函数 从本质上讲函数和变量是类似的它们都指向内存中的一块区域函数指向存放了函数体二进制代码的程序代码区变量指向静态数据区、栈区或堆区。extern 除了用于变量也可以用于函数请看下面的代码sum.c 源码 1 int sum(int n1, int n2)
2 {
3 return n1 n2;
4 } main.c 源码 1 #include stdio.h2 #include stdlib.h3 extern int sum(int, int);4 int main ()5 {6 int num1 20, num2 110;7 printf(%d %d %d, num1, num2, sum(num1, num2));8 system(pause);9 return 0;
10 } 运行结果20 110 130我们在 sum.c 中定义了一个函数 sum() 用来计算两个数的和在 main.c 中对函数进行了调用。extern 的作用是告诉编译器 sum() 函数不在 main.c 中请到其他文件中去查找。但是函数和变量的声明有所不同对于函数你可以省略 extern。例如将 main.c 中的 extern int sum(int, int); 改为 int sum(int, int); 仍然能够编译通过并正确运行。这是因为函数的定义和声明区别很明显有函数体就是定义没有函数体就是声明所以有没有 extern 都是函数声明。但是变量不一样没有 extern 就是变量定义重复定义是错误的。 7. C语言static变量和函数 上一节我们讲到全局变量和函数的作用范围默认是整个程序也就是所有的源文件。这给我们带来了很大的方便让我们能够在 A 文件中调用 B 文件中定义的变量和函数不必把所有的代码都集中到一个文件有利于模块化的程序设计。但是有时候这也会带来冲突例如在 a.c 中定义了一个全局变量 n在 b.c 中又定义了一次编译时就会发生重复定义的错误因为变量只能定义一次。如果两个文件都是我们自己编写的或者其中一个是遇到这样的情况还比较好处理改变变量的名字就可以但如果两个文件都是其他程序员编写的或者是第三方的库修改起来就颇费精力了。所以实际开发中我们一般将不需要被其他文件调用的全局变量或函数的作用范围限制在当前文件中。可以通过 static 关键字来限制请看下面的代码。a.c 源码 1 #include stdio.h
2 static int n 10;
3 void print_n_a()
4 {
5 printf(n(a.c) %d\n, n);
6 } b.c 源码 1 #include stdio.h
2 static int n 20;
3 void print_n_b()
4 {
5 printf(n(b.c) %d\n, n);
6 } main.c 源码 1 #include stdio.h2 #include stdlib.h3 int n 100;4 int main ()5 {6 print_n_a();7 print_n_b();8 printf(n(main.c) %d\n, n);9 system(pause);
10 return 0;
11 } 运行结果n(a.c) 10n(b.c) 20n(main.c) 100我们在 a.c、b.c 和 main.c 中都定义了变量 na.c 和 b.c 中的变量 n 都只在各自的文件内有效main.c 中的变量 n 在整个程序内有效。由此可见加了 static 的变量或函数的作用范围仅限于当前文件对其他源文件隐藏利用这一特性可以在不同的文件中定义同名的变量或函数而不必担心命名冲突。 static 局部变量 static 声明的变量称为静态变量不管是全局变量还是局部变量都存储在静态数据区全局变量本来就存储在静态数据区即使不加 static。静态数据区的数据在程序启动时就会初始化直到程序运行结束对于代码块中的静态局部变量即使代码块执行结束也不会销毁。注意静态数据区的变量只能初始化定义一次以后只能改变它的值不能再被初始化即使有这样的语句也无效。请看下面的代码 1 #include stdio.h2 #include stdlib.h3 int main ()4 {5 int result, i;6 for(i 1; i100; i)7 {8 result sum(i);9 }
10 printf(123...99100 %d\n, result);
11 system(pause);
12 return 0;
13 }
14 int sum(int n)
15 {
16 // 也可以不赋初值 0静态数据区的变量默认初始化为 0
17 static int result 0;
18 result n;
19 return result;
20 } 运行结果123...99100 5050我们在 sum() 中定义了一个静态局部变量 result它存储在静态数据区sum() 函数执行结束也不会销毁下次调用继续有效。静态数据区的变量只能初始化一次第一次调用 sum() 时已经对 result 进行了初始化所以再次调用时就不会初始化了也就是说 static int result 0; 语句无效。静态局部变量虽然存储在静态数据区但是它的作用域仅限于定义它的代码块sum() 中的 result 在函数外无效与 main() 中的 result 不冲突除了变量名一样没有任何关系。总结起来static 变量的主要作用有两个。 1) 隐藏 程序有多个源文件时将全局变量或函数的作用范围限制在当前文件对其他文件隐藏。 2) 保持变量内容的持久化 将局部变量存储到静态数据区。静态数据区的内存在程序启动时就已分配好内存中所有的字节默认值都是0x00直到程序运行结束。 8. C语言register变量 一般情况下变量的值是存储在内存中的CPU 每次使用数据都要从内存中读取。如果有一些变量使用非常频繁从内存中读取就会消耗很多时间例如 for 循环中的增量控制 1 int i;
2 for(i0; i1000; i)
3 {
4 // Some Code
5 } 执行这段代码CPU 为了获得 i会读取 1000 次内存。为了解决这个问题可以将使用频繁的变量放在CPU的通用寄存器中这样使用该变量时就不必访问内存直接从寄存器中读取大大提高程序的运行效率。 寄存器、缓存、内存 为了加深对 register 变量的理解这里有必要讲一下CPU寄存器。按照与CPU的远近来分离CPU最近的是寄存器然后是缓存最后是内存。寄存器是最贴近CPU的而且CPU只在寄存器中进行存取。寄存的意思是暂时存放数据不用每次都从内存中取它是一个临时的存放数据的空间。而寄存器的数据又来源于内存于是 CPU -- 寄存器 -- 内存这就是它们之间的信息交换。那么为什么还需要缓存呢因为如果频繁地操作内存中同一地址上的数据会影响速度于是就在寄存器和内存之间设置一个缓存把使用频繁的数据暂时保存到缓存如果寄存器需要读取内存中同一地址上的数据就不用大老远地再去访问内存直接从缓存中读取即可。缓存的速度远高于内存价格也是如此。注意缓存的容量是有限的寄存器只能从缓存中读取到部分数据对于使用不是很频繁的数据会绕过缓存直接到内存中读取。所以不是每次都能从缓存中得到数据这就是缓存的命中率能够从缓存中读取就命中否则就没命中。关于缓存的命中率又是一门学问哪些数据保留在缓存哪些数据不保留都有复杂的算法。 注意上面所说的CPU是指CPU核心从市场上购买的CPU已是封装好的套件附带了寄存器和缓存插到主板上就可以用。从经济和速度的综合考虑缓存又被分为一级缓存、二级缓存和三级缓存它们的存取速度和价格依次降低容量依次增加。购买到的CPU一般会标出三级缓存的容量。 register 变量 寄存器的数量是有限的通常是把使用最频繁的变量定义为 register 的。来看一个计算 π 的近似值的例子求解的一个近似公式如下为了提高精度循环的次数越多越好可以将循环的增量控制定义为寄存器变量如下所示 1 #include stdio.h2 #include conio.h3 int main()4 {5 register int i 0; // 寄存器变量6 double sign 1.0, res 0, ad 1.0;7 for(i1; i100000000; i)8 {9 res ad;
10 sign-sign;
11 adsign/(2*i1);
12 }
13 res * 4;
14 printf(pi is %f, res);
15 getch();
16 return 0;
17 } 运行结果pi is 3.141593关于寄存器变量有以下事项需要注意1) 为寄存器变量分配寄存器是动态完成的因此只有局部变量和形式参数才能定义为寄存器变量。2) 局部静态变量不能定义为寄存器变量因为一个变量只能声明为一种存储类别。3) 寄存器的长度一般和机器的字长一致所以只有较短的类型如int、char、short等才适合定义为寄存器变量诸如double等较大的类型不推荐将其定义为寄存器类型。4) CPU的寄存器数目有限因此即使定义了寄存器变量编译器可能并不真正为其分配寄存器而是将其当做普通的auto变量来对待为其分配栈内存。当然有些优秀的编译器能自动识别使用频繁的变量如循环控制变量等在有可用的寄存器时即使没有使用 register 关键字也自动为其分配寄存器无须由程序员来指定。 转载于:https://www.cnblogs.com/chunlanse2014/articles/4422153.html