1.volatile
(1)volatile的作用
a)防止编译器(complier)优化
例子:要往某一地址送两指令:
int *ip =...; //设备地址
*ip = 1; //第一个指令
*ip = 2; //第二个指令
以上程序compiler可能做优化而成:
int *ip = ...;
*ip = 2;
结果第一个指令丢失。如果用volatile, compiler就不允许做优化,从而保证程序的原意:
volatile int *ip = ...;
*ip = 1;
*ip = 2;
b)取最原始的值(要从内存取值,不能重复使用放在cache或寄存器中的备份)
比如多线程共享的变量,必须要volatile修饰
(2)常用到volatile的例子?
...待补充..
2.sizeof ---- 实质是求占用内存空间的大小
(1)sizeof(X),X可以是变量也可以是类型,例如
int a = 0;
int size = 0;
size = sizeof(a);// or size = sizeof(int);
(2)sizeof是function还是keyword?验证你的结论
是keywork,gcc -S 生成.s文件,打开看是bl sizeof还是call sizeof
(3)sizeof能否用于计算字符串及函数参数中的数组长度?
sizeof不能用于计算字符串的长度,如int *p = "ab"; sizeof(p)后求得的大小为4,而字符串的大小为3.
应该使用strlen来求字符串的长度.
sizeof 也不能计算函数参数中的数组长度,因为数组做函数的参数时会被弱化为指针传给函数,
故而不能求得数组的实际长度。
(4)sizeof求指针的大小和数组的大小
例如:求sizeof(p)的大小
char *p = NULL; sizeof(p)= __4___; //sizeof(指针)= sizeof(long)
char *p[] = {"hello", "world"}; sizeof(p)= __2*4___; //sizeof(数组)= sizeof(数组元素类型)* 元素个数
char p[10] = "hello"; sizeof(p)= __10 * 1___;//sizeof(数组)=sizeof(数组元素类型) *元素个数!!!!!
char p[] = "hello"; sizeof(p)= __6 * 1___;//sizeof(数组)= sizeof(数组元素类型)* 元素个数,字符串有'\0'结尾
//strlen(p) = 5 * 1;因为strlen求字符串的长度是不包括'\0'的
3.static
修饰函数和全局变量,只能在本文件使用;
修饰局部变量,只初始化一次,放在Data段(在编译的时候已经确定,给其初始化的右值必须为常量)
4.const
(1)const修饰的全局变量放在rodata段,
const修饰的局部变量不能直接改变(可以用过指针间接改变)
(2)const修饰指针变量:const int *p; int const *p; int * const p; int const *p const;
(3)const定义的常量不能作为数组的下标,因为它的实质还是不能被改变的变量(而C++可以)
(4)const和#define的区别
const定义的常量要声明类型,还要为其分配空间,后面加分号;
用#define定义的常量不需要声明类型,不需要分配空间,在预编译的时候进行了简单替换
5.register
(1)register修饰符暗示编译程序相应的变量将被频繁地使用,如果可能的话,应将其保存在CPU的寄存器中,以加快其存储速度。例如下面的内存块拷贝代码,
/* Procedure for the assignment of structures, */
/* if the C compiler doesn't support this feature */
#ifdef NOSTRUCTASSIGN
memcpy (d, s, l)
{
register char *d;
register char *s;
register int i;
while (i--)
*d++ = *s++;
}
#endif
(2)但是使用register修饰符有几点限制
1)register变量必须是能被CPU所接受的类型。
这通常意味着register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。不过,有些机器的寄存器也能存放浮点数。
2)因为register变量可能不存放在内存中,所以不能用“&”来获取register变量的地址。
3)只有局部自动变量和形式参数可以作为寄存器变量,其它(如全局变量)不行。
在调用一个函数时占用一些寄存器以存放寄存器变量的值,函数调用结束后释放寄存器。此后,在调用另外一个函数时又可以利用这些寄存器来存放该函数的寄存器变量。
4)局部静态变量不能定义为寄存器变量。不能写成:register static int a, b, c;
5)由于寄存器的数量有限(不同的cpu寄存器数目不一),不能定义任意多个寄存器变量,而且某些寄存器只能接受特定类型的数据(如指针和浮点数),因此真正起作用的register修饰符的数目和类型都依赖于运行程序的机器,而任何多余的register修饰符都将被编译程序所忽略。
6.aoto
7.extern
声明一个外部变量/函数
8.typedef
(1)如何使用typdef
typedef char __u8;
(2)typdef 和 #define有何区别?
a)执行时间不同
#define在预编译的时候做简单的替换,typedef在编译的时候才回去执行
b)功能不同
typedef是给已有的类型起别名;
#define不只可以给已有的类型起别名,还可以定义常量,变量还有编译的开关.
c)对指针的操作能很明显看出他们的区别
typedef int * Type;
#define TYPE int *;
Type p1, p2;//相当于int *p1,*p2;定义了两个指向int型的指针变量
TYPE p3, p4;//相当于int *p3, p4;只定义了一个指针,另外一个只是int型的变量
const Type p;//相当于int * const p; p本身不能改变,P指向的内容能改变.
const TYPE q;//相当于const int * q; q指向的内容不能被改变,q能改变
例如,思考一下下面的例子:
#define dPS struct s *
typedef struct s * tPS;
以上两种情况的意图都是要定义dPS 和 tPS作为一个指向结构s指针。哪种方法更好?(如果有)为什么?
(3)如何 typedef定义函数指针类型、数组类型、数组指针类型、结构体类型?
typedef int (*pfunc)(int i; char *p);//定义了一个函数指针类型,类型名为pfunc
typedef int arr[10];//定义了一个数组类型,类型名为arr
typedef int (*parr)[10];//定义了一个数组指针类型,类型名为parr
typedef struct Test {
int a;
char b;
} Type1; //定义一个结构体类型,类型名为Type1
typedef struct Test{
int i;
char j;
}Type1, *pT;//?????????????????????????????????????????
9.#warning 和 #error
#warning 编译时会报warning,但会继续编译
#error编译到#error时报error,退出编译
10.inline
(1)inline函数的实质
编译器会在调用该函数的地方把函数的实现直接插入,就像宏一样直接替换,
并不是一个真正的函数调用,没有出栈和入栈的过程
(2)什么情况需要inline来修饰函数?
在头文件中实现的函数,函数实现较简单(递归就不能使用)
必须要加上statuic修饰,看以下例子就知道为什么必须要static修饰:
如果inline函数没有使用static关键字,则函数可以通过extern而不用包含声明inline函数的头文件就可以实现对inline函数的调用,这有时候可能是错误的,但链接器却不会给出错误哪怕是警告;
(3)以max(x, y)为例,说明#define与inline有何区别?
#define max(x,y) ((x) > (y) ? (x) :(y))
static inline int max(int x, int y)
{
return (x) > (y) ? x :y;
}
他们都是直接替换,但是inline函数有类型的检查而宏没有!