你能很随意地说出C语言中 auto,register,volatile,extern,static,const这几个关键字的含义和用法么?
auto
这个关键字用于声明变量的生存期为自动。
C程序是面向过程的,在C代码中会出现大量的函数模块,每个函数都有其生命周期(也称作用域),在函数生命周期中声明的变量通常叫做局部变量,也叫自动变量。
auto 变量是用堆栈(stack)方式占用储存器空间,因此,当执行此区段是,系统会立即为这个变量分配存储器空间,而程序执行完后,这个堆栈立即被系统收回.在大括号{}内声明.这个关键字不怎么多写,因为所有的变量默认就是auto的。
例子
#include <stdio.h> void sayHi(); int main() { printf("Hello!\n"); sayHi(); return 1; } void sayHi() { //int age = 20; auto age = 20; printf("I'm %d.\n",age); }
register
这个关键字命令编译器尽可能的将变量存在CPU内部寄存器中而不是通过内存寻址访问以提高效率.但也只是尽可能,因为cpu就那几个寄存器,不能全给你用了。
限制:必须是能被cpu寄存器所能接受的类型,必须为单个的值,并且长度应小于或者等于整型的长度,而且registor变量可能不存放在内存中,所以不能用取地址运算符来获取地址。
例子:(算Pi)
#include <stdio.h> #include "stdlib.h" #include <time.h> #include <sys/time.h> #define SCALE 10000 #define ARRINIT 2000 //int pow(int num, int n); int main() { struct timeval tpstart,tpend; int timeuse; gettimeofday(&tpstart,NULL); pi_digits(100000); gettimeofday(&tpend,NULL); timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+tpend.tv_usec-tpstart.tv_usec; printf("\nDone in %d ms.\n",timeuse); return 1; } void pi_digits(int digits) { int carry = 0; int arr[digits + 1]; int i,j; for (i = 0; i <= digits; ++i) arr[i] = ARRINIT; for (i = digits; i > 0; i-= 14) { //int sum = 0; register int sum = 0; for (j = i; j > 0; --j) { sum = sum * j + SCALE * arr[j]; arr[j] = sum % (j * 2 - 1); sum /= j * 2 - 1; } printf("%04d", carry + sum / SCALE); carry = sum % SCALE; } }
将pi_digits函数中的sum声明为register之后,运行结果:
运行时间有明显的减少。
volatile
在本次线程内, 当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中;以后,再取变量值时,就直接从寄存器中取值;当变量值在本线程里改变时,会同时把变量的新值copy到该寄存器中,以便保持一致。
当变量在因别的线程等而改变了值,该寄存器的值不会相应改变,从而造成应用程序读取的值和实际的变量值不一致。
当该寄存器在因别的线程等而改变了值,原变量的值不会改变,从而造成应用程序读取的值和实际的变量值不一致。
volatile应该解释为“直接存取原始内存地址”比较合适,“易变的”这种解释简直有点误导人。
这个在多线程编程中常常会用到。
深究的话可以扯到编译原理里面去了。
例子:
#include <stdio.h> int main() { //Has bug when not using volatile //int result = 0; volatile int result = 0; int a = result; printf("result = %d\n",a); int input = 1; __asm__ __volatile__ ("addl %2,%0":"=r"(result):"r"(result),"r"(input)); int c = result; printf("result = %d\n",c); return 1; }
使用汇编的目的是骗过编译器改变寄存器中变量的值。
在没有使用volitile的情况下:
第一个用的是-g参数,没有使用编译器的优化,所以结果是正确的。
第二个使用的是-O2参数,等于是编译release版本,gcc对代码进行了优化,比如常量传播,再次运行,结果并不正确。
接下来使用volitile关键字,结果正确。
尼玛,汇编是硬伤。
注意:频繁地使用volatile很可能会增加代码尺寸和降低性能,因此要合理的使用volatile。
extern
意为“外来的”···它的作用在于告诉编译器:有这个变量,它可能不存在当前的文件中,但它肯定要存在于工程中的某一个源文件中。
最常用的状态是多个文件的工程的时候,调用另外文件的共有变量和函数。
例子:
a.c
#include <stdio.h> int a = 20; int fun(int n) { int sum = 0; while(n>1) { sum += n--; } return sum; }
#include <stdio.h> extern int a; extern int fun(int n); int main() { printf("fun: %d\n",fun(a)); return 1; }
static
c语言中的static和extern是相对的。
static也可以用来休息变量和函数。
在修饰变量的时候,可以是:
全局变量,作用域仅限于变量被定义的文件中,其他文件即使使用extern声明也没办法使用他。
局部变量,只能在函数中使用,同一个文档中的其他函数也用不了。由于static修饰的变量存放在内存的静态区,所以即使这个函数运行结束,这个静态变量的值也不会销毁,下次仍然使用内存中的这个值。
在修饰函数的时候,
函数的作用域仅局限于本文件,也就是内部函数,好处:不同的人编写不同的函数时,不用担心自己定义的函数是否会与其他文件中的函数重名。
例子:函数使用计数器
#include <stdio.h> void count(); int main() { int i; for (i = 1; i <= 3; i++) count(); return 0; } void count() { static num = 0; num++; printf("I have been called %d times.\n",num); }
const
const称之为常量修饰符,即就是说其所修饰的对象为常量。当你代码中想要设法阻止一个变量被改变,那么这个时候可以选择使用const关键字。在你给一个变量加上const修饰符的同时,通常需要对它进行初始化,在之后的程序中就不能再去改变它.
const修饰符的几个典型作用:
1. const类型定义:指明变量或对象的值是不能被更新,引入目的是为了取代预编译指令
2. 可以保护被修饰的东西,防止意外的修改,增强程序的健壮性;
3. 编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
4. 可以节省空间,避免不必要的内存分配。
使用const的直接的作用就是让更多的逻辑错误在编译期被发现。所以我们要尽可能的多使用const。
当const 和 指针斯混在一起的时候,就会很蛋疼。
在此对于判断const的修饰对象给出一种常使用的方法,我们以*为界线,如果const位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。
比如:
const int *p;
int const *q;
int * const r= &n;
p和q都是一样,两个指针,指向的变量为常量,r本身是一个常量,不能再指向其他位置。
参考
语言中auto,register,static,const,volatile,extern的区别 - http://blog.csdn.net/sdwuyulunbi/article/details/8469058
C语言变量存储类型auto static extern static extern register - http://7008965.blog.51cto.com/6998965/1179780
C语言的那些小秘密 - http://blog.csdn.net/bigloomy/article/details/6595197
《C语言深度剖析》
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。