两种C语言之间的差异

一起学嵌入式 2024-08-07 16:45

扫描关注一起学嵌入式,一起学习,一起成长


对于嵌入式软件工程师来说,C 语言可以说是最最最主要的编程语言,然而,Linux GNU C 和 ANSI C 这两者之间,却存在着一定的差异,一旦使用不当,很容易造成语法错误。

Linux 上可用的 C 编译器是 GNU C 编译器,它建立在自由软件基金会的编程许可证的基础上,因此可以自由发布。

GNU C对标准C进行一系列扩展,以增强标准C的功能。

1.零长度和变量长度数组

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

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

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

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

struct var_data s; 
... 
for (i = 0; i < s.len; i++) 
    printf("%02x", s.data[i]);

GNU C中也可以使用1个变量定义数组,例如如下代码中定义的“double x[n]”:

int main (int argc, char *argv[]) 

    int i, n = argc; 
    double x[n]; 
    for (i = 0; i < n; i++) 
        x[i] = i; 
    return 0
}

2.case范围

GNU C支持case x…y这样的语法,区间[x,y]中的数都会满足这个case的条件,请看下面的代码:

switch (ch) { 
case '0'... '9': c -= '0'
    break;
case 'a'... 'f': c -= 'a' - 10
    break
case 'A'... 'F': c -= 'A' - 10
    break
}

代码中的case'0'...'9'等价于标准C中的:

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; }) 
int ia, ib, mini; 
float fa, fb, minf; 
mini = min_t(int, ia, ib); 
minf = min_t(float, fa, fb);

因为重新定义了__xx和__y这两个局部变量,所以用上述方式定义的宏将不会有副作用。在标准C中,对应的如 下宏则会产生副作用:

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

代码min(++ia,++ib)会展开为((++ia)<(++ib)(++ia):(++ib)),传入宏的“参数”增加两次。

4.typeof关键字

typeof(x)语句可以获得x的类型,因此,可以借助typeof重新定义min这个宏:

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

我们不需要像min_t(type,x,y)那个宏那样把type传入,因为通过typeof(x)、typeof(y)可以获得type。代 码行(void)(&_x==&_y)的作用是检查_x和_y的类型是否一致。


5.可变参数宏

标准C就支持可变参数函数,意味着函数的参数是不固定的,例如printf()函数的原型为:

int printfconst 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 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, 
};

但是,Linux 2.6推荐类似的代码应该尽量采用标准C的方式

struct file_operations ext2_file_operations = { 
    .llseek     = generic_file_llseek, 
    .read       = generic_file_read, 
    .write      = generic_file_write, 
    .aio_read   = generic_file_aio_read, 
    .aio_write  = generic_file_aio_write, 
    .ioct       = ext2_ioctl, 
    .mmap       = generic_file_mmap, 
    .open       = generic_file_open, 
    .release    = ext2_release_file, 
    .fsync      = ext2_sync_file, 
    .readv      = generic_file_readv, 
    .writev     = generic_file_writev, 
    .sendfile   = generic_file_sendfile, 
};

7.当前函数名

GNU C预定义了两个标识符保存当前函数的名字,__FUNCTION__保存函数在源码中的名字,__PRETTY_FUNCTION__保存带语言特色的名字。在C函数中,这两个名字是相同的。

void example() 

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

代码中的__FUNCTION__意味着字符串“example”。C99已经支持__func__宏,因此建议在Linux编程中不再使用__FUNCTION__,而转而使用__func__:

void example(void) 

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

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 (printf12)));

上述代码中的第1个参数是格式串,从第2个参数开始都会根据printf()函数的格式串规则检查参数。

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

aligned属性用于变量、结构体或联合体,指定变量、结构体或联合体的对齐方式,以字节为单位,例如:

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

表示该结构类型的变量以4字节对齐。

packed属性作用于变量和类型,用于变量或结构体成员时表示使用最小可能的对齐,用于枚举、结构体或联合体类型时表示该类型使用最小的内存。例如:

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

编译器对结构体成员及变量对齐的目的是为了更快地访问结构体成员及变量占据的内存。例如,对 于一个32位的整型变量,若以4字节方式存放(即低两位地址为00),则CPU在一个总线周期内就可以读取32 位;否则,CPU需要两个总线周期才能读取32位。

