可变参数
#define DBG_OUTPUT(fmt, args...) \
printf("CK File[%s:%s(%d)]:" fmt "\n", __FILE__,__FUNCTION__, __LINE__, ##args)
这里,在宏形参表中,args…表示可变参数。
在宏定义体中,为了能够表示对可变参数的引用,需要一个特殊符号。
于是,使用了C语言中的连字符##。
这是一个固定格式,##args,用来在宏定义体中表示可变参数。
字符串融合,
在printf的处理过程中,多个字符串之间,不加任何其他字符,编译器会自动将前后的字符串融合成一个字符串,所以,如果是多个分段的字符串,最终会融合成一个整体字符串。
内置宏,
__FILE__
__LINE__
__FUNCTION__
__DATE__
__TIME__
++++++++++++++++++++++++++++++++++++++++++
do{}while(0)
显式定义一个局部作用域
场景一,
复合语句块的宏定义。
如果是复合语句块,将他们定义为宏时,GNU推荐的做法是,
使用do{}while(0)结构
例如,
#define swap(a, b) \
do
{ a = a+b;
b = a-b;
a = a-b;
}while(0)
场景二,
替代goto。
do{
ret = func1();
if(ret != 0)
break;
ret = func2();
if(ret != 0)
break;
ret = func3();
if(ret != 0)
break;
return 0;
}while(0);
+++++++++++++++++++++++++++++++++++++++++++++
成员索引操作符“.”或者"[]"
显式为结构体成员初始化赋值。
例如:
typedef struct st1 {
int a;
int b;
int c;
} st_t;
常规初始化为
st_t st1 = {
1,
2,
3
};
使用成员索引操作符,可以显式为各个成员初始化赋值,而且不用按照顺序,每句初始化赋值,以逗号结尾,也不用担心最后一句多了一个逗号而报错。
st_t st1 = {
.c = 3,
.a = 1,
.b = 2,
};
再比如,对于数组的成员的初始化赋值,
unsigned char data[MAX] =
{
[0]=10,
[10]=100,
};
通过成员索引符,选中特定的某个成员。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
typeof关键字
用来计算括号内的表达式结果的临时变量的类型,
例如:
我们定义了一个char*的指针chptr,
char* chptr;
然后,用*chptr寻址访问,获得一个char类型的临时变量,对这个临时变量使用typeof获取类型,得到的类型是char。
typeof (*chptr) ch; //预处理后为 char ch;
然后,对ch这个变量使用typeof获取类型,得到的类型是char,
用这个类型定义了一个char*指针类型的变量 chptr1,
typeof (ch) *chptr1; //预处理后为char *chptr1 ,等价于char* chptr1;
然后,对chptr1这个变量使用typeof获取类型,得到的类型是char指针类型,
用这个类型定义了一个char指针类型的数组 array,
typeof (chptr1) array[5]; //chptr1的数据类型为char * ,预处理后为 char* array[5];
在linux中的具体应用为,
#define min(x,y)
({ \
typeof(x) __min1 = (x); \
typeof(y) __min2 = (y); \
(void) (& __min1 == & __min2); \
__min1 < __min2 ? __min1 :min2;
})
通过typeof获得x和y的数据,然后定义两个临时变量,并把x和y的分别赋给两个临时变量最后进行比较。
另外,宏定义中
(void)(& __min1 = &__min2)
语句的作用是用来警告x和y不能属于不同的数据类型。
GNU中,扩展了取址操作符"&"的含义。
在可运行的函数体中,“&”表示常规的取址运算。
但是在宏定义体中,“&”表示取类型操作。在GNUC中,对于每个类型,编译器会赋予一个TYPE_ID。如果ID相同,那么类型相同。“&”就是取的类型ID来进行比较。
注意,GNUC扩展了表达式的含义。
对于{}括号包括起来的语句块,即复合语句块,可以被视为一条语句,
GNU C中,允许用小括号括起来的复合语句出现在一个表达式中。这个表达式的类型为复合语句中以分号结尾的最后一个子语句表达式的类型,其值也为最后子表达式的值。
来看一个实例。
int main()
{
int a=4;
int b=6;
int min_data;
min_data=min(a,b);
printf(“the min=%d\n”,min_data);
return 0;
}
输出结果为4。
但是如果是
int main()
{
int a=4;
float b=6;
int min_data;
min_data=min(a,b);
printf(“the min=%d\n”,min_data);
return 0;
}
则会提示以下警告:
main.c:17:9: warning: comparison of distinct pointer types lacks acast
+++++++++++++++++++++++++++++++++++++++++++++
零长数组
GNU C 允许使用零长度数组,在定义变长的头结构时,这个特性非常有用。
typedef structuser_def {
char *name;
int length;
char bytes[0];
} user_def_t;
int main()
{
int length = 10;
user_def_t *p;
p = (user_def_t*)malloc (sizeof(user_def_t)+ length);
if (p == NULL) {
printf("malloc failed\n");
exit(1);
}
p->name= "good";
p->length= length;
memset(p->bytes,0, length);
p->bytes[0]= 'a';
p->bytes[1]= 'b';
p->bytes[2]= 'c';
printf("%s\n", p->bytes);
free(p);
return 0;
}
此结构只能通过heap方式分配内存。
所以需要用malloc分配,并在最后用free释放。只能用指针作为句柄。
对于编译器而言, 此时长度为0的数组并不占用空间, 因为数组名本身不占空间, 数组名仅仅是一个符号, 它不会占用任何空间, 它在结构体中, 只是代表了一个偏移量, 代表一个不可修改的地址常量!
在结构体中,数组为0的数组必须在最后声明。
+++++++++++++++++++++++++++++++++++++++
__attribute__
GNU C 的一大特色就是attribute机制。可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
__attribute__ 书写特征是:attribute前后都有两个下划线,并切后面会紧跟一对原括弧,括弧里面是相应的attribute参数。
__attribute__ 语法格式为:__attribute__ ((attribute-list))
参数介绍
1、aligned
指定对象的对齐格式(以字节为单位),如:
struct S {
short b[3];
} __attribute__ ((aligned (8)));
typedef int int32_t __attribute__ ((aligned (8)));
该声明将强制编译器确保(尽它所能)变量类 型为struct S 或者int32_t 的变量在分配空间时采用8字节对齐方式。
你也可以使用默认的对齐方式。如果aligned 后面不紧跟一个指定的数字值,那么编译器将依据你的目标机器情况使用最大最有益的对齐方式。
struct S {
short b[3];
} __attribute__ ((aligned));
2、packed
aligned 属性使被设置的对象占用更多的空间,相反的,使用packed 可以减小对象占用的空间。
告诉编译器取消结构在编译过程中的优化对齐(使用1字节对齐),按照实际占用字节数进行对齐,是GCC特有的语法。跟编译器有关,
struct packed_struct
{
char c;
int i;
}__attribute__ ((__packed__));
3、at
绝对定位,可以把变量或函数绝对定位到Flash中,或者定位到SRAM。
绝对定位不能在函数中定义,只能放在函数外定义,定义为全局变量或者static变量。
定位到flash中,一般用于固化的信息,如出厂设置的参数,上位机配置的参数,ID卡的ID号,flash标记等等
const u16 gFlashDefValue[512] __attribute__((at(0x0800F000))) = {0x1111,0x1111,0x1111,0x0111,0x0111,0x0111};//定位在flash中,其他flash补充为00
const u16 gflashdata__attribute__((at(0x0800F000))) = 0xFFFF;
定位到SRAM中,一般用于数据量比较大的缓存,如某个位置的特定变量
uint8_t BUF[LEN] __attribute__ ((at(0X20002000)));//缓冲,最大LEN个字节,起始地址为0X20002000.
4、section
在ARM编译器编译之后,代码被划分为不同的段,RO Section(ReadOnly)中存放代码段和常量,RW Section(ReadWrite)中存放可读写静态变量和全局变量,ZI Section(ZeroInit)是存放在RW段中初始化为0的变量。
__attribute__((section(“section_name”))),其作用是将作用的函数或数据放入指定名为"section_name"对应的段中。
编译时为变量指定段:
/* in RO section */
const int descriptor[3] __attribute__ ((section ("descr"))) = { 1,2,3 };
/* in RW section */
long long rw[10] __attribute__ ((section ("RW")));
/* in ZI section */
long long altstack[10] __attribute__ ((section ("STACK"), zero_init));
编译时为函数指定段
void Function_Attributes_section_0 (void) __attribute__ ((section ("new_section")));
__attribute__ ((section ("bar"))) int f3()
{
return 1;
}
5、多个属性,组合使用
u8 FileAddr[100] __attribute__ ((section ("FILE_RAM"), zero_init,aligned(4)));