GNU C 与ANSI C 相比,独特的9个地方

2024-02-27 21:08
文章标签 地方 gnu ansi 相比 独特

本文主要是介绍GNU C 与ANSI C 相比,独特的9个地方,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

内核中使用的是GNU C,所以这些特点,经常会在内核中出现。

1.允许零长度数组

GNU C允许零长度数组,在定义变长对象的头结构时,这个特性非常有用。

struct var_data s

{

      int len;

      char data[0];

};

char data[0]仅仅意味着程序中通过var_data的结构体实例的data[index]成员可以访问len之后的第index个地址,并没有为data[0]分配内存。

假设struct var_data的数据域保存在struct var_data紧接着的内存区域,通过如下代码可以遍历这些数据:

struct var_data s;

...

for (i=0;i<s.len;i++)

{

    printf("%02x",s.data[i]);

}

2、case范围

GNU C 支持case x...y 这样的语法,区间[x,y]的数都会满足这个case的条件,记得数据结构试验时,有的同学为了做菜单使用了仅100个case,还好我做的是GUI的

switch(c)

{

      case '0'...'9': c-='0';

      break;

      case 'a'...'f': c-='a'-10;

      break;

      case 'A'...'F': c-='A'-10;

      break;

}

这个case的特点大家都看得出来,比标准C少敲了多少case啊

3、语句表达式

GNU C把包含在括号里的复合语句看做是一个表达式,称为语句表达式,它可以出现在任何允许表达式的地方。我们可以在语句表达式中使用原本只能在复合语句中使用的循环变量、局部变量等,例如

#define min_t(type,x,y) \

({type __x=(x); type __y=(y);__x<__y?__x:__y})

int ia,ib,mini;

mini=min_t(int,ia,ib);

这样,因为重新定义了__x和__y这两个局部变量,所以上述方法定义的宏将不会有副作用。在标准C中,对应的宏通常会有副作用:

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

而代码min(++ia,++ib)将会被展开为

((++ia)<(++ib)?(++ia):(++ib)) 传入宏的参数会被增加两次。

这个在 嵌入式程序员应知道的0x10个基本问题 里有讲过。

4、typeof关键字

typeof(x)语句可以获得x的类型,因此,我们可以借助typeof重新定义第3条提到的min_t这个宏

#define min(x,y) \

({ \

      const typeof(x) _x=(x);\

      const typeof(y) _y=(y);\

      (void) (&_x==&_y);\

       _x<_y ? _x: _y ; })

我们不需要像第三条时那样传一个type进去,因为通过typeof(x)可以得到type。

代码 (void) (&_x==&_y);的作用是检查_x和_y的类型是否一致。

5、可变参数的宏

标准C只支持可变参数的函数,意味着函数的参数可以是不固定的

例如printf()函数的原型是

int printf(const char *format [,argument]...);

而在GNU C中,宏也可以接受可变数目的参数,例如

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

这里arg表示其余的参数可以是零个或多个,这些参数以及参数之间的逗号构成arg的值,

在宏扩展时替换arg ,例如

pr_debug("%s:%d",filename,line);

被扩展为

printk("%s:%d",filename,line);

使用##的原因是为了处理arg不代表任何参数的情况,这时候,前面的逗号就变得多余了。

使用##之后,GNU C预处理器会丢弃前面的逗号,这样代码

pr_debug("success!\n") 会被正确扩展为 printk("success!\n");

而不是 printk("success!\n",);

6.标号元素

标准c要求数组或结构体的初始化值必须以固定的顺序出现,在GNU C中,通过指定索引或结构体成员名,允许初始化值得以任意顺序出现。

指定数组索引的方法是在初始化值前添加 [INDEX]= ,当然也可以用 [FIRST...LAST]= 的形式指定一个范围。例如下面的代码定义一个数组,并把其中的所有元素赋值为0:

unsigned char data[MAX] ={[0...MAX-1]=0 };

下面的代码借助结构体成员名初始化结构体:

struct file_operations DEMO_fops = {
    owner :    THIS_MODULE,
    llseek:      DEMO_llseek,
    read:       DEMO_read,
    write:       DEMO_write,
    ioctl:        DEMO_ioctl,
    open:        DEMO_open,
    release:   DEMO_release,
};

但是Linux 2.6还是推荐采用标准C的方式,如下

struct file_operations DEMO_fops = {
    .owner =    THIS_MODULE,
    .llseek =   DEMO_llseek,
    .read =     DEMO_read,
    .write =    DEMO_write,
    .ioctl =    DEMO_ioctl,
    .open =     DEMO_open,
    .release = DEMO_release,
};

