你知道GNU C 对C语言的扩展吗?

为了方便使用,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, p, 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 p .text:
0000000000400474 <p1>:400474:    55                       push   %rbp400475:    48 89 e5                 mov    %rsp,%rbp400478:    89 7d fc                 mov    %edi,-0x4(%rbp)40047b:    8b 45 fc                 mov    -0x4(%rbp),%eax40047e:    83 c0 01                 add    $0x1,%eax400481:    89 05 3d 04 20 00        mov    %eax,0x20043d(%rip)        # 6008c4 <q>400487:    c9                       leaveq 400488:    c3                       retq   0000000000400489 <p2>:400489:    55                       push   %rbp40048a:    48 89 e5                 mov    %rsp,%rbp40048d:    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),%eax40049c:    8b 55 fc                 mov    -0x4(%rbp),%edx40049f:    8d 04 02                 lea    (%rdx,%rax,1),%eax4004a2:    03 45 f4                 add    -0xc(%rbp),%eax4004a5:    03 45 f0                 add    -0x10(%rbp),%eax4004a8:    83 c0 01                 add    $0x1,%eax4004ab:    89 05 13 04 20 00        mov    %eax,0x200413(%rip)        # 6008c4 <q>4004b1:    c9                       leaveq 4004b2:    c3                       retq   00000000004004b3 <main>:4004b3:    55                       push   %rbp4004b4:    48 89 e5                 mov    %rsp,%rbp4004b7:    53                       push   %rbx4004b8:    8b 05 0a 04 20 00        mov    0x20040a(%rip),%eax        # 6008c8 <t1>4004be:    89 c7                    mov    %eax,%edi4004c0:    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,%esi4004df:    89 c7                    mov    %eax,%edi4004e1:    e8 a3 ff ff ff           callq  400489 <p2>4004e6:    b8 00 00 00 00           mov    $0x0,%eax4004eb:    5b                       pop    %rbx4004ec:    c9                       leaveq 4004ed:    c3                       retq   4004ee:    90                       nop4004ef:    90                       nopDisassembly of p .data:
00000000006008c0 <__data_start>:6008c0:    00 00                    add    %al,(%rax)...00000000006008c4 <q>:6008c4:    5a                       pop    %rdx6008c5:    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...

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

转自:https://my.oschina.net/LinuxDaxingxing/blog/751319


推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

嵌入式Linux

微信扫描二维码,关注我的公众号

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/465355.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 反射 内存_Java内存到反射入门

Java内存到反射入门(一)初识Java内存平常我们最常提到的的JAVA分区是这三个分区其中方法区是一个特殊的堆&#xff0c;功能如图上所示。初识反射&#xff1a;框架设计的灵魂反射的功能&#xff1a;将类的各个组成部分封装为对象&#xff0c;并在程序运行的过程中可以调用它们。…

stopping hbasecat: /tmp/hbase-elastic-master.pid: 没有那个文件或目录

stop-hbase.sh 挂了。。。 原因其实也简单&#xff0c;默认情况下hbase的pid文件保存在/tmp目录下&#xff0c;/tmp目录下的文件很容易丢失&#xff0c;不知道为什么他不会自动创建一个非临时目录&#xff0c;大概是因为权限不好控制&#xff0c;需要手动来&#xff1f; 修改为…

服务容错模式

0.背景 随着服务框架和服务治理体系的逐步成熟&#xff0c;服务化已成为系统设计的趋势。随着业务复杂度的增加&#xff0c;依赖的服务也逐步增加&#xff0c;出现了不少由于服务调用出现异常问题而导致的重大事故&#xff0c;如&#xff1a; 1&#xff09;系统依赖的某个服务发…

从大学到结婚,我和小云的这13年

看了龙哥的文章&#xff0c;感触很深&#xff0c;我有好几次想写一篇我和小云的文章&#xff0c;记录下自己和小云这些年一起经历的很多事情&#xff0c;但是每每都停下来去做了觉得更重要的其他事情&#xff0c;这次真的决定要写了。我和小云是大学同学&#xff0c;我们大一认…

xshell如何运行java_利用Xshell往应用服务器上部署项目

1.首先查询中间件的进程Idps -ef|grep ./startapusic2.然后杀掉进程kill -9 16833.再次查询进程&#xff0c;查看是否关闭成功&#xff0c;如果没有/bin目录下的东西了说明关闭成功4.利用ls查看当前路径下的文件5.启动中间件./startapusic6.如果显示地址已在使用&#xff0c;则…

详解Linux内核在arm上的启动过程

Linux内核加载过程通常&#xff0c;Linux内核都是经过gzip加载过之后的映像文件。bootloader复制压缩内核到内存空间。内核自解压。运行内核。编译完成的Linux内核存放在哪里&#xff1f;./vmlinux elf格式未压缩内核。arch/arm/boot/compressed/vmlinux 压缩以后的elf格…

java 数据库 properties_原生Java读取properties连接数据库

将数据库的驱动、url、用户名、密码等信息以键值对的形式存放在properties文件中&#xff0c;工具类初始化时从配置文件中读取所要连接数据库的信息。当需要更改连接的数据库时&#xff0c;只需要更改配置文件即可&#xff0c;而不必改写工具类的代码。1.在项目中创建一个prope…

【谱聚类算法总结】

