为了方便使用,gnu c在标准c语言的基础上进行了部分方便开发的扩展。
这里讲解一些开发中可能会用到的,或者使用频率比较高的内容。
零长度数组和变量长度数组 gnu c 允许使用零长度数组,比如:
char data[0];
gnu c 允许使用一个变量定义数组的长度如:
int n=0;scanf(%d,&n);int array[n];
case 范围
gnu c支持 case x...y这样的语法,[x,y]之间数均满足条件。
case 'a'...'z': /*from 'a' to 'z'*/break;
语句表达式
gnu c 把包含在括号中的复合语句看作是一个表达式,称为语句表达式。
#define min_t(type,x,y) ({type __x=(x); type __y=(y);__x<__y?__x:__y;})
这种写法可以避免:
#define min_t(x,y) ((x)<(y)?(x):(y))
在min_t(x++,++y)中出现的副作用。
typeof 关键字
typeof(x)可以获得x的类型借助typeof关键字我们可以重新定义min_t:
#define min_t(x,y) ({typeof(x) __x=(x); typeof(y) __y=(y);__x<__y?__x:__y;})
可变参数宏
gnu c中宏也支持可变参数:
#define pr_debug(fmt,arg...) printk(fmt,##arg)
这里,如果可变参数被忽略或为空,“##”操作将使预处理器去掉它前面的那个逗号。如果你在宏调用时,确实提供了一些可变参数,gnu c也会工作正常,它会把这些可变参数放到逗号的后面。
标号元素
标准c要求数组或结构体的初始化值必须以固定的顺序出现,在gnu c中,通过指定索引或结构体成员名,允许初始化以任意顺序出现。
unsigned char data[max] ={ [0]=10, [10]=100,};struct file_operations ext2_file_operations={ open:ext2_open, close:ext2_close,};
在linux 2.6中推荐如下方式:
struct file_operations ext2_file_operations={ .read=ext2_read, .write=ext2_write,};
当前函数名
gnu c中预定义两个标志符保存当前函数的名字,__ function __ 保存函数在源码中的名字, __ pretty__ function __保存带语言特色的名字。在c函数中这两个名字是相同的。
void func_example(){ printf(the function name is %s,__function__);}
在c99中支持__ func __ 宏,因此建议使用 __ func __ 替代 __ function __ 。
特殊属性声明
gnu c 允许声明函数、变量和类型的特殊属性,以便进行手工的代码优化和定制。如果要指定一个属性声明,只需要在声明后添加__ attribute __((attribute))。其中attribute为属性说明,如果存在多个属性,则以逗号分隔。gnu c 支持noreturn,noinline, always_inline, pure, const, nothrow, format, format_arg, no_instrument_function, section, constructor, destructor, used, unused, deprecated, weak, malloc, alias warn_unused_result nonnull等十个属性。
noreturn属性作用于函数,表示该函数从不返回。这会让编译器优化代码并消除不必要的警告信息。例如:
#define attrib_noret __attribute__((noreturn)) ....asmlinkage noret_type void do_exit(long error_code) attrib_noret;
packed属性作用于变量和类型,用于变量或结构域时,表示使用最小可能的对齐,用于枚举、结构或联合类型时表示该类型使用最小的内存。如对于结构体,就是它告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。例如:
struct example_struct{ char a; int b; long c;} __attribute__((packed));
regparm属性用于指定最多可以使用n个寄存器(eax, edx, ecx)传递参数,n的范围是0~3,超过n时则将参数压入栈中(n=0表示不用寄存器传递参数)。
注意:以上这些属性都是在x86处理器体系结构下的,在x64体系结构下,大部分内容都是同样有效的。但是,这里要注意regparm属性,由于在x64体系结构下,gun c的默认调用约定使用寄存器传递参数。所以,如果regparm属性里使用的寄存器个数超过3个,也仍然会使用其他寄存器来传递参数。这一点要遵循x64体系结构的调用约定。
下面可以看一个例子:
int q = 0x5a;int t1 = 1;int t2 = 2;int t3 = 3;int t4 = 4;#define regparm3 __attribute((regparm(3)))#define regparm0 __attribute((regparm(0)))void regparm0 p1(int a){ q = a + 1;}void regparm3 p2(int a, int b, int c, int d){ q = a + b + c + d + 1;}int main(){ p1(t1); p2(t1,t2,t3,t4); return 0;}
使用objdump命令反汇编,相关命令如下:
objdump -d 可执行程序
其中-d选项用于反汇编所有的程序段,包括:代码段、数据段、只读数据段以及一些系统段等等。而-d命令只反汇编代码段的内容。
反汇编后的关键代码如下:
disassembly of section .text:0000000000400474 : 400474: 55 push %rbp 400475: 48 89 e5 mov %rsp,%rbp 400478: 89 7d fc mov %edi,-0x4(%rbp) 40047b: 8b 45 fc mov -0x4(%rbp),%eax 40047e: 83 c0 01 add $0x1,%eax 400481: 89 05 3d 04 20 00 mov %eax,0x20043d(%rip) # 6008c4 400487: c9 leaveq 400488: c3 retq 0000000000400489 : 400489: 55 push %rbp 40048a: 48 89 e5 mov %rsp,%rbp 40048d: 89 7d fc mov %edi,-0x4(%rbp) 400490: 89 75 f8 mov %esi,-0x8(%rbp) 400493: 89 55 f4 mov %edx,-0xc(%rbp) 400496: 89 4d f0 mov %ecx,-0x10(%rbp) 400499: 8b 45 f8 mov -0x8(%rbp),%eax 40049c: 8b 55 fc mov -0x4(%rbp),%edx 40049f: 8d 04 02 lea (%rdx,%rax,1),%eax 4004a2: 03 45 f4 add -0xc(%rbp),%eax 4004a5: 03 45 f0 add -0x10(%rbp),%eax 4004a8: 83 c0 01 add $0x1,%eax 4004ab: 89 05 13 04 20 00 mov %eax,0x200413(%rip) # 6008c4 4004b1: c9 leaveq 4004b2: c3 retq 00000000004004b3 : 4004b3: 55 push %rbp 4004b4: 48 89 e5 mov %rsp,%rbp 4004b7: 53 push %rbx 4004b8: 8b 05 0a 04 20 00 mov 0x20040a(%rip),%eax # 6008c8 4004be: 89 c7 mov %eax,%edi 4004c0: e8 af ff ff ff callq 400474 4004c5: 8b 0d 09 04 20 00 mov 0x200409(%rip),%ecx # 6008d4 4004cb: 8b 15 ff 03 20 00 mov 0x2003ff(%rip),%edx # 6008d0 4004d1: 8b 1d f5 03 20 00 mov 0x2003f5(%rip),%ebx # 6008cc 4004d7: 8b 05 eb 03 20 00 mov 0x2003eb(%rip),%eax # 6008c8 4004dd: 89 de mov %ebx,%esi 4004df: 89 c7 mov %eax,%edi 4004e1: e8 a3 ff ff ff callq 400489 4004e6: b8 00 00 00 00 mov $0x0,%eax 4004eb: 5b pop %rbx 4004ec: c9 leaveq 4004ed: c3 retq 4004ee: 90 nop 4004ef: 90 nopdisassembly of section .data:00000000006008c0 : 6008c0: 00 00 add %al,(%rax) ...00000000006008c4 : 6008c4: 5a pop %rdx 6008c5: 00 00 add %al,(%rax) ...00000000006008c8 : 6008c8: 01 00 add %eax,(%rax) ...00000000006008cc : 6008cc: 02 00 add (%rax),%al ...00000000006008d0 : 6008d0: 03 00 add (%rax),%eax ...00000000006008d4 : 6008d4: 04 00 add $0x0,%al ...
如果读者还记得2.2.3节中,关于gcc基于x64体系结构的调用约定的话,那就很容易可以看出,函数p1和p2都使用寄存器传递参数,顺序就是rdi, rsi, rdx, rcx,这些细节已经跟regparm的规定完全不一致了。所以,在这里作者觉得,regparm已经不起作用了。
来源:https://my.oschina.net/linuxdaxingxing/blog/751319
IPD滤波器与射频滤波器对比
手电筒低压差线性恒流驱动方案
智能手表监测血糖,推动健康功能成为可穿戴设备研发趋势
橘子皮都能打开手机?是否打脸指纹安全解锁性能?
4个等级的晶振的特性和区别
C语言开发中可能会用到的GNU
夺得多项第一!问界M7冬测成绩大翻身?低温仍是纯电车的痛
云和数据促创新 中兴通讯GoldenDB亮相2020数据技术嘉年华
智能手机外观趋势:从华为P10、小米6、魅蓝E2衍生话题!
目前出现人工智能专业人才紧缺现状,人才培养体系出炉
虹科案例 | 虹科工业树莓派在节能建筑中的应用
高考作弊“黑科技”真能逃避安检系统吗
在PCB组装中X射线检查原理简介
德国新电动汽车和混合动力汽车使用的百分比得到了显著上涨
凌力尔特推出隔离反激式控制器-LT3798
allegro推出首款完全集成的高灵敏度电流传感器 适用于电流小于5A的应用
华为品牌价值位列全球第40名 中国手机向高端市场发起进攻
三防漆自动涂覆工艺的常见问题及解决方法
工信部出台PCB新规 将于2月1日起开始施行
智能网络必须针对任务关键型环境而发展