7.当前函数名

GUN C预定义了两个标识符保存当前的函数名,__FUNCTION__保存函数在源码中的名字,

__PRETTY_FUNCTION__保存带语言特色的名字。在c函数中,这两个名字是相同的。

void example()

{

      printf("This is function: %s ",__FUNCTION__);

}

代码中的__FUNCTION__意味着字符串"example"

8、特殊属性声明

GNU C允许声明函数、变量和类型的特殊属性,以便进行手工的代码优化和定制代码检查的方法。指定一个声明的属性,只需要在申明后添加 __attribute__((ATTRIBUTE))

其中ATTRIBUTE为属性说明,如果存在多个属性,则以逗号分隔。GNU C支持noreturn format section aligned packed等十多个属性

noreturn属性作用于函数,表示该函数从不返回。这会让编译器优化代码,并消除不必要的的警告信息。例如

#define ATTRIB_NORET __attribute__ ((noreturn)) ....

asmlinkage NORET_TYPE void do_exit(long error_code) ATTRIB_NORET;

format属性也可用于函数,表示该函数printf scanf 或strftime风格的参数,指定format属性可以让编译器根据格式串检查参数类型。例如:

asmlinkage int printk(const char * fmt,...)\

__attribute__((format(printf,1,2)));

详细的可以看http://blog.163.com/sunm_lin/blog/static/9192142200741533038695/

unused属性作用于函数和变量,表示该函数或变量可能不会被用到,避免编译器产生的警告信息。

aligned属性指定结构体、变量、联合体的对齐方式。packed属性作用于变量和类型,表示压缩结构体,使用最小的内存。

struct examprl_struct

{

      char a;

      int b;

      long c;

}__attribute__((packed));

注意,这个__attribute__((packed))只能用在GNU C

关于在VC下的结构体对齐,参照http://hi.baidu.com/deep_pro/blog/item/421db081aeb604debd3e1e01.html

9、内建函数

GNU C 提供了大量的内建函数,其中很多是标准 C 库函数的内建版本,例如memcpy,它们与对应的 C 库函数功能相同,本文不讨论这类函数,其他内建函数的名字通常以 __builtin 开始。

* __builtin_return_address (LEVEL)

内建函数 __builtin_return_address 返回当前函数或其调用者的返回地址,参数LEVEL 指定在栈上搜索框架的个数,0 表示当前函数的返回地址,1 表示当前函数的调用者的返回地址,依此类推。例如:

++++ kernel/sched.c
437:                 printk(KERN_ERR “schedule_timeout: wrong timeout ”
438:                        “value %lx from %p\n”, timeout,
439:                        __builtin_return_address(0));

* __builtin_constant_p(EXP)

内建函数 __builtin_constant_p 用于判断一个值是否为编译时常数,如果参数 EXP 的值是常数,函数返回 1,否则返回 0。例如:

++++ include/asm-i386/bitops.h
249: #define test_bit(nr,addr) \
250: (__builtin_constant_p(nr) ? \
251: constant_test_bit((nr),(addr)) : \
252: variable_test_bit((nr),(addr)))

很多计算或操作在参数为常数时有更优化的实现,在 GNU C 中用上面的方法可以根据参数是否为常数,只编译常数版本或非常数版本,这样既不失通用性,又能在参数是常数时编译出最优化的代码。

* __builtin_expect(EXP, C)

内建函数 __builtin_expect 用于为编译器提供分支预测信息,其返回值是整数表达式 EXP 的值,C 的值必须是编译时常数。例如:

++++ include/linux/compiler.h
13: #define likely(x)       __builtin_expect((x),1)
14: #define unlikely(x)     __builtin_expect((x),0)
++++ kernel/sched.c
564:         if (unlikely(in_interrupt())) {
565:                 printk(”Scheduling in interrupt\n”);
566:                 BUG();
567:         }

这个内建函数的语义是 EXP 的预期值是 C,编译器可以根据这个信息适当地重排语句块的顺序,使程序在预期的情况下有更高的执行效率。上面的例子表示处于中断上下文是很少发生的,第 565-566 行的目标码可能会放在较远的位置,以保证经常执行的目标码更紧凑。

这篇关于GNU C 与ANSI C 相比,独特的9个地方的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/753597

相关文章

G++(GNU C++)安装及使用(windows)

