变量类型(接C变量作用域,生存期,链接特性)
自动变量
自动存储类型,特点:自动存储期,块作用域,无链接。默认情况下,在块级作用域中或函数头中的变量属于自动存储类型的变量。当然,也可以受用关键字"auto"特别声明,一般用处不大。
示例:
// hiding.c -- variables in blocks
#include<stdio.h>
int main()
{
int x = 30; // original x
printf("x in outer block: %d at %p\n", x, &x);
{
int x = 77; // new x, hides first x
printf("x in inner block: %d at %p\n", x, &x);
}
printf("x in outer block: %d at %p\n", x, &x);
while (x++ < 33) // original x
{
int x = 100; // new x, hides first x
x++;
printf("x in while loop: %d at %p\n", x, &x);
}
printf("x in outer block: %d at %p\n", x, &x);
return 0;
}
输出:
x in outer block: 30 at 0x7ffee9725ae8
x in inner block: 77 at 0x7ffee9725ae4
x in outer block: 30 at 0x7ffee9725ae8
x in while loop: 101 at 0x7ffee9725ae0
x in while loop: 101 at 0x7ffee9725ae0
x in while loop: 101 at 0x7ffee9725ae0
x in outer block: 34 at 0x7ffee9725ae8
没有{}的块级作用域
loop或if内的代码一般也会产生一个块,其中定义的变量的生存期只在loop内或条件判断内。
示例:
// forc99.c -- new C99 block rules
#include<stdio.h>
int main()
{
int n = 8;
printf(" Initially, n = %d at %p\n", n, &n);
for (int n = 1; n < 3; n++)
printf(" loop 1: n = %d at %p\n", n, &n);
printf("After loop 1, n = %d at %p\n", n, &n);
for (int n = 1; n < 3; n++)
{
printf(" loop 2 index n = %d at %p\n", n, &n);
int n = 6;
printf(" loop 2: n = %d at %p\n", n, &n);
n++;
}
printf("After loop 2, n = %d at %p\n", n, &n);
return 0;
}
输出:
Initially, n = 8 at 0x7ffeef08dae8
loop 1: n = 1 at 0x7ffeef08dae4
loop 1: n = 2 at 0x7ffeef08dae4
After loop 1, n = 8 at 0x7ffeef08dae8
loop 2 index n = 1 at 0x7ffeef08dae0
loop 2: n = 6 at 0x7ffeef08dadc
loop 2 index n = 2 at 0x7ffeef08dae0
loop 2: n = 6 at 0x7ffeef08dadc
After loop 2, n = 8 at 0x7ffeef08dae8
寄存器变量
在块级作用域内使用关键字"register",和自动变量的唯一区别是该变量保存在CPU寄存器中,而不是内存中,意味着不能进行取地址操作(指针等)。寄存器变量存储在CPU寄存器中,可以比常规变量更快地访问和操作。不过编译器会权衡可用寄存器的数量和你的显式寄存器变量声明,因此有可能会忽略该声明,从而该变量实际上会变为自动变量,但仍然不能使用地址操作符进行操作。
块级作用域中的静态变量
特点:作用域为块级,存储期为静态存储期,无链接。
示例:
// loc_stat.c using a local static variable
#include<stdio.h>
void trystat(void);
int main(void)
{
int count;
for (count = 1; count <= 3; count++)
{
printf("Here comes iteration %d:\n", count);
trystat();
}
return 0;
}
void trystat(void)
{
int fade = 1;
static int stay = 1;
printf("fade = %d and stay = %d\n", fade++, stay++);
}
输出:
Here comes iteration 1:
fade = 1 and stay = 1
Here comes iteration 2:
fade = 1 and stay = 2
Here comes iteration 3:
fade = 1 and stay = 3
说明:虽然静态变量stay在trystat函数中声明,但该变量只会声明一次,这是因为静态变量和外部变量在程序加载到内存前已经存在。将静态变量的声明放在函数内只是为了告诉编译器该变量仅在该函数内可见。
外部链接特性的静态变量
特点:文件作用域,外部链接性,静态存储期。一般定义在一个文件的任何函数外。若一个变量定义在源文件A,那么另一个源文件B定义声明同一个变量时,需要使用关键字extern。
使用外部变量,示例:
// global.c -- uses an external variable
#include<stdio.h>
int units = 0; /* an external variable */
void critic(void);
int main(int argc, char const *argv[])
{
extern int units; /* an optional redeclaration */
printf("How many pounds to a firkin of butter?\n");
scanf("%d", &units);
while (units != 56)
critic();
printf("You must have looked it up!\n");
return 0;
}
void critic(void)
{
/* optional redeclaration */
printf("No luck, my friend. Try again.\n");
scanf("%d", &units);
}
输出:
How many pounds to a firkin of butter?
14
No luck, my friend. Try again.
56
You must have looked it up!
可以看出,unit拥有文件作用域,在main和critic函数中均可见。
定义和声明(Definitions and Declarations)
int tern = 1; /* tern defined /
main()
{
external int tern; / use a tern defined elsewhere */
第一处为“定义声明”,第二处为“引用声明”。第二处仅仅是告诉编译器使用之前定义的变量。
若:
extern int tern;
int main(void)
{
则编译器将认为tern的实际定义位于程序的其他位置,一般是某个源文件定义tern,然后其他文件进行外部链接性声明使用。
外部变量只能够被初始化一次。
// file one.c
char permis = 'N';
...
// file two.c
extern char permis = 'Y'; /* error */
错误原因:permis已经在file one.c中定义声明。
多文件用例
// parta.c -- various storage classes
#include<stdio.h>
void report_count();
void accumulate(int k);
int count = 0; // file scope, external linkage
int main(void)
{
int value; // automatic variable
register int i; // register variable
printf("Enter a positive integer (0 to quit): ");
while (scanf("%d", &value) == 1 && value > 0)
{
++count; // use file scope variable
for (i = value; i >= 0; i--)
accumulate(i);
printf("Enter a positive integer (0 to quit): ");
}
report_count();
return 0;
}
void report_count()
{
printf("Loop executed %d times\n", count);
}
```C // partb.c -- rest of the program #include
extern int count; // reference declaration, external linkage
static int total = 0; // static definition, internal linkage
void accumulate(int k); // prototype
void accumulate(int k) // k has block scope, no linkage
{
static int subtotal = 0; // static, no linkage
if (k <= 0)
{
printf("loop cycle: %d\n", count);
printf("subtotal: %d; total: %d\n", subtotal, total);
subtotal = 0;
}
else
{
subtotal += k;
total += k;
}
}
以上两个文件需要同时编译,cc parta.c partb.c。
输出:
Enter a positive integer (0 to quit): 5
loop cycle: 1
subtotal: 15; total: 15
Enter a positive integer (0 to quit): 10
loop cycle: 2
subtotal: 55; total: 70
Enter a positive integer (0 to quit): 2
loop cycle: 3
subtotal: 3; total: 73
Enter a positive integer (0 to quit): 0
Loop executed 3 times
Note:函数一般为外部链接,可以被其他文件中的函数调用,若使用关键字static,则函数仅能被函数定义所在的文件使用。
double gamma(double); /* external by default */
static double beta(int, int);
extern double delta(double, int);
gamma和delta都能够被其他文件中的函数使用。而beta只能在定义所在文件中使用。
<br>
## 总结 ##
The following are C's five storage classes(excluding thread concepts):
* <strong>Automatic</strong>--A variable declared in a block (or as a parameter in a function header) with no storage class modifier, or with the auto storage class modifier, belongs to the automatic storage class. It has automatic storage duration, block scope, and no linkage. Its value, if uninitialized, is not undetermined.
* <strong>Register</strong>--A variable declared in a block (or as a parameter in a function header) with the register storage class modifier belongs to the register storage class. It has automatic storage duration, block scope, and no linkage, and its address cannot be taken. Declaring a variable as a register variable is a hint to the compiler to provide the fastest access possible. Its value, if uninitialized, is not undetermined.
* <strong>Static, no linkage</strong>--A variable declared in a block with the static storage class modifier belongs to the "static, no linkage" storage class. It has static storage duration, block scope, and no linkage. It is initialized just once, at compile time. If not initialized explicitly, its bytes are set to 0.
* <strong>Static, external linkage</strong>--A variable defined external to any function and without using the static storage class modifiers belongs to the "static, external linkage" storage class. It has static storage duration, file scope, and external linkage. It is initialized just once, at compile time. If not initialized explicitly, its bytes are set to 0.
* <strong>Static, internal linkage</strong>--A variable defined external to any function and using the static storage class modifier belongs to the "static, internal linkage" storage class. It has static storage duration, file scope, and internal linkage. It is initialized just once, at compile time. If not initialized explicitly, its bytes are set to 0.
Allowed memory is provided by using the malloc() (or related) function, which returns a pointer to a block of memory having the requested number of bytes. This memory can be made available for reuse by calling the free() function, using the address as the argument.<br>
The type qualifiers are const, volatile, and restrict. The const specifier qualifies data as being constant. When used with pointers, const can indicate that the pointer itself constant or that the data it points to is constant, depending on the placement of const in the declaration. The volatile specifier indicates that data may be altered by processes other than the program. Its purpose is to warn the compiler to avoid optimizations that assume otherwise. The restrict specifier is also provided for reasons of optimization. A pointer qualified with restrict is identified as providing the only access to a block of data.