C语言关键字:static,const,inline,volatile与assert()等

来源:互联网 发布:二建做题软件哪个好 编辑:程序博客网 时间:2024/05/17 06:33

1.static

(1)用于全局变量:外部静态变量,只能在本源文件中被引用,不能被其它源文件所引用。

(2)用于局部变量:局部静态变量,在函数返回后存储单元不释放;下一次调用该函数时,该变量为上次函数返回时的值。

(3)用于函数:内部函数,只能被本源文件中的函数所调用,不能被其它源文件调用。

 

Static变量与普通的变量有什么区别: 

1.static全局变量只初使化一次,防止在其他文件单元中被引用

2.static局部变量只被初始化一次,下一次依据上一次结果值; 

3.static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

 

2.const

const关键字在C语言中用于声明”只读变量”,其值不可修改,但具有确定的数据类型。C编译器总是为其分配相应的存储单元。

C++中,const关键字用于声明常量,C++编译器视具体情况决定是为其分配存储单元还是仅将其作为编译期间的常量。

C++中,还可以修饰类的成员函数,不改变类中的数据成员.

const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

char const p; //常量指针,p的值不可以修改 

char const p//指向常量的指针,指向的常量值不可以改 

const char *p //char const *p

const作用:修饰变量、修饰函数参数、修饰函数返回值三个作用。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

1) const变量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。

 2) 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试(不能用指针指向宏)

 

3.inline

因为函数的调用必须要将程序执行的顺序转移到函数所存放在内存中的某个地址,将函数的程序内容执行完后,再返回到调用函数的下一行代码继续执行。这种转移操作要求在转去执行前要保存现场并记忆执行的地址,转回后要恢复现场,并按原来保存地址继续执行。因此,函数调用要有一定的时间和空间方面的开销,于是将影响其效率。

而宏只是在预处理的地方把代码展开,不需要额外的空间和时间方面的开销,所以调用一个宏比调用一个函数更有效率。

但是宏也有很多的不尽人意的地方。

1)、宏不能访问对象的私有成员。

2)、宏的定义很容易产生二义性。

3)、宏定义的常量在代码区,很多调试器不能够对其调试

4)、宏会增大代码的尺寸

我们举个例子:

#define square(x) (x*x)

避免这些错误的方法是:给宏的参数都加上括号。

#define square(x) ((x)*(x))

 

内联函数和宏很类似,而本质区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈出栈,减少了调用的开销。你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。

 

4.volatile 

volatile字面意思是“易变的”。这个关键字来描述一个变量时,意味着给该变量赋值(写入)之后,马上再读取,写入的值与读取的值可能不一样,所以说它“易变的”。这是因为这个变量可能是一个寄存器,直接与外部设备相连,你写入之后,该寄存器也有可能被外部设备的写操作所改变或者,该变量被一个中断程序,或另一个进程改变了。volatile 不会被编译器优化影响。

简而言之,volatile关键字用于声明内存映射的易失型变量,这类变量的值随时可能由于某种编译器所不知道的原因(例如,外部设备对其写入)所改变,所以编译器在进行代码优化时不能对其做任何的假设和依赖

 

建议使用volatile变量的场所:
(1) 并行设备的硬件寄存器
(2) 一个中断服务子程序中会访问到的非自动变量(全局变量)
(3) 多线程应用中被几个任务共享的变量

 

5.assert()

#include <assert.h>
void assert( int expression );

 assert()测试一个条件并可能使程序终止的函数,原型如上,在程序运行时它计算括号内的表达式,如果表达式为false(0),那么它先向stderr打印一条出错信息,然后通过调用abort来终止程序运行,如果表达式不为0,则继续执行后面的语句。这个宏通常原来判断程序中是否出现了明显非法的数据,如果出现了就终止程序以免导致严重后果,同时也便于查找错误。例如,变量n在程序中不应该为0,如果为0可能导致错误,你可以这样写程序:

assert(n != 0);

********************************************************

零碎小知识点:

1.switch的表达式不能为实型。


2.对于 #include <filename.h> ,编译器从标准库路径开始搜索 filename.h

  对于 #include "filename.h" ,编译器从用户的当前工作路径开始搜索,如果没找到,会接着去标准库中查找 filename.h

 

3.stdoutstderr是不同设备描述符。stdout是块设备,stderr则不是。对于块设备,只有当下面几种情况下才会被输入,1)遇到回车,2)缓冲区满,3fflush被调用。而stderr则不会。

 

4. 结构和联合都是由多个不同或相同的数据类型成员组成但在任一时刻联合中只存放了一个被选中的成员(所有成员共用一块地址空间)而结构的所有成员都存在(不同成员的存放地址不同)对于联合的不同成员赋值将会对其它成员重写原来成员的值就不存在了而对于结构的不同成员赋值是互不影响的。


5.对于C++代码,对一个结构体而言,就算是空的,编译器仍然要为它分配空间,分配1个字节的空间;对于gccC代码,它是0;对于VCC代码,它是1. 所以因编译器不同而有所不同。

 

6.为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问。

0 0
原创粉丝点击