(2)宏定义求一年中有多少秒:注意类型的转换,因为C中默认时int类型,而秒数超出int范围
#define second (365*24*60*60UL) \\将60转为unsigned long类型 #define second (365*24*60*60)UL \\错误,编译器无法通过(3)谨记宏定义的含义就是完全替换
#define FUNC (((a) > (b)) ? (a) : (b)) int func(int a, int b) { return a > b? a: b; } int main(int args, char *argv[]) { int a = 1, b = 3 int c = FUNC(++a, b); printf("a = %d\nb = %d\nc = %d\n", a, b, c); //输出为3 3 3 func(++a, b); printf("a = %d\nb = %d\nc = %d\n", a, b, c); //输出为2 3 2 } 带参宏与带参函数 (1)带参宏预处理时会将宏原地展开,运行时直接在原地运行;而带参函数运行时会调用子函数,通过函数名(指针)跳转到子函数地址去执行,调用开销大,若子函数较小时,效率不如带参宏; (2)带参函数运行时,结果的返回值是在定义函数时就必须设定的,因此在返回结果时,编译器会自动检查返回值的类型是否正确; (3)带参宏计算的结果返回值不包含数据类型,预处理时仅进行字符替换,因此也不会报任何警告错误,但运行结果可能会因为数据的类型不匹配等导致结果错误; /**********带参函数************/ #define max(a, b) (((a) > (b))? (a) : (b)) int func(int a, int b) //由于a 和 b的类型与int 不匹配,因此编译器警告 { if(a > b) return a; else return b; } int main(void) { float a = 3.14; float b = 6.18; float c = func(a, b); //由于形参和实参的类型 不匹配,因此编译器警告 int d = max(a, b); //由于带参宏返回值不附带数据类型,因此编译器不报任何异常 printf("d = %f\n", d); //此处结果是错误的 } 宏定义来实现条件编译:debug宏 (#define #undef #ifdef) #define DEBUG //定义一个宏 #undef DEBUG //取消这个宏定义 #ifdef DEBUG //若定义了这个宏 #define DEBUG(format,...) printf("file:"__FILE__", line:%d, "format"\n", __LINE__, ##__VA_ARGS__) //用debug代替 print函数 #else //若没定义 #define DEBUG(format...) //debug就是空 #endif int main(void) { char str[] = "hello"; DEBUG("%s", str); } //输出结果:file:main.c, line:16, test, hello offsetof宏与container_of宏 (1)offsetof宏:用来计算结构体某成员指针与结构体指针的偏移量: #define offsetof(type, member) (size_t)&(((type*)0)->member) // ((type*)0):将0地址强制类型转换为指针类型,指向一个type类型的结构体,即0地址为结构体的首地址( int *0 <==> int *p = 0 //p中存放着某个int变量的地址,将2赋给p即将2赋给int变量的地址即2中存放着int变量); //1、(((type*)0)->member):得到通过结构体指针得到member成员的空间(参数名); //2、&(((type*)0)->member):得到member成员的地址; //3、(size_t)&(((type*)0)->member):将member成员的地址转为int类型即偏移量 //4、因为type结构体的首地址为0,因此member成员的地址就是member成员地址的偏移量(2)container_of宏:通过结构体某成员的指针计算结构体指针:
#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) //ptr:某成员的指针;type:结构体类型;member:某成员名 //1、( ((type *)0)->member ):得到某成员在内存的空间; //2、const typeof( ((type *)0)->member ):获取member的类型 //typeof是根据变量的值获取变量类型的类型 //3、const typeof( ((type *)0)->member ) *__mptr:定义一个名为__mptr,类型为指向member的类型的指针; //4、const typeof( ((type *)0)->member ) *__mptr = (ptr):将member的指针赋给之前定义的__mptr变量 //5、offsetof(type,member):获取当前成员的指针相对于结构体指针的偏移; //6、( (char *)__mptr - offsetof(type,member) ):用当前的指针地址-指针偏移 == 结构体的起始地址(此时为char *类型的地址) //7、(type *)( (char *)__mptr - offsetof(type,member) ):将当前的char*类型地址转化为结构体type类型。 内联函数和inline关键字 (1)内联函数就是在定义函数的前面加inline关键字,来实现该函数既可以原地展开,又可以通过编译器来检查参数和返回值的类型。 (2)一般内联函数都是用在代码比较少的小型函数上。 int func(int a, int b) //由于a 和 b的类型与int 不匹配,因此编译器警告 { if(a > b) return a; else return b; } int main(void) { float a = 3.14; float b = 6.18; float c = func(a, b); //此函数运行时会在原地展开运行而非跳转,同时编译器也会报警告 printf("c = %f\n",c); }