0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

C语言语法扩展

汽车电子技术 来源:宅学部落 作者: 王利涛 2023-02-17 09:34 次阅读

Linux 内核驱动中的奇怪语法

大家在看一些 GNU 开源软件,或者阅读 Linux 内核、驱动源码时会发现,在 Linux 内核源码中,有大量的 C 程序看起来“怪怪的”。说它是C语言吧,貌似又跟教材中的写法不太一样;说它不是 C 语言呢,但是这些程序确确实实是在一个 C 文件中。此时,你肯定怀疑你看到的是一个“假的 C 语言”!

比如,下面的宏定义:

#define mult_frac(x, numer, denom)({\\
typeof(x) quot = (x) / (denom);\\        
typeof(x) rem  = (x) % (denom);\\
(quot * (numer)) + ((rem * (numer)) / (denom));\\
})
#define ftrace_vprintk(fmt, vargs)\\
do {\\    
if (__builtin_constant_p(fmt)) {\\        
static const char *trace_printk_fmt __used \\
  __attribute__((section("__trace_printk_fmt"))) = \\
  __builtin_constant_p(fmt) ? fmt : NULL; \\
  __ftrace_vbprintk(_THIS_IP_, trace_printk_fmt, vargs);\\
  } 
else \\
  __ftrace_vprintk(_THIS_IP_, fmt, vargs);\\
} while (0)

字符驱动的填充:

static const struct 
file_operations 
lowpan_control_fops = {
.open =lowpan_control_open,
.read = seq_read,
.write=lowpan_control_write,
 .llseek = seq_lseek,
 .release = single_release,
 };

内核中实现打印功能的宏定义:

