您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > C/C++/C#

GNU C和标准C的区别

时间:2021-11-08 10:33:54  来源:  作者:嵌入式软件开发

GNU C是什么?

GNU C建立在自由软件基金会的编程许可证的基础上,因此可以自由发布。GNU C对标准C进行进一步扩展,以增强标准C的功能。

特别是嵌入式软件开发过程中,经常会遇到很多GNU C的语法,以为是C语言没学过关,其实很多都不是标准C语言中的语法。哈哈!

 

扩展差异

1、零长度数组

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

struct minix_dir_entry { 
   __u16 inode; 
   char name[0];
};

结构的最后一个元素定义为零长度数组,它不占结构的空间。在标准 C 中则需要定义数组长度为 1,分配时计算对象大小比较复杂。

 

2、case范围
GNU C 允许在一个 case 标号中指定一个连续范围的值,例如:

case '0' ... '9': c -= '0'; break; 
case 'a' ... 'f': c -= 'a'-10; break; 
case 'A' ... 'F': c -= 'A'-10; break;

其中case '0' ... '9': 相当于 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':


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

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

复合语句的最后一个语句应该是一个表达式,它的值将成为这个语句表达式的值。这里定义了一个安全的求最小值的宏,在标准 C 中,通常定义为:

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

这个定义计算 x 和 y 分别两次,当参数有副作用时(比如出现参数自增或自减语句时),将产生不正确的结果,使用语句表达式只计算参数一次,避免了可能的错误。语句表达式通常用于宏定义。


4、typeof关键字
使用前一节定义的宏需要知道参数的类型,利用 typeof 可以定义更通用的宏,不必事先知道参数的类型,例如:

#define min(x,y) ({  
  const typeof(x) _x = (x);     
  const typeof(y) _y = (y);     
  (void) (&_x == &_y);           
_x < _y ? _x : _y; })

这里 typeof(x) 表示 x 的值类型, const typeof(x) _x = (x); 中定义了一个与 x 类型相同的局部变量 _x 并初使化为 x, (void) (&_x == &_y); 的作用是检查参数 x 和 y 的类型是否相同。typeof 可以用在任何类型可以使用的地方,通常用于宏定义。


5、可变参数的宏
在 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 不匹配任何参数的情况,这时 arg 的值为空,GNU C 预处理器在这种特殊情况下,丢弃 ## 之前的逗号,这样

pr_debug("success!n")

会被扩展为

printk("success!n")

而不是printk("success!n",)
注意最后的逗号。


6、标号元素
标准 C 要求数组或结构变量的初使化值必须以固定的顺序出现,在 GNU C 中,通过指定索引或结构域名,允许初始化值以任意顺序出现。指定数组索引的方法是在初始化值前写 '[INDEX] =',要指定一个范围使用 '[FIRST ... LAST] =' 的形式,例如:

static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL };

将数组的所有元素初使化为 ~0UL,这可以看做是一种简写形式。
要指定结构元素,在元素值前写 'FIELDNAME:',例如:

struct file_operations ext2_file_operations = { 
     llseek:       generic_file_llseek, 
     read:           generic_file_read, 
     write:       generic_file_write, 
     ioctl:       ext2_ioctl, 
     mmap:           generic_file_mmap, 
     open:           generic_file_open, 
     release:        ext2_release_file, 
     fsync:       ext2_sync_file, 
};

将结构 ext2_file_operations 的元素 llseek 初始化为 generic_file_llseek,元素 read 初始化为 genenric_file_read,依次类推。我觉得这是 GNU C 扩展中最好的特性之一,当结构的定义变化以至元素的偏移改变时,这种初始化方法仍然保证已知元素的正确性。对于未出现在初始化中的元素,其初值为 0。


7、当前函数名
GNU CC 预定义了两个标志符保存当前函数的名字,__FUNCTION__ 保存函数在源码中的名字,__PRETTY_FUNCTION__ 保存带语言特色的名字。在 C 函数中,这两个名字是相同的,在 C++ 函数中,__PRETTY_FUNCTION__ 包括函数返回类型等额外信息,linux 内核只使用了 __FUNCTION__。

void example()
{
  printf{“This is function:%s”, __FUNCTION__};
}

代码中__FUNCTION__意味着字符串“example”。


8、特殊属性声明
GNU C 允许声明函数、变量和类型的特殊属性,以便手工的代码优化和更仔细的代码检查。要指定一个声明的属性,在声明后写 __attribute__ (( ATTRIBUTE ))其中 ATTRIBUTE 是属性说明,多个属性以逗号分隔。GNU C 支持十几个属性,这里介绍最常用的:

noreturn
属性 noreturn 用于函数,表示该函数从不返回。这可以让编译器生成稍微优化的代码,最重要的是可以消除不必要的警告信息比如未初使化的变量。例如:

# define ATTRIB_NORET   __attribute__((noreturn)).... 
asmlinkage NORET_TYPE void do_exit(long error_code) ATTRIB_NORET;

format
属性 format 用于函数,表示该函数使用 printf, scanf 或 strftime 风格的参数,使用这类函数最容易犯的错误是格式串与参数不匹配,指定 format 属性可以让编译器根据格式串检查参数类型。例如:

