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]来读出数据。仅仅凭几句话很难说清楚,我后来发现网上一篇文章写的非常好,把链接贴在下面,想深入学习的去看那个就好了,也省的我大段敲文字。
链接:http://blog.csdn.net/ssdsafsdsd/article/details/8234736
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 也在用。有的人刚看到这,感觉这个不就执行了一次吗,直接写出里面的语句效果不是一样的嘛,搞成这样相对复杂来说不是一点意义都没有嘛,这个好处还是很大的,这个我详解,我找到一个文章应该说写的比我自己去写还要好,还要全面,把它读完,你就应该会发现这些程序设计的技巧所在,也不得不佩服当初使用的人的睿智。
链接:http://blog.csdn.net/ssdsafsdsd/article/details/8235400
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中支持可变参数的宏。
待续。。。。。