第5课 - 变量属性
第5课 - 变量属性
1. C语言变量的属性
C语言中的变量可以拥有自己的属性。在定义变量时可以加上属性关键字,用来指明变量的特有意义。
语法:
property type var_name;
示例:
1 auto char i; 2 register int j; 3 static long k; 4 extern double m;
2. auto关键字
(1)auto属性关键字将被修饰的变量存储在栈上
(2)C编译器默认所有的局部变量都是auto属性的(auto是局部变量的默认属性),即局部变量存储在栈上
示例:
1 void f(){ 2 int i; // 局部变量默认属性为auto 3 auto int j; // 显式声明局部变量为auto属性 4 }
3. register关键字
(1)register关键字请求编译器将局部变量存储于寄存器中,而不是内存中,以加快其存取速度,多用于修饰需要频繁使用的局部变量
※※ register不能修饰全局变量
(2)不能使用 & 运算符获取register变量的地址,因为寄存器是没有地址的,只有内存才有地址
1 #include<stdio.h> 2 3 // register int g_v; // 全局变量的生命周期从程序运行到程序结束,那么在整个过程中都要占用寄存器,但CPU寄存器的数量是有限的,长时间占用会影响CPU工作 4 // 因此不允许register修饰全局变量,register修饰全局变量编译器会直接报错,error! 5 int main() 6 { 7 register char var; 8 // printf("0x%08X\n", &var); // 寄存器变量没有地址,编译报错,error! 9 10 return 0; 11 }
(3)register只是请求寄存器变量,但不一定请求成功
(4)由于register修饰的变量存储在寄存器中,因此该变量必须是CPU寄存器可以接受的值
下面是libevent中一个拷贝字符串的函数,使用了register关键字
1 size_t event_strlcpy_(char *dst, const char *src, size_t size) 2 { 3 register char *d = dst; 4 register const char *s = src; 5 register size_t n = size; 6 7 /* Copy as many bytes as will fit */ 8 if (n != 0 && --n != 0) { 9 do { 10 if ((*d++ = *s++) == 0) 11 break; 12 } while (--n != 0); 13 } 14 15 /* Not enough room in dst, add NUL and traverse rest of src */ 16 if (n == 0) { 17 if (size != 0) 18 *d = '\0'; /* NUL-terminate dst */ 19 while (*s++) 20 ; 21 } 22 23 return (s - src - 1); /* count does not include NUL */ 24 }
前面说了register可以加快局部变量的存取速度,可能不太直观,我们通过下面的例子直观的感受一下! 0.68s VS 0.195s
4. static关键字
(1)static 关键字指明变量的"静态"属性,局部变量存储在程序静态区(普通的局部变量存储在栈上)
(2)sttaic关键字同时具有"作用域限定符"
- static修饰的全局变量,作用域是声明该变量的文件中,其它文件不能使用
- static修饰的函数,作用域是声明该函数的文件中,其它文件不能使用
1 #include <stdio.h> 2 3 int g_v; // 全局变量,程序的任意地方均能访问 4 static int g_vs; // 静态全局变量,只有当前文件中可访问 5 6 int main() 7 { 8 int var; // 局部变量,在栈上分配空间 9 static int svar; // 静态局部变量,在静态数据区分配空间 10 11 return 0; 12 }
【auto、register、static对比分析】
1 #include<stdio.h> 2 3 int f1() 4 { 5 int r = 0; 6 r++; 7 8 return r; 9 } 10 11 int f2() 12 { 13 static int r = 0; // 静态局部变量,只初始化一次 14 r++; 15 16 return r; 17 } 18 19 int main() 20 { 21 auto int i = 0; // 显式声明auto属性,i为栈变量 22 static int k = 0; // 局部变量k的存储区位于静态区,作用域位于main中 23 register int j = 0; // 向编译器申请将j存储于寄存器中 24 25 // 两个变量在前面是相邻定义的,地址差别却非常之大,就是前者存储在栈上,后者存储在静态区 26 printf("%p\n", &i); // 0x7ffd7784424c 27 printf("%p\n", &k); // 0x601048 28 // printf("%p\n", &j); // compile error,寄存器变量不能取地址 29 30 31 for (i = 0; i < 5; i++) 32 { 33 printf("%d\n", f1()); // 1 1 1 1 1 34 } 35 36 for (i = 0; i < 5; i++) 37 { 38 printf("%d\n", f2()); // 1 2 3 4 5 39 } 40 41 return 0; 42 }
5. extern关键字
(1)extern 用于声明"外部"定义的变量和函数
- extern 变量在其它地方分配空间
- extern 函数在文其它地方定义
1 #include<stdio.h> 2 3 extern int g_i; // 告诉编译器g_i在其它的地方定义 4 5 int main() 6 { 7 printf("%d\n", g_i); // 先使用,后面链接时在其它地方再寻找 8 9 return 0; 10 } 11 12 int g_i = 0;
(2)extern用于告诉编译器用C方式编译代码
【static和extern的使用】
// g.c
1 static int g_i; // g_i只能在本文件中使用 2 3 int getI() 4 { 5 return g_i; 6 }
// main.c
1 #include<stdio.h> 2 3 extern int getI(); // extern声明getI()是在其它地方定义的 4 5 int main() 6 { 7 printf("%d\n", getI()); // 0 8 9 return 0; 10 }