GNU C 对标准C语言的扩展(转载 懵懵懂懂)

GNU C 对标准C语言的扩展

为了方便使用,GNU C在标准C语言的基础上进行了部分方便开发的扩展。这里讲解一些开发中可能会用到的,或者使用频率比较高的内容。

1 零长度数组和变量长度数组

GNU C 允许使用零长度数组:

char data[0];

GNU C 允许使用一个变量定义数组的长度:

int n = 0;
scanf("%d",&n);
int array[n];

2 case 范围

GNU C 支持 case x … y 这样的语法,[x,y]之间的数均满足条件。

case 'a' ... 'z': /*从 'a' 到 'z' */
break;

3 语句表达式

GNU C 把包含在括号中的符合语句看作一个表达式,称为语句表达式。

#define min_t(type,x,y) \
         ({type __x=(x); type __y=(y);__x<__y?__x:__y;})

这种写法可以避免如下写法中 min_t(x++,++y) 中出现的副作用。

 #define min_t(x,y) ((x)<(y)?(x):(y))  

4 typeof 关键字

typeof(x)可以获得x的类型借助typeof关键字我们可以重新定义min_t:

#define min_t(x,y)\
    ({typeof(x) __x=(x); typeof(y) __y=(y);__x<__y?__x:__y;})  

5 可变参数宏

GNU C 中宏也支持可变参数

#define pr_debug(fmt,arg...) \
        printk(fmt,##arg)  

这里,如果可变参数被忽略或为空,“##”操作将使预处理器去掉它前面的那个逗号。如果你在宏调用时,确实提供了一些可变参数,GNU C也会工作正常,它会把这些可变参数放到逗号的后面。

6 标号元素 不懂?

标准 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,
};  

7 当前函数名

GNU C 中预定义两个标志符保存当前函数的名字,__ FUNCTION __ 保存函数在源码中的名字, __ PRETTY__ FUNCTION __保存带语言特色的名字。在C函数中这两个名字是相同的.

void func_example()
{
     printf("the function name is %s",__FUNCTION__);
}

注意:在C99中支持__ func __ 宏,因此建议使用 __ func __ 替代 __ FUNCTION __ 。

8 特殊属性声明

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));  

regpram 属性用于指定最多可以使用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((regpram(3)))
#define REGPARM0 __attribute((regpram(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 <p1>:
  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 <q>
  400487:    c9                       leaveq 
  400488:    c3                       retq   


0000000000400489 <p2>:
  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 <q>
  4004b1:    c9                       leaveq 
  4004b2:    c3                       retq   


00000000004004b3 <main>:
  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 <t1>
  4004be:    89 c7                    mov    %eax,%edi
  4004c0:    e8 af ff ff ff           callq  400474 <p1>
  4004c5:    8b 0d 09 04 20 00        mov    0x200409(%rip),%ecx        # 6008d4 <t4>
  4004cb:    8b 15 ff 03 20 00        mov    0x2003ff(%rip),%edx        # 6008d0 <t3>
  4004d1:    8b 1d f5 03 20 00        mov    0x2003f5(%rip),%ebx        # 6008cc <t2>
  4004d7:    8b 05 eb 03 20 00        mov    0x2003eb(%rip),%eax        # 6008c8 <t1>
  4004dd:    89 de                    mov    %ebx,%esi
  4004df:    89 c7                    mov    %eax,%edi
  4004e1:    e8 a3 ff ff ff           callq  400489 <p2>
  4004e6:    b8 00 00 00 00           mov    $0x0,%eax
  4004eb:    5b                       pop    %rbx
  4004ec:    c9                       leaveq 
  4004ed:    c3                       retq   
  4004ee:    90                       nop
  4004ef:    90                       nop


Disassembly of section .data:
00000000006008c0 <__data_start>:
  6008c0:    00 00                    add    %al,(%rax)
    ...


00000000006008c4 <q>:
  6008c4:    5a                       pop    %rdx
  6008c5:    00 00                    add    %al,(%rax)
    ...


00000000006008c8 <t1>:
  6008c8:    01 00                    add    %eax,(%rax)
    ...


00000000006008cc <t2>:
  6008cc:    02 00                    add    (%rax),%al
    ...


00000000006008d0 <t3>:
  6008d0:    03 00                    add    (%rax),%eax
    ...


00000000006008d4 <t4>:
  6008d4:    04 00                    add    $0x0,%al
    ...

关于GCC基于X64体系结构的调用约定的话,那就很容易可以看出,函数p1和p2都使用寄存器传递参数,顺序就是RDI, RSI, RDX, RCX,这些细节已经跟regparm的规定完全不一致了。所以,在这里作者觉得,regparm已经不起作用了。

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值