这篇文章主要介绍“C语言自动变量的类型有哪些及怎么用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C语言自动变量的类型有哪些及怎么用”文章能帮助大家解决问题。
关键字auto
为了更清楚地表达你的意图(例如,为了表明有意覆盖一个外部变量定义,或者强调不要把该变量改为其他存储类别),可以显式使用关键字auto
,如下所示:
int main(void){ auto int plox;
关键字auto
是存储类别说明符(storage-class specifier)。auto
关键字在C++
中的用法完全不同,如果编写C/C++
兼容的程序,最好不要使用auto
作为存储类别说明符。
块作用域
和无链接
意味着只有在变量定义所在的块中才能通过变量名访问该变量(当然,参数用于传递变量的值和地址给另一个函数,但是这是间接的方法)。另一个函数可以使用同名变量,但是该变量是存储在不同内存位置上的另一个变量。
变量具有自动存储期意味着,程序在进入该变量声明所在的块时变量存在,程序在退出该块时变量消失。原来该变量占用的内存位置现在可做他用。
嵌套快的情况
接下来分析一下嵌套块的情况。块中声明的变量仅限于该块及其包含的块使用。
int loop(int n){ int m; // m in scope scanf("%d", &m); { int i; // both m and i in scope for (i = m; i < n; i++) puts("i is local to a sub-blockn"); } return m; // m in scope, i gone}
在上面的代码中,i
仅在内层块中可见。如果在内层块的前面或后面使用i
,编译器会报错。通常,在设计程序时用不到这个特性。然而,如果这个变量仅供该块使用,那么在块中就近定义该变量也很方便。这样,可以在靠近使用变量的地方记录其含义。另外,这样的变量只有在使用时才占用内存。变量n
和m
分别定义在函数头和外层块中,它们的作用域是整个函数,而且在调用函数到函数结束期间都一直存在。
如果内层块中声明的变量与外层块中的变量同名会怎样?内层块会隐藏外层块的定义。但是离开内层块后,外层块变量的作用域又回到了原来的作用域。程序hiding.c
演示了这一过程。
// hiding.c -- variables in blocks#include <stdio.h>int main(){ int x = 30; // original x printf("x in outer block: %d at %pn", x, &x); { int x = 77; // new x, hides first x printf("x in inner block: %d at %pn", x, &x); } printf("x in outer block: %d at %pn", x, &x); while (x++ < 33) // original x { int x = 100; // new x, hides first x x++; printf("x in while loop: %d at %pn", x, &x); } printf("x in outer block: %d at %pn", x, &x); return 0;}
下面是该程序的输出:
x in outer block: 30 at 0x7fff5fbff8c8x in inner block: 77 at 0x7fff5fbff8c4x in outer block: 30 at 0x7fff5fbff8c8x in while loop: 101 at 0x7fff5fbff8c0x in while loop: 101 at 0x7fff5fbff8c0x in while loop: 101 at 0x7fff5fbff8c0x in outer block: 34 at 0x7fff5fbff8c8
首先,程序创建了变量x
并初始化为30,如第1条printf()
语句所示。然后,定义了一个新的变量x
,并设置为77,如第2条printf()
语句所示。根据显示的地址可知,新变量隐藏了原始的x
。第3条printf()
语句位于第1个内层块后面,显示的是原始的x
的值,这说明原始的x
既没有消失也不曾改变。也许该程序最难懂的是while循环
。
while循环
的测试条件中使用的是原始的x
:
while(x++ < 33)
在该循环中,程序创建了第3个x
变量,该变量只定义在while
循环中。所以,当执行到循环体中的x++
时,递增为101的是新的x
,然后printf()
语句显示了该值。每轮迭代结束,新的x
变量就消失。然后循环的测试条件使用并递增原始的x
,再次进入循环体,再次创建新的x
。在该例中,这个x
被创建和销毁了3次。注意,该循环必须在测试条件中递增x
,因为如果在循环体中递增x
,那么递增的是循环体中创建的x
,而非测试条件中使用的原始x
。
我们使用的编译器在创建while循环体
中的x
时,并未复用内层块中x
占用的内存,但是有些编译器会这样做。
该程序示例的用意不是鼓励读者要编写类似的代码(根据C的命名规则,要想出别的变量名并不难),而是为了解释在内层块中定义变量的具体情况。
没有花括号的块
前面提到一个C99
特性:作为循环或if
语句的一部分,即使不使用花括号({}
),也是一个块。更完整地说,整个循环是它所在块的子块(sub-block
),循环体是整个循环块的子块。与此类似,if
语句是一个块,与其相关联的子语句是if
语句的子块。这些规则会影响到声明的变量和这些变量的作用域。程序forc99.c
演示了for循环
中该特性的用法。
// forc99.c -- new C99 block rules#include <stdio.h>int main(){ int n = 8; printf(" Initially, n = %d at %pn", n, &n); for (int n = 1; n < 3; n++) printf(" loop 1: n = %d at %pn", n, &n); printf("After loop 1, n = %d at %pn", n, &n); for (int n = 1; n < 3; n++) { printf(" loop 2 index n = %d at %pn", n, &n); int n = 6; printf(" loop 2: n = %d at %pn", n, &n); n++; } printf("After loop 2, n = %d at %pn", n, &n); return 0;}
假设编译器支持C语言
的这个新特性,该程序的输出如下:
Initially, n = 8 at 0x7fff5fbff8c8loop 1: n = 1 at 0x7fff5fbff8c4loop 1: n = 2 at 0x7fff5fbff8c4After loop 1, n = 8 at 0x7fff5fbff8c8loop 2 index n = 1 at 0x7fff5fbff8c0loop 2: n = 6 at 0x7fff5fbff8bcloop 2 index n = 2 at 0x7fff5fbff8c0loop 2: n = 6 at 0x7fff5fbff8bcAfter loop 2, n = 8 at 0x7fff5fbff8c8
需要注意的一点:
支持C99
和C11
有些编译器并不支持C99/C11
的这些作用域规则(Microsoft Visual Studio 2012就是其中之一)。有些编译会提供激活这些规则的选项。例如,撰写本书时,gcc
默认支持了C99
的许多特性,但是要用–std=c99
选项激活程序清单12.2中使用的特性:
gcc --std=c99 forc99.c
与此类似,gcc
或clang
都要使用–std=c1x
或-std=c11
选项,才支持C11特性
。
程序forc99.c
第1个for
循环头中声明的n
,其作用域作用至循环末尾,而且隐藏了原始的n
。但是,离开循环后,原始的n
又起作用了。
第2个for
循环头中声明的n作为循环的索引,隐藏了原始的n
。然后,在循环体中又声明了一个n
,隐藏了索引n
。结束一轮迭代后,声明在循环体中的n
消失,循环头使用索引n
进行测试。当整个循环结束时,原始的n
又起作用了。再次提醒读者注意,没必要在程序中使用相同的变量名。如果用了,各变量的情况如上所述。
自动变量的初始化
自动变量不会初始化,除非显式初始化它。考虑下面的声明:
int main(void){ int repid; int tents = 5;
tents
变量被初始化为5,但是repid
变量的值是之前占用分配给repid
的空间中的任意值(如果有的话),别指望这个值是0。可以用非常量表达式(non-constantexpression)初始化自动变量,前提是所用的变量已在前面定义过:
int main(void){ int repid; int tents = 5;
关于“C语言自动变量的类型有哪些及怎么用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。