1、重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植,假如平台不一样,字节不一样,就仅仅改变typedef定义即可。
typedef unsigned char boolean; /* Boolean value type. */
typedef unsigned long int uint32; /* Unsigned 32 bit value */
typedef unsigned short uint16; /* Unsigned 16 bit value */
typedef unsigned char uint8; /* Unsigned 8 bit value */
typedef signed long int int32; /* Signed 32 bit value */
typedef signed short int16; /* Signed 16 bit value */
typedef signed char int8; /* Signed 8 bit value */
2、防止头文件被重复包含
#ifndef __AD2S1210_H__
#define __AD2S1210_H__
#endif
3、已知一个结构体成员的地址,求结构体首地址
#define offsetof(TYPE, MEMBER) ((unsigned int) &((TYPE *)0)->MEMBER)
//通过0地址得到结构体中成员地址的偏移量也就是相对于0这个地址的数值。
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
//1、offsetof是偏移的数值就是距离首地址有多少字节。
//指针类型加减数值相当于当前指针加减(指针所指类型字节*数值)。
//为了不影响ptr的类型,所以通过缓存__mptr来处理。
#define offsetof1(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof1(type,member) );})
void fun(int a , int b)
{
printf("test");
}
int main(void)
{
struct str{
int a;
char b;
int c;
int d;
void (*fun)(int ,int);
}str;
str.a = 12;
str.b = 24;
str.c = 24;
str.d = 24;
str.fun = fun;
printf("0x%x\n" , &str);
printf("0x%x\n" , container_of(&str.fun , struct str , fun));
exit(0);
}
0xa089aa10
0xa089aa10
按 <RETURN> 来关闭窗口...
//搞定
/*
编译出现错误error: cast from 'void (**)(int, int)' to 'unsigned int' loses precision [-fpermissive]
printf("0x%x\n" , container_of(&str.fun , struct str , fun));
编译的系统为linux64位,其指针类型和long型大小相等(8字节)而与int型4B所以出现了精度丢失,系统报错。将unsigned int 改成unsigned long即可
*/
4、宏定义中do{ }while(0)
如果要使用宏定义来定义多条语句时,采用do { … } while (0) 的形式是一种较好的方法。空的宏定义避免warning;存在一个独立的block中,可以用来进行变量定义作用域是块,因此可以实现比较复杂的功能;如果出现在判断语句过后的宏,这样可以保证作为一个整体来是实现。
5、宏定义中#和##的用法
宏定义默认遇到换行符才终止,但是可以通过\续行。
- #的功能是将其后面的宏参数进行字符串化操作(Stringizing),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号,然后利用输出函数时候可以直接输出字符。
#define WARNING(EXP) \
do{ if (EXP) \
fprintf(stderr, "Warning: " #EXP "\n"); } \
while(0)
WARNING(divider == 0);//由于do{}while(0)的效果,后面可以加分号,类似函数调用。
//等效于:
do {
if (divider == 0)
fprintf(stderr, "Warning" "divider == 0" "\n");
} while(0);
- ##被称为连接符(concatenator),用来将两个Token连接为一个Token。注意这里连接的对象是Token就行,而不一定是宏的变量。比如你要做一个菜单项命令名和函数指针组成的结构体的数组,并且希望在函数名和菜单项命令名之间有直观的、名字上的关系。
#define COMMAND(NAME) { NAME, NAME##_command }
struct command
{
char * name;
void (*function) (void);
};
struct command commands[] = {
COMMAND(quit),
COMMAND(help),
};//定义结构体数组并且通过宏定义初始化,前面应该定义了对应的quit_command()函数才可以。
#define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d
LINK_MULTIPLE(name,company,position,salary);
//等效于name_company_position_salary;
//UBOOT里面运用#和##定义菜单宏。
struct cmd_tbl_s {
char *name; /* Command Name */
int maxargs; /* maximum number of arguments */
int repeatable; /* autorepeat allowed? */
/* Implementation function */
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);//function pointer
char *usage; /* Usage message (short) */
char *help; /* Help message (long) */
};
//这是结构体,里面存放菜单需要的函数指针和字符指针说明用法。
//为了达到直观的效果,利用宏定义进行初始化。
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
//链接时候放在对应的段,后期直接到指定地方寻址,快速。
typedef struct cmd_tbl_s cmd_tbl_t;
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}
//定义一个变量,并进行初始化。运用了格式字符串和连接符。
U_BOOT_CMD(
menu, 3, 0, do_menu,
"menu - display a menu, to select the items to do something\n",
" - display a menu, to select the items to do something"
);//效果很实用,达到方便快捷的效果。
6、变参数宏:…和__VA_ARGS__
7、Side Effect是指宏在展开的时候对其参数可能进行多次Evaluation(也就是取值)
#define min(X,Y) ((X) > (Y) ? (Y) : (X))
int c = min(a,func(b));
//这时进行宏替换的时候func()函数就被调用了两次。很有可能出现问题,为了解决这个潜在的问题,改写宏定义min(X,Y):
#define min(X,Y) ({ \
typeof (X) x_ = (X); \
typeof (Y) y_ = (Y); \
(x_ < y_) ? x_ : y_; }) //typeof将X类型返回用来定义x_暂存(X)。
//({...})的作用是将内部的几条语句中最后一条的值返回,它也允许在内部声明变量(因为它通过大括号组成了一个局部Scope),然后利用变量进行计算,最后一句是宏定义结果的返回。前面container_of宏定义用到了这种功能。