#define pr_info(fmt, ...)\\
__pr(__pr_info, fmt, ##__VA_ARGS__)

#define pr_debug(fmt, ...)\\
__pr(__pr_debug, fmt, ##__VA_ARGS__)

你没有看错,这些其实也是 C 语言,但并不是标准的 C 语言语法,而是我们 Linux 内核使用的 GNU C 编译器扩展的一些 C 语言语法。这些语法在 C 语言教材或资料中一般不会提及,所以你才会似曾相识而又感到陌生,看起来感觉“怪怪的”。我们在做 Linux 驱动开发,或者阅读 Linux 内核源码过程中,会经常遇到这些“稀奇古怪”的用法,如果不去了解这些特殊语法的具体含义,可能就对代码的理解造成一定障碍。

本文和接下来的几篇文章,将带领大家一起去了解 Linux 内核或者 GNU 开源软件中,常用的一些 C 语言特殊语法扩展,扫除阅读 Linux 内核或 GNU 开源软件时,这些扩展特性带给我们的语法阅读障碍和困惑。

C 语言标准和编译器

在进入正式学习之前,先给大家普及一下 C 标准的概念。在学习 C 语言时,大家在教材或资料上,或多或少可能见到过“ANSI C”的字眼。可能当时没有太在意,其实“ANSI C” 表示的就是 C 语言标准。

什么是 C 语言标准呢?我们生活的现实世界,就是由各种标准构成的,正是这些标准,我们的社会才会有条不紊的运行。比如我们过马路,遵循的交通规则就是一个标准:红灯停,绿灯行,黄灯亮了等一等。当行人和司机都遵循这个默认的标准时,我们的交通系统才会顺畅运行。电脑中的 USB 接口也是一种标准,当大家生产的 USB 产品都遵循 USB 协议这种通信标准时,我们的手机、U 盘、USB 摄像头、USB 网卡才可以在各种电脑设备上互插互拔。2G、3G、4G 也是一种标准,当不同厂家生产的基带芯片都遵循这种通信标准,我们所用的不同品牌、不同操作系统的手机才可能互相打电话、互相发微信、互相给对方点赞。

同样,C 语言也有它自己的标准。我们知道,C 语言程序需要通过编译器,编译生成二进制指令,才能在我们的电脑上运行。在 C 语言刚发布的早期,各大编译器厂商开发自己的编译器时,各自开发,各自维护,时间久了,就会变得比较混乱。这就会造成这样一种局面:程序员写的程序,在一个编译器上编译通过,在另一个编译器编译通不过。大家按各自的习惯来,谁也不服谁,就像春秋战国时代:不同的货币、不同的度量衡,不同的文字,都是中国人,因为标准不统一,所以交流起来很麻烦,这样下去也不是办法啊。

后来 ANSI(AMERICAN NATIONAL STANDARDS INSTITUTE: 美国国家标准协会,简称 ANSI)出山了,联合 ISO(国际化标准组织)召集各个编译器厂商大佬,各种技术团体,一起喝个茶、开个碰头会,开始启动 C 语言的标准化工作。期间各种大佬之间也是矛盾重重,充满各种争议,但功夫不负有心人,经过艰难的磋商,终于在1989年达成一致,发布了 C 语言标准,后来第二年又做了一些改进。于是,就像秦始皇统一六国、统一文字和度量衡一样,C 语言标准终于问世了!因为是在 1989 年发布的,所以人们一般称其为 C89 或 C90 标准,或者叫做 ANSI C。

C 标准内容

C 标准里主要讲了什么?

C 标准英文文档,洋洋洒洒几百页,讲了很多东西,但总体归纳起来,主要就是 C 语言编程的一些语法惯例,比如:

  • 定义各种关键字、数据类型
  • 定义各种运算规则
  • 各种运算符的优先级和结合性
  • 数据类型转换
  • 变量的作用域
  • 函数原型
  • 函数嵌套层数
  • 函数参数个数限制
  • 标准库函数

C 标准发布后,大家都遵守这个标准:程序员开发程序时,按照这种标准写;编译器厂商开发编译器时,也按照这种标准去解析、翻译程序。不同的编译器厂商支持统一的标准,这样大家写的程序,使用不同的编译器,都可以正确编译、运行,大大提高程序的开发效率,推动了 IT 行业的发展。

C 标准的发展过程

C 标准并不是永远不变的,就跟移动通信一样,也是从 2G、3G、4G 到 5G 不断发展变化的。C 标准也经历了下面四个阶段:

  • K&R C
  • ANSI C
  • C99
  • C11

K&R C

K&R C 一般也称为传统 C。在 C 标准没有统一之前,C 语言的作者 Dennis Ritchie 和 Brian Kernighan 合作写了一本书《C 程序设计语言》。早期程序员编程,这本书可以说是绝对权威。这本书很薄,内容精炼,主要介绍了 C 语言的基本使用方法。后来《C 程序设计语言》第二版问世,做了一些修改:比如新增 unsigned int、long int、struct 等数据类型;把运算符 =+/=- 修改为 +=/-=,避免运算符带来的一些歧义和 Bug。这本书可以看作是 ANSI 标准的雏形。但早期的 C 语言还是很简单的,比如还没有定义标准库函数、没有预处理命令等。

ANSI C

ANSI C 是 ANSI(美国国家标准协会)在 K&R C 的基础上,统一了各大编译器厂商的不同标准,并对 C 语言语法和特性做了一些扩展,而发布的一个标准。这个标准一般也叫做 C89/C90,也是目前各种编译器默认支持的 C 语言标准。ANSI C 主要新增了以下特性:

  • 增加 signed、volatile、const 关键字
  • 增加 void* 数据类型
  • 增加预处理器命令
  • 增加宽字符、宽字符串
  • 定义了 C 标准库
  • ……

C99 标准

C99 标准是 ANSI 1999 年在 C89 标准的基础上新发布的一个标准,该标准对 ANSI C 标准做了一些扩充,比如新增一些关键字,支持新的数据类型:

  • 布尔型:_Bool
  • 复数:_Complex
  • 虚数:_Imaginary
  • 内联:inline
  • 指针修饰符:restrict
  • 支持long long、long double数据类型
  • 支持变长数组
  • 允许对结构体特定成员赋值
  • 支持16进制浮点数、float _Complex等数据类型
  • ……

除此之外,C99 标准也借鉴其它语言的一些优点,对语法和函数做了一系列改进,大大方便了程序员开发程序,比如:

  • 变量声明可以放代码块的任何地方。ANSI C 规定变量的声明要全部写在函数语句的最前面,否则就会报编译错误。现在不需要这样写了,哪里需要使用变量,在哪里直接声明使用即可;
  • 源程序每行最大支持4095个字节。这个貌似足够用了,没有什么程序能复杂到一行程序有4KB个字符;
  • 支持//单行注释。ANSI C使用/**/没有C++的//注释方便,所以 C99 新标准借鉴过来了,也开始支持这种注释方式;
  • 标准库新增了一些头文件:如 stdbool.h、complex.h、stdarg.h、fenv.h 等。大家在 C 语言中经常返回的 true、false,其实这也是 C++ 里面定义的 bool 类型。那为什么我们经常这样写,而编器编译程序时没有报错呢,这是因为早期大家编程使用的都是 VC++6.0 系列,是 C++ 编译器。还有一种可能就是有些 IDE 对这个数据类型的数据做了封装。

C11 新标准

C11 标准是2011年发布的最新 C 语言标准,修改了 C 语言标准的一些 Bug、新增了一些特性:

  • 增加 _Noreturn,声明函数无返回值;
  • 增加_Generic:支持泛型编程;
  • 修改了标准库函数的一些 Bug:如 gets( )函数被 gets_s() 函数代替;
  • 新增文件锁功能;
  • 支持多线程;
  • ……

从 C11 标准的修改内容来看,也慢慢察觉到 C 语言未来的发展趋势:C 语言现在也在借鉴现在编程语言的优点,不断添加到自己的标准里面。比如现代编程语言的多线程、字符串、泛型编程等,C 语言最新的标准都支持。但是这样下去,C 语言是不是还能保持她“简单就是美”的优雅特色呢,我们只能慢慢期待了。但至少目前我们不用担心这些,因为 C11 新发布的标准,目前绝大多数编译器还不支持,所以我们暂时还用不到。

编译器对 C 标准的支持

标准是一回事,各种编译器支不支持是另一回事,这一点,大家要搞清楚。这就跟手机一样,不同时期发布的手机对通信标准支持也不一样。早期的手机可能只支持 2G 通信,后来支持 3G,现在发布的新款手机基本上都支持 4G了,而且可以兼容 2G/3G。

现在 5G 标准正在研发,快发布了,据说 2019 年发布,2020 年商用。但是目前还没有手机支持 5G 通信,就跟现在没有编译器支持 C11 标准一样。

不同编译器,甚至对 C 标准的支持也不一样。有的编译器只支持 ANSI C,这是目前默认的 C 标准。有的编译器可以支持 C99,或者支持 C99 标准的部分特性。目前对 C99 标准支持最好的是 GNU C 编译器,据说可以支持 C99标准99%的新增特性。

编译器对 C 标准的扩展

不同编译器,出于开发环境、硬件平台、性能优化的需要,除了支持 C 标准外,还会自己做一些扩展。

51单片机上用 C 语言开发程序,我们经常使用 Keil for C51 集成开发环境。你会发现 Keil for C51 或其他 IDE 里的 C 编译器会对 C 语言标准作很多扩展。比如增加各种关键字:

  • data:RAM 的低128B空间,单周期直接寻址;
  • code:表示程序存储区;
  • bit:位变量,常用来定义单片机的 P0~P3 管脚;
  • sbit:特殊功能位变量;
  • sfr:特殊功能寄存器;
  • reentrant:重入函数声明。

如果你在程序中使用以上这些关键字,那么你的程序就只能使用51编译器来编译运行,你使用其它的编译器,比如 VC++6.0,是编译通不过的。

同样的道理,GCC 编译器,也对 C 标准做了很多扩展:

  • 零长度数组
  • 语句表达式
  • 内建函数
  • __attribute__特殊属性声明
  • 标号元素
  • case 范围
  • ...

比如支持零长度数组。这些新增的特性,C 标准目前是不支持的,其它编译器也不支持。如果你在程序中定义一个零长度数组:

int a[0];

只能使用 GCC 编译器才能正确编译,使用 VC++ 6.0编译器编译可能就通不过,因为微软的 C++ 编译器不支持这个特性。

教程主要内容

本文是《C语言嵌入式Linux高级编程》第5期:Linux内核中GNU C语法扩展视频教程的文本预览版,如果想系统学习一下C语言标准及Linux内核中的GNU C扩展语法,可以到51CTO学院或CSDN学院搜索讲师名字:“王利涛”,即可观看相关教程。或者在百度中,直接搜索“王利涛”也可以找到相关视频教程。

在 GNU 开源软件、Linux 内核中会大量使用 GCC 自己扩展的语法,这会对我们理解开源软件、Linux 内核代码带来一定障碍和困扰。本教程主要介绍 GNU C 对 C 标准扩展的一些常用语法和使用。终极目标是看懂 Linux 内核驱动、GNU 开源软件中这些特殊语法的应用,扫除这些特殊语法对我们理解内核代码带来的困扰和障碍。

本教程需要的学习环境

在本教程讲解中,会使用一些 arm-linux-gnueabi-gcc 等命令用来编译和反汇编程序。所以在学习本教程之前,确保你的电脑上有如下 Linux 环境或源代码:

  • Linux学习环境:Ubuntu、Fedora等皆可;
  • arm-linux-gnueabi-gcc 交叉编译工具;
  • Linux 内核源码:Linux 4.4.x
  • U-boot-2016.09 源代码
备注

本教程是《C语言嵌入式Linux高级编程》第5期:Linux内核中的GNU C语法扩展,文本预览版,如果想系统学习Linux内核中的各种GNU C扩展及使用技巧,可百度搜索:“王利涛”,到51CTO学院或CSDN学院点击相关课程即可开始系统学习。

如果您手头暂时没有 Linux 学习环境,也可以在 Windows 环境下安装 C-Free 学习。教程中的 C 语言示例程序在 C-Free 环境下面也能编译通过。当然在这里,还是建议您使用虚拟机安装一个 Linux 学习环境,一个良好的环境更有利于我们的学习,在安装过程有什么疑惑,可以通过邮件(3284757626@qq.com)联系,也可以加入QQ群(475504428),参与技术讨论。

微信公众号:宅学部落

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C语言
    +关注

    关注

    180

    文章

    7602

    浏览量

    136358
  • LINUX内核
    +关注

    关注

    1

    文章

    316

    浏览量

    21627
  • GNU
    GNU
    +关注

    关注

    0

    文章

    143

    浏览量

    17482
收藏 人收藏

    评论

    相关推荐

    C语言相关资料(编程规范,语法解析等等)

    眼不说,隔一段时间再看的时候实在是费很大气力;有一些基础的C语言语法,或许你看过郭天祥的51那本书之后就没有再深究过了,不是说郭天祥大哥写的书不好,而是或许那些知识入门绰绰有余,但是我们有必要深究常用C
    发表于 12-23 17:19

    请问下面的C语言语句表达什么意思?

    下面的C语言语句表达什么意思??
    发表于 11-01 04:14

    C语言语句的规则函数

    的,而C语言语句的规则函数返回的数据类型+函数名+参数+函数主体所以这几行代码也就不难理解了。该函数类型是void型,函数名称是Delay,_IO是类型修饰符,指的是单片机的静态IO口;uint_32_t是指32位的无符号整形变量;由for循环构成了函数的主体,意思是32
    发表于 07-14 08:13

    Linux内核中GNU C扩展的一些常用C语言语法分析

    13.1 总结前面12节的课程,主要针对 Linux 内核中 GNU C 扩展的一些常用 C 语言语法进行了分析。GNU C 的这些
    发表于 12-14 06:29

    Linux内核中的C语言语法扩展

    收集一些感觉比较好的博客链接,方便以后再看Linux 内核中的 C 语言语法扩展Linux 内核驱动中的指定初始化宏构造利器:语句表达式Linux 内核第一宏:container_of零长
    发表于 12-15 06:04

    YACC在ATLAS语言语法分析中的冲突消解研究

    对使用YACC工具进行ATLAS语言语法分析过程中出现的大量冲突进行了详细的分类讨论与研究,给出了实现过程中出现的主要冲突类型及相应解决方案:文法符号的不断自身循环产生
    发表于 09-08 15:30 0次下载

    C语言语法基础

    发表于 07-06 15:18 7次下载

    C语言误用易错知识点与基本语法汇总

    语言之所以能称之为语言,它肯定是一种工具一种相互交流相互通信相互传达之间的意图的工具,作为语言那肯定得有自己的语法,要想相互交流肯定得先学好它的语法
    发表于 05-05 15:32 2043次阅读

    Prel语法C语言语法的异同综述

    Prel语法C语言语法的异同综述
    发表于 05-25 11:44 6次下载

    嵌入式系统常用的C语言基本语法概要

    嵌入式系统常用的C语言基本语法概要
    发表于 10-20 12:51 6次下载
    嵌入式系统常用的<b class='flag-5'>C</b><b class='flag-5'>语言</b>基本<b class='flag-5'>语法</b>概要

    单片机中常用的C语言语句合集

    单片机中常用的C语言语句合集
    发表于 01-12 09:24 45次下载

    C语言有哪些语法技巧和功能

    C语言常常让人觉得它所能表达的东西非常有限。它不具有类似第一级函数和模式匹配这样的高级功能。但是C非常简单,并且仍然有一些非常有用的语法技巧和功能,只是没有多少人知道罢了。
    的头像 发表于 03-10 17:42 2017次阅读

    第九章 AVR C语言的应用.PDF

    本教程讲解的是AVR C语言编程,包括开发环境、语言语法、特殊用法基础知识的讲解
    发表于 03-17 14:21 0次下载

    C语言_语句与位运算基本练习

    总结C语言语句的几个基本练习题,计算素数、排序、求偶数和、可逆素数、水仙花数、交换大小写、交换变量的值、位运算、语法特性等知识点。
    的头像 发表于 08-14 09:46 987次阅读

    初学C语言不了解的那些事~

    其实C语言语法非常简单,可以说是目前主流编程语言语法点最少的一门编程语言了,然而C
    发表于 08-07 10:00 260次阅读