前言&#xff1a;以前只是调用过谱聚类算法&#xff0c;我也不懂为什么各家公司都问我一做文字检测的这个算法具体咋整的&#xff0c;没整明白还给我挂了哇擦嘞&#xff1f;讯飞还以这个理由刷本宝&#xff0c;今天一怒把它给整吧清楚了&#xff0c;下次谁再问来&#xff01;说…

嵌入式的汉字原来是这样显示的?

摘要&#xff1a;LCD是嵌入式常见设备&#xff0c;如何在LCD上显示汉字和英文&#xff1f;矢量字体和点阵字体有何不同&#xff1f;同一个字符为何有多种编码&#xff1f;GB2312、GB18030指什么&#xff1f;他们之间有关系吗&#xff1f;嵌入式设备如何支持多国语言&#xff1f…

查看数据库、表、索引的物理存储情况

在管理、维护数据库时&#xff0c;经常需要查看数据库文件、日志文件所占用的磁盘空间&#xff0c;以及磁盘还剩下的磁盘空间&#xff0c;你可以通过下面几种方面查看数据库文件详细情况&#xff1a;1&#xff1a; 是通过远程连接(或VPN)连接到数据库服务器&#xff0c;直接去查…

Spring简化Java开发_spring如何简化java开发

1、spring简介Spring的主要目的是用来替代更加重量级的企业级的java技术2、spring如何简化java开发1)基于POJO的轻量级和最小侵入性编程&#xff1b;2)通过依赖注入和面向接口实现松耦合&#xff1b;3)基于切面和惯例进行声明式编程&#xff1b;4)通过切面和模板减少样版式代码…

Linux 应用调试神器- ASan

今天有小伙伴跟我他的程序崩溃了要怎么办&#xff0c;刚好我最近做项目使用到这个神器&#xff0c;做应用开发的同学可以看看&#xff0c;可以在C/C上配置使用。不过这仅限于调试&#xff0c;如果你是发布版本&#xff0c;一定要把这个功能关闭。当然&#xff0c;这是我实操的结…

asp.net验证提示美化效果

前段时间做了个ymPrompt提示效果的验证控件&#xff0c;ymPrompt是弹出提示的。弹出提示会增加无畏的鼠标点击动作。前段时间看到园里有位高手写了个纯cssjs的一个效果&#xff0c;我把它和asp.net验证控件结合起来&#xff0c;用这位高手写的效果做为asp.net难控件的提示效果。…

在Linux内核使用Kasan

昨天的文章发出来后&#xff0c;有同学在群里说内核也可以使用这个工具&#xff0c;所以再转发一篇wowo网站的文章&#xff0c;希望对大家有帮助。Linux 应用调试神器- ASan1. 前言KASAN是一个动态检测内存错误的工具。KASAN可以检测全局变量、栈、堆分配的内存发生越界访问等问…

跟韦老师聊了一个深夜

自从尚明从韦老师那边离职后&#xff0c;我已经很难得到韦老师的消息了&#xff0c;有时候我也想过去看下他&#xff0c;但是也很难抽的开时间。而且&#xff0c;我们两都挺技术的&#xff0c;如果见面后不知道聊什么&#xff0c;会不会有点尴尬。&#x1f604;上面那个回复是我…

大厂不是铁饭碗

最近的事情有点多前段时间一个以前的朋友&#xff0c;买房找我咨询&#xff0c;问我恒大的房子能不能买&#xff0c;我当时没有给出明确的回复&#xff0c;但是也说了自己的看法。近些年房子越来越贵&#xff0c;我是从15年开始接触房子&#xff0c;不过我从没有想过靠房子来赚…

使用valgrind检测内存问题

valgrind是一款用于内存调试、内存泄漏检测以及性能分析的软件开发工具。1valgrind安装可以到官网下载最新的源码包&#xff1a;valgrind官网下载&#xff0c;也可以直接使用 c_utils/debug/valgrind 目录提供的 valgrind-3.13.0.tar.bz2 源码包。首先解压源码包tar xjf valgri…

项目中cxf和weblogic整合时报错的问题

GJYW项目使用的weblogic版本是10.3.6&#xff0c;cxf使用的版本是3.1.4 在将项目部署到weblogic服务器上时就会报错&#xff0c;通过下面的方式可以解决weblogic和cxf框架在一起报错的问题&#xff08;解决了本项目报错的问题&#xff0c;未必全部适用&#xff09;&#xff1a;…

下周开幕!给深圳的嵌入式和电子工程师准备的嘉年华来了

我和电子圈老江认识了很久&#xff0c;应该是2012年&#xff0c;小龙第一次参加电子圈年会&#xff0c;那年他年会中奖的奖品送给我。后来&#xff0c;我也加入了电子圈的QQ群&#xff0c;早些年的时候&#xff0c;大家都喜欢在QQ群聊天&#xff0c;后来才慢慢转到微信群。老江…

AS3.0中的显示编程(末篇)-- 滤镜(下)

剩下的三种滤镜&#xff0c;因为我自己也不是很懂矩阵啊这些的&#xff0c;只能做些简单的范例和说明了&#xff0c;抱歉&#xff01;颜色矩阵滤镜、卷积滤镜、置换图滤镜这三种滤镜只能通过AS代码实现。如果说上面的六种滤镜&#xff0c;只是在原图的基础上做些简单的修改&…