分享

GNU C与ANSI C

 黄南山 2017-12-13

     Linux上使用的C编译器是GNU C编译器,其对标准的C(ansi c)进行了一定的扩展,这带来的影响是两方面的。一方面增强了其原来的没有的功能,另一方面却对要编写移植性要求较高的程序带来了一些问题。对于后一个问题,在编写程序时,建议是如果在ANSI C中也提供的同样的功能时,尽量使用ANSI C来实现,当然如果不考虑程序的移植性,比如说,我们就是要在一个产品上写一段代码,那就不需要考虑这些了,但是如果要想写一个通用的库,这是应该考虑的。笔者原来很少考虑GNU C与ANSI C之间的区别,最近开了一个文章,感觉这些还是比较重要的,简要把GNU C中一些特别多罗列在下面。其中有几部分我在网上找到的参考文章,非常好,贴出链接。

     1、变量长度数组。

      在标准C中,我们都知道实例化一个数组的时候,其大小必须是常量。GNU C可以使用一个变量的数值来实例化一个数组。例如,下面的程序是正确的。

      int i=10;

      char aa[i];

      2、零长度数组。

      数组长度可以是0吗?很多人一听到认为当然不可以,这完全没有意义嘛。。GNU C中是可以的,但是其并不是表示这个数组的长度为0,这只是一种表现形式,其意义完全不是说这个数组的长度是0长度。例如:

 struct var_d{
 int len;
 char data[0];
 };

这种表现形式一般用在可变长度的BUFF上,你会发现sizeof(struct var_d)==sizeof(int),这就说明了data是不占任何空间的。其实data就是一个常量指针,指向指向用上述结构体实例化对象所占内存空间的下一个地址,其用法一般如下:

     struct var_data *thisline;
     thisline =(struct var_data *)malloc(sizeof(struct var_data)+10);
     thisline->len = 10;

此时data指向的空地址就是上述后面多分配出来的10个字节的首地址,这样的好处是,用于可变的buff,我们完全可以用data[i]来读出数据。仅仅凭几句话很难说清楚,我后来发现网上一篇文章写的非常好,把链接贴在下面,想深入学习的去看那个就好了,也省的我大段敲文字。


3、goto的使用

          标准C里面是不建议使用goto的,记得当年刚学C语言的时候,我们的C语言老师一再给我们强调千万不要用goto,搞的当时我们都以为这是个非常危险的东西,用了程序马上就会崩溃一样。即使至今,用不用goto仍饱受争议,但是如果搞过linux的驱动或内核的就应该知道,里面还是用了不少goto的,主要用于错误的处理上,将显得十分高效。例如:

if(register1()!=0)

       goto err1;

if(register2()!=0)

       goto err2;

if(register3()!=0)

       goto err3;

err3:

      unregister3();

err2:

      unregister2();

err1:

      unregister1();

linux经常会以这种方式使用。

4、do{}while(0)的使用

      do{}while(0)的使用可以说在这里并不能算是GNU特色吧,因为ANSI 也在用。有的人刚看到这,感觉这个不就执行了一次吗,直接写出里面的语句效果不是一样的嘛,搞成这样相对复杂来说不是一点意义都没有嘛,这个好处还是很大的,这个我详解,我找到一个文章应该说写的比我自己去写还要好,还要全面,把它读完,你就应该会发现这些程序设计的技巧所在,也不得不佩服当初使用的人的睿智。


5、标号元素

      标准C要求数组或结构体的初始化数值必须以固定的顺序出现.GNU C,可以通过指定索引或结构体的成员名来以任意的顺序进行初始化。

         数组的初始化是通过指定数组的索引来实现,在初始化一个数值前在前面添加索引"[INDEX]=",也可以使用“[FIRST...LAST]=”来指定一个范围。example:

       unsigned char da[LEN]={[0...LEN-1]=0}; 通过这种方式将数组全部初始化为0

       unsigned char da[LEN]={[3]=‘a’};             通过这种方式将da[3]个数初始化为a

        结构体的初始化,是用结构体的成员名来指定。example

      struct date{
          int year;
          int month;
          int day;
          char hour;
          char min;
          char sec;
        };
typedef struct date DATE;

下面是使用GNU C来实现初始化。

DATE dd={
 year:2003,
 month:11,
 day:28,
 hour:16,
 min:43,
 sec:0,
 };

标准C使用下列方法初始化

DATE cc={
 .year = 2012,
 .month= 11,
 .day  = 22,
 }; 

6、当前函数名

      GNU C预定义两个标识符保存当前的函数的名字,一个是__FUNCTION__保存函数在源代码中的名字,另一个是是__PRETTY_FUNCTION__保存带语言特色的名字。C函数中,这两个是相同的。目前C99也支持了__func__宏,用来保存函数的名字。example

void fun()

{

       printf("functionname:%s",__FUNCTION__);

       printf("functionname:%s",__func__);

}

7、特殊属性声明

     GNU C允许声明函数、变量和类型的特殊属性,以便进行手工的代码优化和定制代码检查方法。要指定一个声明的属性,主需要在声明后添加__attribute__((ATTRIBUTE))。其中ATTRIBUTE为属性声明,如果存在多个属性,则以逗号分开。GNU C支持noreturn、format、section、aligned、packed等十多个属性。

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

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

    unused用于变量或函数,当他们未被用到时,不会提示警告信息。

    aligned属性用于变量,结构体或联合,指定他们的对齐方式,以字节为单位。

     struct   aa{

     int a;

     char b;

     }__attributr((aligned(4)))__; 表示以四个字节进行对界

8、内建函数

    GNU C 提供了大量的内建函数,其中大部分是便准C库函数的GNU C编译器的内建版本。他们与对应的便准C库函数的作用相同。不属于库函数的内建函数的命名通常以__builtin开始。example

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

9、可变参数宏

   GNU C中支持可变参数的宏。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多