9.内建函数

GNU C提供了大量内建函数,其中大部分是标准C库函数的GNU C编译器内建版本,例如memcpy()等,它们与对应的标准C库函数功能相同。

不属于库函数的其他内建函数的命名通常以__builtin开始,如下所示。

内建函数__builtin_return_address(LEVEL)返回当前函数或其调用者的返回地址,参数LEVEL指定调用栈的级数,如0表示当前函数的返回地址,1表示当前函数的调用者的返回地址。

内建函数__builtin_constant_p(EXP)用于判断一个值是否为编译时常数,如果参数EXP的值是常数,函数返回1,否则返回0。例如,下面的代码可检测第1个参数是否为编译时常数以确定采用参数版本还是非参数版本:

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

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

Linux内核编程时常用的likely()和unlikely()底层调用的likely_notrace()、unlikely_notrace()就是基于 __builtin_expect(EXP,C)实现的。

#define likely_notrace(x) __builtin_expect(!!(x), 1) 
#define unlikely_notrace(x) __builtin_expect(!!(x), 0)

若代码中出现分支,则即可能中断流水线,我们可以通过likely()和unlikely()暗示分支容易成立还是不容易 成立,例如:

if (likely(!IN_DEV_ROUTE_LOCALNET(in_dev)))
    if (ipv4_is_loopback(saddr)) 
    goto e_inval;

在使用gcc编译C程序的时候,如果使用“-ansi–pedantic”编译选项,则会告诉编译器不使用GNU扩展语法。例如对 于如下C程序test.c:

struct var_data { 
    int len; 
    char data[0]; 
};
struct var_data a;

直接编译可以通过:

gcc -c test.c

如果使用“-ansi–pedantic”编译选项,编译会报警:

gcc -ansi -pedantic -c test.c 
test.c:3: warning: ISO C forbids zero-size array 'data'



关注【一起学嵌入式】,回复加群进技术交流群。




觉得文章不错,点击“分享”、“”、“在看” 呗!

