一,在软件开发过程中,经常有一些常用或者通用的功能或者代码段,这些功能既可以写成函数,也可以封装成为宏定义。那么究竟是用函数好,还是宏定义好?
1,函数调用会带来额外的开销,它需要开辟一片栈空间,记录返回地址,将形参压栈,从函数返回还要释放堆栈。
2,函数的参数必须被声明为一种特定的类型,所以它只能在类型合适的表达式上使用,我们如果要比较两个浮点型的大小,就不得不再写一个专门针对浮点型的比较函数。
3,和使用函数相比,使用宏的不利之处在于每次使用宏时,一份宏定义代码的拷贝都会插入到程序中。除非宏非常短,否则使用宏会大幅度增加程序的长度。
二,static关键字
1,在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变
2,在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问
3,在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用
三,const关键字
1,定义const常量,具有不可变性。要在定义的同时进行初始化
2,const修饰的变量具有只读属性,但是它与常量有着本质的区别,如:const int n = 5; int a[n];
由于在ANSI C规定数组定义的长度必须是‘常量’,‘只读变量’并非是常量。常量是被编译器放在内存中的只读区域,而‘只读变量’则是在内存中开辟一个地方来存放它的值
3,便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患,而宏定义是不进行类型检查的
4,节省空间,避免不必要的内存分配。const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干份拷贝
5,可以很方便地进行参数的调整和修改,如:const int Max = 需要的常量;
6,在函数调用中,可以保护被修饰的东西,防止意外的修改
例如:void fun(const int i) { i = 10;//错误}
7,编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率很高
8,修饰长指针遵循“左定值,右定向”
char *const cp = &x; //指针不可改变,指向的内容可以改变
char const *cp = &x; //指针可变,指向的内容不可变
const char *cp = &x; //指针可变,指向的内容不可变
注意:在初始化时候说明指针的指向,防止出现野指针
四,程序内存分配
1,静态存储区:存放全局变量和静态变量,程序结束由系统释放
2,文字常量区:存放常量字符串,程序结束由系统释放
3,栈区:按照先进后出的原则存储数据,在函数调用的时候储存断点,保护现场;所有的局部变量、形参都是从栈区分配空间,分配空间的大小在编译的时候就已经确定;只有栈的剩余空间大于申请空间,系统才会分配内存,否则将提示栈溢出
4,堆区:堆是在程序运行的时候请求操作系统分配给自己内存,效率比较低,优点在于编译器不必知道要从堆里分配多少空间,也不必知道存储的数据,有更大的灵活性。操作系统有一个记录空闲内存地址的链表,当有空间申请时,系统由低地址向高地址遍历该链表,寻找第一个空间大于申请空间的堆结点,将结点从链表中删除,并将空间分配给程序,找到的堆结点空间如果大于申请的空间,系统会自动将多余的部分放入空闲链表中
5,程序代码区:存放二进制代码
五,volatile关键字
一个定义为volatile 的变量,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
一般,volatile用在如下几个情况:
1,中断服务程序中修改的供其它程序检测的变量需要加volatile;
2,多任务环境下各任务间共享的标志应该加volatile;
3,存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义
六,struct与typedef关键字
struct关键字的一个重要作用是它可以实现对数据的封装,有一点点类似于C++的对象,可以将一些分散的特性对象化,这在编写某些复杂程序时提供很大的方便性。
例如:
typedef struct Student //可以省略Student
{
int a;
}Stu; //Stu是一个结构体类型
在声明变量的时候,就可以用Stu x;
struct Student
{
int a;
}Stu1; //Stu1是一个变量
由于Stu1是一个变量,所以可以直接访问Stu1.a;