asmlinkage int printk(const char * fmt, ...)    __attribute__ ((format (printf, 1, 2)));

表示第一个参数是格式串,从第二个参数起根据格式串检查参数。
unused
属性 unused 用于函数和变量,表示该函数或变量可能不使用,这个属性可以避免编译器产生警告信息。
aligned
属性 aligned 用于变量、结构或联合类型,指定变量、结构域、结构或联合的对齐量,以字节为单位,例如:

struct example_struct
{
 char a;
 int   b;
 long c;
} __attribute__((aligned(4)));

表示该结构类型的变量以4字节对界。
packed
属性 packed 用于变量和类型,用于变量或结构域时表示使用最小可能的对齐,用 于枚举、结构或联合类型时表示该类型使用最小的内存。例如:

struct example_struct
{
   char a;
   int b__attribute__ ((packed));
   long c__attribute__((packed));
};

对于结构体example_struct而言,在i386平台下,其sizeof的结果为9,如果删除其中的2个—attribute__((packed)),其sizeof将为12.


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


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


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


__builtin_expect(EXP, C)
内建函数 __builtin_expect 用于为编译器提供分支预测信息,其返回值是整数表
达式 EXP 的值,C 的值必须是编译时常数。
例如,下面的代码检测第一个参数是否为编译时常数以确定采用参数版本还是非参数版本代码:

#define test_bit(nr,addr)  
(__builtin_constant_p(nr) ?  
constant_test_bit((nr),(addr)) : 
  variable_test_bit((nr),(addr)))


Tags:GNU C   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
GNU C是什么?GNU C建立在自由软件基金会的编程许可证的基础上,因此可以自由发布。GNU C对标准C进行进一步扩展,以增强标准C的功能。特别是嵌入式软件开发过程中,经常会遇到很多...【详细内容】
2021-11-08  Tags: GNU C  点击:(37)  评论:(0)  加入收藏
▌简易百科推荐
一、简介很多时候我们都需要用到一些验证的方法,有时候需要用正则表达式校验数据时,往往需要到网上找很久,结果找到的还不是很符合自己想要的。所以我把自己整理的校验帮助类分...【详细内容】
2021-12-27  中年农码工    Tags:C#   点击:(0)  评论:(0)  加入收藏
引言在学习C语言或者其他编程语言的时候,我们编写的一个程序代码,基本都是在屏幕上打印出 hello world ,开始步入编程世(深)界(坑)的。C 语言版本的 hello world 代码:#include <std...【详细内容】
2021-12-21  一起学嵌入式    Tags:C 语言   点击:(10)  评论:(0)  加入收藏
读取SQLite数据库,就是读取一个路径\\192.168.100.**\position\db.sqlite下的文件<startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0"/...【详细内容】
2021-12-16  今朝我的奋斗    Tags:c#   点击:(21)  评论:(0)  加入收藏
什么是shellshell是c语言编写的程序,它在用户和操作系统之间架起了一座桥梁,用户可以通过这个桥梁访问操作系统内核服务。 它既是一种命令语言,同时也是一种程序设计语言,你可以...【详细内容】
2021-12-16  梦回故里归来    Tags:shell脚本   点击:(16)  评论:(0)  加入收藏
一、编程语言1.根据熟悉的语言,谈谈两种语言的区别?主要浅谈下C/C++和PHP语言的区别:1)PHP弱类型语言,一种脚本语言,对数据的类型不要求过多,较多的应用于Web应用开发,现在好多互...【详细内容】
2021-12-15  linux上的码农    Tags:c/c++   点击:(17)  评论:(0)  加入收藏
1.字符串数组+初始化char s1[]="array"; //字符数组char s2[6]="array"; //数组长度=字符串长度+1,因为字符串末尾会自动添&lsquo;\0&lsquo;printf("%s,%c\n",s1,s2[2]);...【详细内容】
2021-12-08  灯-灯灯    Tags:C语言   点击:(46)  评论:(0)  加入收藏
函数调用约定(Calling Convention),是一个重要的基础概念,用来规定调用者和被调用者是如何传递参数的,既调用者如何将参数按照什么样的规范传递给被调用者。在参数传递中,有两个很...【详细内容】
2021-11-30  小智雅汇    Tags:函数   点击:(19)  评论:(0)  加入收藏
一、问题提出问题:把m个苹果放入n个盘子中,允许有的盘子为空,共有多少种方法?注:5,1,1和1 5 1属同一种方法m,n均小于10二、算法分析设f(m,n) 为m个苹果,n个盘子的放法数目,则先对...【详细内容】
2021-11-17  C语言编程    Tags:C语言   点击:(46)  评论:(0)  加入收藏
一、为什么需要使用内存池在C/C++中我们通常使用malloc,free或new,delete来动态分配内存。一方面,因为这些函数涉及到了系统调用,所以频繁的调用必然会导致程序性能的损耗;另一...【详细内容】
2021-11-17  深度Linux    Tags:C++   点击:(37)  评论:(0)  加入收藏
OpenCV(Open Source Computer Vision Library)是一个(开源免费)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android、ios等操作系统上,它轻量级而且高效---由一系列...【详细内容】
2021-11-11  zls315    Tags:C#   点击:(50)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条