一起学嵌入式 公众号【一起学嵌入式】,RTOS、Linux编程、C/C++,以及经验分享、行业资讯、物联网等技术知
评论 (0)
  • 最近,巴黎奥运会将全球人民的心捆绑在一起,奥林匹克精神的魅力也让低沉已久的大众情绪再度昂扬。在这样一场场顶尖运动员大展身手的体育盛事中,你可能会好奇,运动员们如何在激烈的比赛中保持最佳状态呢?特别是对于耐力运动员?这就不得不提到一个关键因素——体温管理。比如游泳。当地时间8月4日,在巴黎奥运会男子4×100米混合泳接力决赛中,由徐嘉余、覃海洋、孙佳俊、潘展乐组成的中国队夺得金牌。这是中国游泳队首次获得该项目的奥运金牌,更是打破了美国队在该项目奥运会历史上长达40年的金牌垄断。在全民感慨游泳健儿拼
    艾迈斯欧司朗 2024-09-14 17:35 184浏览
  •  电机转速传感器是工业自动化和控制系统中的重要元件,它们能够实时、准确地测量电机或其他旋转设备的转速,确保系统运行的稳定性和可靠性。随着技术的发展,电机转速传感器的种类和类型日益丰富,以满足不同应用场合的需求。 电机转速传感器是用于测量电机转速的设备,广泛应用于工业自动化、车辆、家电等领域。根据工作原理和应用场景,电机转速传感器主要可以分为以下几种类型: 1、光电传感器: (1)光电编码器:通过光学原理测量转速,通常由一个旋转的编码盘和光源组成。编码盘上有透明和不透明的区域,光源通过编码盘产生脉
    博扬智能 2024-09-14 17:26 198浏览
  •     近日看到《对人类意识的重视回归网络安全视野》一文,来自于IEEE电气电子工程师学会IEEE Transmitte 8月7日的文章。看完后颇有些感想。作为一名工程人员做了那么多工程,维护了那么多的设备和系统,我对非法黑客是深恶痛绝,同时也深深感到网络安全的重要性。试想一下,一个完整的医疗设备开发耗资巨大,大量人力,消耗时间数年,还要做大量测试和验证,能为人类健康带来福音。无奈道高一尺魔高一丈,非法的黑客组织硬生生将救人机器变成了害人的工具(国内外既有案例)。他们不需要耗
    广州铁金刚 2024-09-14 11:53 115浏览
  • 蓝牙模块是一种集成了蓝牙功能的 PCBA 板,主要用于短距离无线通讯。依据功能的不同,它被分为蓝牙数据模块和蓝牙语音模块等。实际上,蓝牙模块指的是集成蓝牙功能的芯片基本电路集合,用于无线网络通讯,大致能划分为数据传输模块、蓝牙音频模块、蓝牙音频与数据二合一模块等类别。通常来说,模块具有半成品的特点,是在芯片基础上进行加工处理,旨在让后续的应用变得更为简便。作为替代数据电缆的短距离无线通信技术,蓝牙支持点对点以及点对多点的通信方式,能够以无线形式将家庭或办公室里的各种数据和语音设备连接成一个微微网
    压电侠piezoman 2024-09-14 11:46 79浏览
  •  电容位移传感器作为一种重要的精密测量工具,在科研、教学和生产中发挥着不可替代的作用。通过了解不同类型的电容位移传感器及其特点和应用领域,可以更好地选择和使用这些传感器,为科技进步和工业发展贡献力量! 一、平行板电容传感器 平行板电容传感器是最常见的电容位移传感器类型之一,它由两个平行的电极板构成,当被测物体发生位移时,两个电极板之间的距离会发生变化,从而导致电容值的变化。通过测量这种电容值的变化,我们可以准确地得到物体的位移量。结构简单、易于制造,广泛应用于各种需要精确测量的场合。 二、圆柱电
    博扬智能 2024-09-14 11:55 70浏览
  •  电机转速传感器是一种将电机旋转速度转换为可测量电信号的装置,它不仅是电机控制系统中的重要组成部分,更是实现设备精准控制、提高生产效率的关键。无论是风力发电、汽车制造还是工业自动化领域,电机转速传感器都发挥着不可替代的作用。 一、揭秘电机转速传感器的工作原理电机转速传感器的工作原理多种多样,但核心思想都是通过感知电机的旋转运动,并将其转换为电信号输出。以下介绍几种常见的转速传感器类型及其工作原理: (1)磁电式转速传感器 磁电式转速传感器以其结构简单、抗干扰性强而广受青睐。它主要由铁芯、磁钢和感
    博扬智能 2024-09-14 15:59 147浏览
  • 2024年9月14日 调研咨询机构环洋市场咨询出版的《全球特厚止裂钢板行业总体规模、主要厂商及IPO上市调研报告,2024-2030》只要调研全球特厚止裂钢板总体规模,主要地区规模,主要企业规模和份额,主要产品分类规模,下游主要应用规模以及未来发展前景预测。统计维度包括销量、价格、收入,和市场份额。同时也重点分析全球市场主要厂商(品牌)产品特点、产品规格、价格、销量、销售收入及发展动态。历史数据为2019至2023年,预测数据为2024至2030。 调研机构:Global Info Resea
    GIRtina 2024-09-14 11:57 119浏览
  • 在信息安全的诸多领域之中,密码的安全存储无疑已然成为最为核心的问题之一。随着攻击技术的不断演进,传统的密码存储方法已无法抵御现代复杂的威胁。更为安全、健壮的密码存储机制也成为当代信息安全从业者的关注点。本篇文章将引入并介绍密码存储中的基石,关于密码哈希、盐加密(Salting)、密钥派生函数(KDF)的原理及其应用,揭示密码存储中的常见误区,并分享一系列安全实践。一、为什么不能使用明文存储密码直接将密码以明文存储在数据库中无异于为攻击者打开了方便之门。一旦数据库遭遇泄露,攻击者将轻而易举地获得所
    艾体宝IT 2024-09-14 17:40 183浏览
  • 2024年9月14日  Global Info Research行业调研机构发布的《全球人工智能实验室行业总体规模、主要厂商及IPO上市调研报告,2024-2030》分析了全球人工智能实验室总体规模,主要地区规模,主要企业规模和份额,主要产品分类规模,下游主要应用规模等。统计维度包括收入和市场份额等。不仅全面分析全球范围内主要企业竞争态势,收入和市场份额等。同时也重点分析全球市场主要厂商(品牌)产品特点、产品规格、收入、毛利率及市场份额、及发展动态。历史数据为2019至2023年,预测
    GIRtina 2024-09-14 13:41 158浏览
  • 2024-9-14调研咨询机构环洋市场咨询出版的【全球氢气循环泵和引射器行业总体规模、主要厂商及IPO上市调研报告,2024-2030】只要调研全球氢气循环泵和引射器总体规模,主要地区规模,主要企业规模和份额,主要产品分类规模,下游主要应用规模以及未来发展前景预测。统计维度包括销量、价格、收入,和市场份额。同时也重点分析全球市场主要厂商(品牌)产品特点、产品规格、价格、销量、销售收入及发展动态。历史数据为2019至2023年,预测数据为2024至2030年。 调研机构:Global Info
    GIRtina 2024-09-14 11:48 71浏览
  • 采用快速紧凑的 Raspberry Pi 计算模块3(Raspberry Pi Compute Module 3)的简易设置,为Korg备受推崇的高端乐器提供了一种经济高效的解决方案。解决方案:Compute Module 3企业规模:大型企业行业:音乐技术标志性的乐器制造商Korg起源于20世纪60年代的日本,最初将鼓机推向市场,随后是日本第一台合成器和世界上第一台针式调音器。随着20世纪70年代和80年代电子音乐的兴起,Korg在国际上声名鹊起。随着对功能更强大且更实惠的合成器的需求不断增长
    树莓派开发者 2024-09-14 14:54 141浏览
  • 作者介绍 一、Skydel与NI USRP软件定义架构(Software Defined Architecture)是一种新型的架构模式,它是一种基于软件的架构,通过软件来定义系统的功能,从而提高系统的运行效率和能量效率。软件定义架构可以将硬件资源抽象化,从而使得应用程序可以更好地利用硬件资源,提高系统的性能和可靠性。Skydel GNSS仿真引擎是创新型的软件定义GNSS模拟仿真引擎,它利用PC的GPU的计算能力并借助Nvidia GPU极快的数学计算能力处理星座、频率与轨迹信息,并
    虹科测试测量TM 2024-09-14 15:33 134浏览
  •  电容位移传感器将继续向更高精度、更高灵敏度、更小型化的方向发展。随着物联网、大数据等技术的不断普及,电容位移传感器将与更多领域相结合,为人类创造更加美好的未来。 一、电容位移传感器的构成 电容位移传感器通常由两个平行极板构成,其中一个极板固定不动,另一个极板与被测物体相连,随被测物体的位移而移动。当被测物体发生位移时,两个极板之间的距离会发生变化,从而导致电容值的变化。通过测量这一变化,就可以准确地知道被测物体的位移量。 二、电容位移传感器的优势 1.高精度:具有极高的测量精度,能够满足各种高
    博扬智能 2024-09-14 15:35 132浏览
  • 随着物联网(IoT)、工业自动化、医疗设备等领域对嵌入式系统的需求不断增加。嵌入式核心板(SOM)作为嵌入式系统的核心组件,其市场需求也随之增长。在快速发展的同时,也面临一定的挑战:如进口芯片供应链不可控、单一平台受地域政策限制、多平台产品开发周期长、开发难度高等问题,米尔电子设计开发了纯FPGA 开发平台,支持一款平台,双芯设计,支持同款底板可换国产和进口芯片,推出MYIR 7A100T和PG2L100H核心板,解决客户对国内国际市场的不同需求。国产开发维护与进口主流出货痛点痛点一 政策受限,
    米尔电子嵌入式 2024-09-14 14:56 109浏览
  • 2024年9月14日,调研咨询机构环洋市场咨询出版的《全球D级全动飞行模拟机行业总体规模、主要厂商及IPO上市调研报告,2024-2030》,主要调研全球D级全动飞行模拟机总体规模,主要地区规模,主要企业规模和份额,主要产品分类规模,下游主要应用规模以及未来发展前景预测。统计维度包括销量、价格、收入,和市场份额。同时也重点分析全球市场主要厂商(品牌)产品特点、产品规格、价格、销量、销售收入及发展动态。历史数据为2019至2023年,预测数据为2024至2030年。 调研机构:Global In
    GIRtina 2024-09-14 11:59 123浏览
我要评论
0
0