static关键字的作用——限制作用域,可用于修饰局部变量、全局变量和函数
(1)当用static声明函数内的局部变量时,表示这个局部变量是在静态存储区分配的空间,在函数调用结束后其占用的存储单元并不释放,仍然保留原值。此外,静态局部变量是在编译时赋一次初值,程序运行时每次调用函数都不再重新赋初值,而是使用前次函数调用结束时的值。若程序不赋初值,则编译时自动赋值0(普通局部变量不赋初值的话,其值不确定)。
(2)当用static声明在函数体外的全局变量时,表示这个全局变量只能用于本文件,而不能被别的文件引用(即使使用了extern来扩展使用域),这样就能在不同文件中使用相同的全局变量名,提高了程序的通用性和可移植性。//ps:全局变量和静态全局变量都存放在静态存储区中,都是在编译时分配内存,只是作用域不同(全局变量可以在其他文件中用extern扩展使用域)。
(3)当用static声明函数时,表示此函数只能被本文件中的其他函数调用(称为内部函数),这样在不同文件中就可以使用相同函数名,只要用static声明即可。
在C语言中,关键字static有三个明显的作用:
-
第一、在修饰变量的时候,static修饰的静态局部变量只执行一次,而且延长了局部变量的生命周期,直到程序运行结束以后才释放。
-
第二、static修饰全局变量的时候,这个全局变量只能在本文件中访问,不能在其它文件中访问,即便是extern外部声明也不可以。
-
第三、static修饰一个函数,则这个函数的只能在本文件中调用,不能被其他文件调用。Static修饰的局部变量存放在全局数据区的静态变量区。初始化的时候自动初始化为0;
-
(1)不想被释放的时候,可以使用static修饰。比如修饰函数中存放在栈空间的数组。如果不想让这个数组在函数调用结束释放可以使用static修饰
-
(2)考虑到数据安全性(当程想要使用全局变量的时候应该先考虑使用static)
-
在C++中static关键字除了具有C中的作用还有在类中的使用在类中,static可以用来修饰静态数据成员和静态成员方法静态数据成员
-
(1)静态数据成员可以实现多个对象之间的数据共享,它是类的所有对象的共享成员,它在内存中只占一份空间,如果改变它的值,则各对象中这个数据成员的值都被改变。
-
(2)静态数据成员是在程序开始运行时被分配空间,到程序结束之后才释放,只要类中指定了静态数据成员,即使不定义对象,也会为静态数据成员分配空间。
-
(3)静态数据成员可以被初始化,但是只能在类体外进行初始化,若为对静态数据成员赋初值,则编译器会自动为其初始化为0
-
(4)静态数据成员既可以通过对象名引用,也可以通过类名引用。
静态成员函数
-
(1)静态成员函数和静态数据成员一样,他们都属于类的静态成员,而不是对象成员。
-
(2)非静态成员函数有this指针,而静态成员函数没有this指针。
-
(3)静态成员函数主要用来方位静态数据成员而不能访问非静态成员。
extern关键字的作用
(1)用extern声明全局变量。
a.在一个文件内声明全局变量,可以将全局变量的使用域扩展到从“声明”到文件结尾,而不是从“定义”到文件结尾。
b.在一个文件中用extern声明另一个文件定义的全局变量,可将此全局变量的作用域扩展到这个文件。
ps: 系统处理extern的过程:编译时遇到extern,先在本文件中找是否有此全局变量的定义(即a情况),若没有,则在连接时从其他文件中找此全局变量的定义(b情况)。如果找不到,则出错。
(2)当用extern在定义函数时,表示此函数可以被其他文件的函数调用。//此种情况extern可以省略
(3)当需要使用别的文件的函数时,用extern声明所用的函数是外部函数。
(4)在C++语言中调用C编译器编译的C语言函数,要用extern "C"声明,原因是:C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为:void foo(int x, int y); 该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。C++提供了C连接交换指定符号extern“C”来解决名字匹配问题
声明与定义:
1.从广义上说,声明包含定义。“int a;”--既是声明也是定义。“extern A;”只是声明不是定义。
2.从狭义上说,建立了存储空间的是定义,不需要建立存储空间的是声明。
关键字const的作用——只读不写,可用于定义const常量,修饰函数的输入参数、返回值及函数本身
笼统的说,const的意思是“只能读不能写”。
(1)给读你代码的人传达非常有用的信息。例如,在函数的形参前添加const关键字意味着这个参数在函数体内不会被修改,属于"输入参数"。
(2)合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改,这样可以减少bug的出现。
ps:在编译阶段需要的常数仍然只能以#define宏定义!在C语言中如下程序是非法的:
const int SIZE = 10;
char a[SIZE];//编译时SIZE仍是变量,变长数组是不允许的
几个const相关的定义实例:
(1)const int a; = int const a;
const int *p; = int const *p;
const与int前后顺序可以调换,前提是他们是紧挨在一起。
(2)const int * p与int * const p
a.const int * p----const修饰的是*p,即*p不能修改,但p可以修改。
b.int * const p----const修饰的是p,即p指针所指的地址不能改,而地址空间中的值可以修改
(3)int * p指针指向const int i常量的情况
const int i=5;
int *p;
p=&i; //这样是不行的,编译出错。const int 类型的i的地址是不能赋值给指向int类型地址的指针pi的。否则pi岂不是能修改i1的值了吗!
解决办法是只要将他们的类型统一就可以了,如:
const int *p;//将p定义为const int的指针
或者强制类型转换:pi=(int* ) &i;//至于是否可以通过*p来修改i的值,根据编译器不同而不同
(4)用const int * const p声明的指针
int i;
const int * const p=&i;//p的值不能修改,也不能通过*p修改i的值。但可直接修改i的值
注意: const修饰的变量,只能在定义的时候赋初值,不能在新行赋值。
Const修饰只读变量与#define的区别:
const 定义的只读变量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数。
所以,const 定义的只读变量在程序运行过程中只有一份拷贝(因为它是全局的只读变量,存放在静态区),而#define 定义的宏常量在内存中有若干个拷贝。
#define 宏是在预编译阶段进行替换,而const 修饰的只读变量是在编译的时候确定其值。
#define 宏没有类型,而const 修饰的只读变量具有特定的类型。
注意:const变量有全局的和局部的。C语言中全局的const变量默认为外连接,所以默认都是有内存地址的,c++中全局的const变量默认为内连接,它可以被编译器放到符号表中作为编译期常量,所以在C中,const int k = 2; int a[k]是非法的,但在C++中是合法的。而局部的const变量在C和C++中一视同仁,都是放在函数局部栈中的。所以局部的const变量只是用来吓唬人的,想改它的值照样改,修改的时候只要做个强制类型转化就好。至于全局的const变量,C中默认是外连接,它有地址,有地址当然也能够被改掉,所以,你可以像在C语言中改变局部const变量那样通过指针来修改全局const的值,编译时没有任何问题,连个warning都没有,只不过在运行时,你会得到一个segmentation fault而已。为什么呢?因为全局的const变量是存放在只读数据段里的,它比函数局部栈里的const变量高级,它受到只读数据段的权限保护。而你试图修改一个只读数据段中的内容,当然会得到一个运行时错误。
只要一个变量前用const来修饰,就意味着该变量里的数据只能被访问,而不能被修改,也就是意味着const“只读”(readonly)。
规则:const离谁近,谁就不能被修改;
const修饰一个变量时,一定要给这个变量初始化,若不初始化,在后面也不能初始化。
const作用:
-
1:可以用来定义常量,修饰函数参数,修饰函数返回值 ,且被const修饰的东西,都受到强制保护,可以预防其它代码无意识的进行修改,从而提高了程序的健壮性(是指系统对于规范要求以外的输入能够判断这个输入不符合规范要求,并能有合理的处理方式。ps:即所谓高手写的程序不容易死);
-
2:使编译器保护那些不希望被修改的参数,防止无意代码的修改,减少bug;
-
3:给读代码的人传递有用的信息,声明一个参数,是为了告诉用户这个参数的应用目的;
const优点:
-
1:编译器可以对const进行类型安全检查(所谓的类型安全检查,能将程序集间彼此隔离开来,这种隔离能确保程序集彼此间不会产生负面影响,提高程序的可读性);
-
2:有些集成化的调试工具可以对const常量进行调试,使编译器对处理内容有了更多的了解,消除了一些隐患。eg:void hanshu(const int i){.......} 编译器就会知道i是一个不允许被修改的常量
-
3:可以节省空间,避免不必要的内存分配,因为编译器通常不为const常量分配内存空间,而是将它保存在符号表中,这样就没有了存储于读内存的操作,使效率也得以提高;
-
4:可以很方便的进行参数的修改和调整,同时避免意义模糊的数字出现
关键字volatile的作用
一个定义为volatile的变量是说这个变量可能会被意想不到的改变,也就是说编译器不会去假设这个变量的值。当编译时用到这个变量的时候,优化器必须每次都重新读取这个变量,而不能使用寄存器里的备份。
volatile变量运用的几个例子:
(1)并行设备的硬件寄存器,如状态寄存器。每个设备在任何时刻都有可能会因为状态的变化而修改寄存器。
(2)一个中断服务子程序中访问到的非自动变量。
(3)多线程任务中被几个任务共享的变量。每个线程都有可能去修改这个变量。
关键字register的作用
用register定义的变量存在寄存器中,而不存在内存中。
(1)只有局部自动变量和形式参数可以作为寄存器变量。(不能让全局变量占着寄存器不放;register不和static连用)
(2)不能定义任意多个寄存器变量,处理器中寄存器变量是有限的。
关键字typedef的作用
声明新的类型来代替已有的类型。
使用方法:1、先按普通定义的方法写出定义体(如:int n[10];);
2、将变量名换成自己指定的类型名(int NUM[10]);
3、在前面加上typedef得到:typedef int NUM[10];
4、可以用心得类型名来定义变量了:NUM n = int n[10]
注意:typedef是在编译的时候处理的,而#define是在预编译的时候处理;
关键字小结
1.数据类型关键字
(1) char :声明字符型变量或函数
(2) double :声明双精度变量或函数
(3) enum :声明枚举类型
(4) float:声明浮点型变量或函数
(5) int: 声明整型变量或函数
(6) long :声明长整型变量或函数
(7) short :声明短整型变量或函数
(8) signed:声明有符号类型变量或函数
(9) struct:声明结构体变量或函数
(10) union:声明共用体(联合)数据类型
(11) unsigned:声明无符号类型变量或函数
(12) void :声明函数无返回值或无参数,声明无类型指针(基本上就这三个作用)
2.控制语句关键字
A循环语句
(1) for:一种循环语句(可意会不可言传)
(2) do :循环语句的循环体
(3) while :循环语句的循环条件
(4) break:跳出当前循环
(5) continue:结束当前循环,开始下一轮循环
B条件语句
(1)if: 条件语句
(2)else :条件语句否定分支(与 if 连用)
(3)goto:无条件跳转语句
C开关语句
(1)switch :用于开关语句
(2)case:开关语句分支
(3)default:开关语句中的“其他”分支
D返回语句
return :子程序返回语句(可以带参数,也看不带参数)
3.存储类型关键字
(1)auto :声明自动变量 一般不使用
(2)extern:声明变量是在其他文件正声明(也可以看做是引用变量)
(3)register:声明积存器变量
(4)static :声明静态变量
4.其它关键字
(1)const :声明只读变量
(2)sizeof:计算数据类型长度
(3)typedef:用以给数据类型取别名(当然还有其他作用
(4)volatile:说明变量在程序执行中可被隐含地改变