安装步骤: 1、下载 Cygnus 的G++编译器(http://www.claremontmckenna.edu/math/ALee/g++/full.exe),并保存到E盘(或其它任意盘),格式为E:full.exe。 2、直接运行该文件,可能需要十几分钟的时间(中间会有停顿甚至没有任何提示性图片或者符号,需要耐心等待哦)。       或者Win + R ,输入cmd,按Ent

全倒装COB超微小间距LED显示屏的工艺技术,相比SMD小间距有何优势

全倒装COB(Chip On Board)超微小间距LED显示屏,在工艺技术上的革新,相较于传统的SMD(Surface Mount Device)小间距LED显示屏,展现出了多方面的显著优势。 首先,全倒装技术极大地提升了LED芯片的散热性能。通过将芯片直接焊接在基板上,减少了热阻,使得热量能够更快速地传导至基板并散发出去,有效避免了因高温导致的光衰和色彩偏移问题,从而保证了显示屏的长期稳定性

二分猜数易错的地方

今天做简单的二分猜数问题,while循环总是跳不出去,搞了好久才弄好了。。。好几次都这样,总结一下吧 while(left <= right){int mid = (left+right)/2;if(ok(mid)) left = mid+1,ans = mid;//记录结果,要不然跳不出循环,视题目而定,ans也可以写在下面else right = mid-1;}

软文发稿相比其他广告形式有哪些持续性优势?

软文发稿在品牌宣发中具有显著的持续性优势,特别是在与其他广告形式的比较中更能体现这些特点。凭借其潜移默化的影响力、增强品牌权威性和公信力、持续性的曝光优势、精准触达目标受众的能力、强互动性与引导性,以及较高的性价比,已经成为品牌推广不可或缺的手段 一 长期存在与持续曝光 长时间的内容可见性     软文发表后,通常会长时间存在于各种平台上,无论是官网、博客、行业网站还是社交媒体帖子。读

GNU工具集和MinGW

GNU工具集         GNU工具集包含了一系列的基本工具和实用程序,这些工具为系统管理员、开发人员和普通用户提供了丰富的功能和工具支持。         许多开源软件和项目都依赖于GNU工具集提供的稳定和强大的功能,因此GNU工具集的发展与完善对整个开源社区都具有重要意义。通过GNU工具集,用户可以在命令行下完成各种任务,如文件操作、进程管理、编程开发等,为用户提供了强大的工具支持。

GNU的伪操作 (25)

这里主要是 对 GNU的 各个伪操作进行 详细的解释。 先来看着几个 伪操作。 .byte,  .short,  .long,  .quad , .float ,  这个是关于 字节的。 .string   .ascii 是关于字符串的。 这个字符串编译器是可以自动在末尾补0 的。 举例: val:         .word 0x11223344         m

利用UIWebView获取userAgent需要注意的地方

网络通信有时候需要传递参数userAgent,iOS中获取userAgent很简单. UIWebView* webView = [[UIWebView alloc] initWithFrame:CGRectZero];NSString *userAgentString = [webView stringByEvaluatingJavaScriptFromString:@"naviga

解密虚拟内存0x400000以下的地方

一. 前言   最近看CSAPP时,对以前没有仔细注意的一处知识盲区产生了兴趣,所以进行了深入研究,并写下此文一记录。 二. 问题   二话不说直接上图。下图是CSAPP第七章的虚拟内存分析图。书中提到 在X86-64位Linux系统中,代码段总是从地址0x400000处开始,后面是数据段。堆在数据段之后,通过调用malloc向上增长…   但是0X400000以下呢?为什么没有

云计算和传统IT相比,有哪些优势?

云计算相比于传统的IT基础设施,具有以下一些显著的优势: 成本效益: 云计算通常采用按需付费模式,用户只需为实际使用的资源支付费用,避免了高昂的前期硬件投资和维护成本。 弹性计费方式使得企业可以根据业务需求灵活调整资源,从而优化成本。 可伸缩性: 云服务提供了几乎无限的计算能力和存储空间,允许用户根据需要快速增加或减少资源,以应对业务高峰或低谷。 这种灵活性使得企业能够更轻松地管理业务增长,而无需

GNU/Linux - Open函数使用的O_CLOEXEC flag

在 Linux 中,“O_CLOEXEC ”标志与 “open ”系统调用一起使用,用于指定在使用 “exec ”系列函数(如 “execve”、“execl ”等)执行新程序时,“open ”返回的文件描述符应自动关闭。 In Linux, the `O_CLOEXEC` flag is used with the `open` system call to specify that