const,volatile,static,typdef,几个关键字辨析和理解
1、const类型修饰符
const它限定一个变量初始化后就不允许被改变的修饰符。使用const在一定程度上可以提高程序的安全性和可靠性。它即有预编译命令的优点也有预编译没有的优点。const修饰的变量被编译器处理只读变量(不是常量,常量是放到内存的只读区域的)放在内存中,由编译器限定不允许改变。
例如:const int Max=100; Max++会产生错误;
(2)便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。
例如: void f(const int i) { .........} 编译器就会知道i是一个不允许修改的变量;
(3)可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。 同宏定义一样,可以做到不变则已,一变都变!
如(1)中,如果想修改Max的内容,只需要:const int Max=you want;即可!
例如: void f(const int i)
(6) 可以节省空间,避免不必要的内存分配。 例如:
#define PI 3.14159 //常量宏放入只读内存中 const double Pi=3.14159; //此时并未将Pi放入ROM中 ...... double i=Pi; //此时为Pi分配内存,以后不再分配! double I=PI; //编译期间进行宏替换,分配内存 double j=Pi; //没有内存分配 double J=PI; //再进行宏替换,又一次分配内存!
(6) 提高了效率。
编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。(参考百度百科)
2、volatile类型修饰符
用来修饰被不同线程访问和修改的变量(即随时会被意想不到的改变)。volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
int square(volatile int *ptr) { return ((*ptr) * (*ptr)); }
int square(volatile int* &ptr)//这里参数应该申明为引用,不然函数体里只会使用副本,外部没法更改 { int a,b; a = *ptr; b = *ptr; return a*b; }
long square(volatile int*ptr) { int a; a = *ptr; return a*a; }
3、static类型修饰符
当一个进程的全局变量被声明为static之后,它就是静态全局变量。静态全局变量和其他的全局变量的存储地点并没有区别,要说有区别就是static是在.data段因为编译器会在你未初始化时自动初始化为0,而普通变量已初始化的变量在data段或者未初始化在.bss段内;static变量它只在定义它的源文件内有效,其他源文件无法访问它。普通全局变量extern后,它就可以被其他源文件及其函数访问,而static变量是无法extern 因为编译器在其他源文件中看不到被static修饰的变量。
1.用在全局变量上时例如
1 //test.c 2 #include <stdio.h> 3 #include "test.h" 4 5 static char str[] = "read ok !"; 6 int test() 7 { 8 printf("static is %s\n",str); 9 return 0; 10 }
主函数:
1 //main.c 2 3 #include <stdio.h> 4 #include "test.h" 5 6 char str[]="is not ok!"; 7 8 int main() 9 { 10 test(); 11 printf("static is %s\n",str); 12 return 0; 13 }
上面程序输出的结果是明显的,但如果去掉test.c内的str的static修饰符后编译时链接会出错,因为是有两个地方存在相同的变量,导致编译器编译时无法识别应该使用哪一个。但当你用static 修饰test.c内的str变量后编译器就知道在那个源文件内该使用哪一个。
2,在局部变量上使用时
普通的局部变量在栈空间上分配,这个局部变量所在的函数被多次调用时,每次调用这个局部变量在栈上的位置都不一定相同。局部变量也可以在堆上动态分配,但是记得使用完这个堆空间后要释放。
static局部变量中文名叫静态局部变量。它与普通的局部变量比起来有如下几个区别:
1)位置:静态局部变量被编译器放在全局存储区.data(前面提到编译器会自动初始化),所以它虽然是局部的,但是在程序的整个生命周期中存在。
2)访问权限:静态局部变量只能被其作用域内的变量或函数访问。也就是说虽然它会在程序的整个生命周期中存在,由于它是static的,它不能被其他的函数或源文件访问。
3)值:静态局部变量如果没有被用户初始化,则会被编译器自动赋值为0,以后每次调用静态局部变量的时候都用上次调用后的值。这个比较好理解,每次函数调用静态局部变量的时候都修改它然后离开,下次读的时候从全局存储区读出的静态局部变量就是上次修改后的值。
例如:
1 #include "test.h" 2 #include <stdio.h> 3 int test() 4 { 5 int b = 0; 6 static int a; 7 printf("%d,%d\n",a++,b++); 8 return 0; 9 }
主函数:
1 #include <stdio.h> 2 #include "test.h" 3 4 int main() 5 { 6 test(); 7 test(); 8 test(); 9 test(); 10 test(); 11 return 0; 12 }
结果:
0,0 注意这里验证了static变量的自动初始化 1,0 2,0 3,0 4,0
3.static函数
既然static变量在其他源程序中不可以访问,那用在函数前是否也有相同的功能呢?验证一下:
//test.c #include "test.h" #include <stdio.h> static int test() { int b = 0; printf("%d\n",b); return 0; } int mytry() { test(); return 0; }
头文件:
//test.h #ifndef __TEST_H__ #define __TEST_H__ #include"test.h" static int test(); int mytry(); #endif
主函数:
//main.c #include <stdio.h> #include "test.h" int main() { //test();//加上这一句程序链接时会出错 mytry(); return 0; }
结果你猜对了吗?所以static函数可以很好地解决不同原文件中函数同名的问题,因为一个源文件对于其他源文件中的static函数是不可见的。参考博客
4、typdef 或 #define 类型修饰符
(网上好多地方都有关于这两个关键字如何使用的讨论,本文只是简单罗列各自的特性。)
1.#define是预处理指令,通常用来替代常量(大写)在编译预处理时进行简单的替换,不作正确性检查,不关含义是否正确照样带入。即使你在末尾习惯性的写上 ; define 还是会乖乖的替换,幽默点说如果你爱浪爱自由#define是不二之选(但是记住出来混是要还的)
知乎上看到这样的代码:
#include <iostream> #define start using namespace std; int main(int argc, char *argv[]) { #define end <<endl;} #define print cout<< start print"hello world!" end 作者:DreamPiggy 链接:https://www.zhihu.com/question/29798061/answer/78916243 来源:知乎。
另一个例子:
#define Pi 3.1415926; (Pi*R*R ) 就会成为 (3.1415926;*R*R)
2.typdef 是由编译器处理的,由名字也能感觉到功能的一点差别,他是用于长类型 有一个剪短的名字而常常被使用,其实typdef创造的是一个类型(官方的解释是任何声明变量的语句前面加上typedef之后,原来是变量的都变成一种类型。不管这个声明中的标识符号出现在中间还是最后.)
如:
define PINT int* PINT a,b 就是 int *a;int b; 而 typedef PINT int* PINT a,b 就是 int *a,int *b;
常见用法:
- 用在C代码中,帮助struct。声明struct新对象时,必须要带上struct,即形式为: struct 结构名对象名,如:
struct tagPOINT1 { int x; int y; };
struct tagPOINT1 p1;
而在C++中,则可以直接写:结构名对象名,即:tagPOINT1 p1;
typedef struct tagPOINT { int x; int y; }POINT; POINT p1; 简洁多了吧
- 用typedef来定义与平台无关的类型。
比如定义一个叫 FLOAT的浮点类型,在目标平台一上,让它表示最高精度的类型为:
typedef long double FLOAT;
在不支持 long double 的平台二上,改为:
typedef double FLOAT;
在连 double 都不支持的平台三上,改为:
typedef float FLOAT;
也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
标准库就广泛使用了这个技巧,比如size_t。另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健。
- 还有就是类似#deine的作用
3.区别
- typedef是有作用域的,而#define不管怎么写都是全局的
- 细节上的差别
typedef int * pint ;
#define PINT int *
那么:
const pint p ;//p不可更改,但p指向的内容可更改
const PINT p ;//p可更改,但是p指向的内容不可更改。
两者的区别还很多。。。。。。在同样的功能下看个人喜好。
5、extern
- 默认情况下所有文件的变量都可以访问,只需要在定义变量时添加一个extern(extern int age)(而且没有分配内存)引用一下就行,这个不管int age是定义在哪个文件中都可以得到,而且此变量还可以被修改。
- extern引用的时候,优先找本文件夹,如果找不到再去其它文件夹。
2017.9.1
本博客起笔与于一个月前刚到实习地点,在实习最后一天再次继续完成。