linux内核C -- 第03课:宏构造利器——语句表达式

c语言常见表达式汇总(赋值表达式,条件表达式,关系表达式,算数表达式......) 关系表达式:用比较运算符(>、=、 y【用关系运算符将两个数值或数值表达式连接起来的式子,称为关系表达式】位运算表达式:使用位运算符(&、|、^、)对整数的二进制位进行操作,例如:x & y。算术表达式(数值表达式):使用算术运算符(+、-、*、/)来执行算术运算,例如:x + y。这些只是常见的表达式类型,C语言还有许多其他类型的表达式,包括位段、条件编译表达式等等。字符表达式:用于访问字符类型的值,如 'a'、'b'、'a'+'b' 等。 阅读详情

 

基础复习:表达式、语句和代码块

表达式

表达式和语句是 C 语言中的基础概念。什么是表达式呢?表达式就是由一系列操作符和操作数构成的式子。操作符可以是 C 语言标准规定的各种算术运算符、逻辑运算符、赋值运算符、比较运算符等。操作数可以是一个常量,也可以是一个变量。表达式也可以没有操作符,单独的一个常量甚至是一个字符串,也是一个表达式。下面的字符序列都是表达式:

  • 2 + 3
  • 2
  • i = 2 + 3
  • i = i++ + 3
  • "wit"

表达式一般用来数据计算或实现某种功能的算法。表达式有2个基本属性:值和类型。如上面的表达式2+3,它的值为5。根据操作符的不同,表达式可以分为多种类型,如:

  • 关系表达式
  • 逻辑表达式
  • 条件表达式
  • 赋值表达式
  • 算术表达式
  • ……

语句

语句是构成程序的基本单元,一般形式如下:

表达式 ;
i = 2 + 3 ;

表达式的后面加一个; 就构成了一条基本的语句。编译器在编译程序、解析程序时,不是根据物理行,而是根据分号 ; 来判断一条语句的结束标记的。如 i = 2 + 3; 这条语句,你写成下面的形式也是可以编译通过的:

i =
2 + 
3
;

代码块

不同的语句,使用大括号{}括起来,就构成了一个代码块。C 语言允许在代码块里定义一个变量,这个变量的作用域也仅限于这个代码块内,因为编译器就是根据{}来做入栈出栈操作来管理变量的作用域的。如下面的程序:

int main(void)
{
    int i = 3; 
    printf("i=%d\n",i);
    {
        int i = 4;
        printf("i=%d\n",i);
    }
    printf("i=%d\n",i);
    return 0;
}
运行结果为:
i=3
i=4
i=3

语句表达式

语句表达式的定义

GNU C 对 C 标准作了扩展,允许在一个表达式里内嵌语句,允许在表达式内部使用局部变量、for 循环和 goto 跳转语句。这样的表达式,我们称之为语句表达式。语句表达式的格式如下:

({ 表达式1; 表达式2; 表达式3; })

语句表达式最外面使用小括号()括起来,里面一对大括号{}包起来的是代码块,代码块里允许内嵌各种语句。语句的格式可以是 “表达式;”这种一般格式的语句,也可以是循环、跳转等语句。

跟一般表达式一样,语句表达式也有自己的值。语句表达式的值为内嵌语句中最后一个表达式的值。我们举个例子,使用语句表达式求值。

int main(void)
{
    int sum = 0;
    sum = 
    ({
        int s = 0;
        for( int i = 0; i < 10; i++)
            s = s + i;
            s;
    });
    printf("sum = %d\n",sum);
    return 0;
}

在上面的程序中,通过语句表达式实现了从1到10的累加求和,因为语句表达式的值等于最后一个表达式的值,所以在 for 循环的后面,我们要添加一个 s; 语句表示整个语句表达式的值。如果不加这一句,你会发现 sum=0。或者你将这一行语句改为100; 你会发现最后 sum 的值就变成了100,这是因为语句表达式的值总等于最后一个表达式的值。

语句表达式内使用 goto 跳转

在上面的程序中,我们在语句表达式内定义了局部变量,使用了 for 循环语句。在语句表达式内,我们同样也可以使用 goto 进行跳转。

int main(void)
{
    int sum = 0;
    sum = 
    ({
        int s = 0;
        for( int i = 0; i < 10; i++)
            s = s + i;
            goto here;
            s;  
    });
    printf("sum = %d\n",sum);
here:
    printf("here:\n");
    printf("sum = %d\n",sum);
    return 0;
}

在宏定义中使用语句表达式

语句表达式的亮点在于定义复杂功能的宏。使用语句表达式来定义宏,不仅可以实现复杂的功能,而且还能避免宏定义带来的歧义和漏洞。下面就以一个宏定义例子,让我们来见识见识语句表达式在宏定义中的强悍杀伤力!

假如你此刻正在面试,面试职位是:Linux C 语言开发工程师。面试官给你出了一道题:

请定义一个宏,求两个数的最大值。

别看这么简单的一个考题,面试官就能根据你写出的宏,来判断你的 C 语言功底,来决定给不给你 Offer。

合格

对于学过 C 语言的同学,写出这个宏基本上不是什么难事,使用条件运算符就能完成:

#define  MAX(x,y)  x > y ? x : y

这是最基本的 C 语言语法,如果连这个也写不出来,估计场面会比较尴尬。面试官为了缓解尴尬,一般会对你说:小伙子,你很棒,回去等消息吧,有消息,我们会通知你!这时候,你应该明白:不用再等了,赶紧把这篇文章看完,接着面下家。这个宏能写出来,也不要觉得你很牛 X,因为这只能说明你有了 C 语言的基础,但还有很大的进步空间。比如,我们写一个程序,验证一下我们定义的宏是否正确:

#define MAX(x,y) x > y ? x : y
int main(void)
{
    printf("max=%d",MAX(1,2));
    printf("max=%d",MAX(2,1));
    printf("max=%d",MAX(2,2));
    printf("max=%d",MAX(1!=1,1!=2));
    return 0;
}

测试程序吗,我们肯定要把各种可能出现的情况都测一遍。这不,测试第4行语句,当宏的参数是一个表达式,发现实际运行结果为 max=0,跟我们预期结果 max=1 不一样。这是因为,宏展开后,就变成了这个样子:

printf("max=%d",1!=1>1!=2?1!=1:1!=2);

因为比较运算符 > 的优先级为6,大于 !=(优先级为7),所以展开的表达式,运算顺序发生了改变,结果就跟我们的预期不一样了。为了避免这种展开错误,我们可以给宏的参数加一个小括号()来防止展开后,表达式的运算顺序发生变化。这样的宏才能算一个合格的宏:

#define MAX(x,y) (x) > (y) ? (x) : (y)

中等

上面的宏,只能算合格,但还是存在漏洞。比如,我们使用下面的代码测试:

#define MAX(x,y) (x) > (y) ? (x) : (y)
int main(void)
{
    printf("max=%d",3 + MAX(1,2));
    return 0;
}

在程序中,我们打印表达式 3 + MAX(1, 2) 的值,预期结果应该是5,但实际运行结果却是1。我们展开后,发现同样有问题:

3 + (1) > (2) ? (1) : (2);

因为运算符 + 的优先级大于比较运算符 >,所以这个表达式就变为4>2?1:2,最后结果为1也就见怪不怪了。此时我们应该继续修改这个宏:

#define MAX(x,y) ((x) > (y) ? (x) : (y))

使用小括号将宏定义包起来,这样就避免了当一个表达式同时含有宏定义和其它高优先级运算符时,破坏整个表达式的运算顺序。如果你能写到这一步,说明你比前面那个面试合格的同学强,前面那个同学已经回去等消息了,我们接着面试下一轮。

良好

上面的宏,虽然解决了运算符优先级带来的问题,但是仍存在一定的漏洞。比如,我们使用下面的测试程序来测试我们定义的宏:

#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void)
{
    int i = 2;
    int j = 6;
    printf("max=%d",MAX(i++,j++));
    return 0;
}

在程序中,我们定义两个变量 i 和 j,然后比较两个变量的大小,并作自增运算。实际运行结果发现 max = 7,而不是预期结果 max = 6。这是因为变量 i 和 j 在宏展开后,做了两次自增运算,导致打印出 i 的值为7。

遇到这种情况,那该怎么办呢? 这时候,语句表达式就该上场了。我们可以使用语句表达式来定义这个宏,在语句表达式中定义两个临时变量,分别来暂储 i 和 j 的值,然后进行比较,这样就避免了两次自增、自减问题。

#define MAX(x,y)({     \
    int _x = x;        \
    int _y = y;        \
    _x > _y ? _x : _y; \
})
int main(void)
{
    int i = 2;
    int j = 6;
    printf("max=%d",MAX(i++,j++));
    return 0;
}

在语句表达式中,我们定义了2个局部变量 _x、_y 来存储宏参数 x 和 y 的值,然后使用 _x 和 _y 来比较大小,这样就避免了 i 和 j 带来的2次自增运算问题。

你能坚持到了这一关,并写出这样自带 BGM 的宏,面试官心里可能已经有了给你 Offer 的意愿了。但此时此刻,千万不要骄傲!为了彻底打消面试官的心理顾虑,我们需要对这个宏继续优化。

优秀

在上面这个宏中,我们定义的两个临时变量数据类型是 int 型,只能比较两个整型的数据。那对于其它类型的数据,就需要重新再定义一个宏了,这样太麻烦了!我们可以基于上面的宏继续修改,让它可以支持任意类型的数据比较大小:

#define MAX(type,x,y)({     \
    type _x = x;        \
    type _y = y;        \
    _x > _y ? _x : _y; \
})
int main(void)
{
    int i = 2;
    int j = 6;
    printf("max=%d\n",MAX(int,i++,j++));
    printf("max=%f\n",MAX(float,3.14,3.15));
    return 0;
}

在这个宏中,我们添加一个参数:type,用来指定临时变量 _x 和 _y 的类型。这样,我们在比较两个数的大小时,只要将2个数据的类型作为参数传给宏,就可以比较任意类型的数据了。如果你能在面试中,写出这样的宏,面试官肯定会非常高兴,他一般会跟你说:稍等,待会 HR 会跟你谈待遇问题。恭喜你,你拿到 Offer 了!

语句表达式在 Linux 内核中的使用

语句表达式,作为 GNU C 对 C 标准的一个扩展,在内核中,尤其是在内核的宏定义中,被大量的使用。使用语句表达式定义宏,不仅可以实现复杂的功能,还可以避免宏定义带来的一些歧义和漏洞。比如在 Linux 内核中,max_t 和 min_t 的宏定义,就使用了语句表达式:

#define min_t(type, x, y) ({            \
    type __min1 = (x);          \
    type __min2 = (y);          \
    __min1 < __min2 ? __min1 : __min2; })

#define max_t(type, x, y) ({            \
    type __max1 = (x);          \
    type __max2 = (y);          \
    __max1 > __max2 ? __max1 : __max2; })

除此之外,在 Linux 内核、GNU 开源软件中,你会发现,还有大量的宏定义使用了语句表达式。通过本节教程的学习,相信大家以后再碰到这种使用语句表达式定义的宏,肯定就知道是怎么回事了,心中有丘壑,再也不用慌。

C语言嵌入式Linux高级编程:程序的编译、链接与运行深度解析 理解程序的编译、链接与运行机制是嵌入式开发的核心基础。从预处理到动态链接,每一步都涉及底层细节的精确控制。通过工具链(如GCC、Binutils)和脚本(如链接脚本),开发者可以灵活适配不同硬件平台,优化程序性能与资源占用。深入掌握这些知识,能够有效解决系统级问题,提升嵌入式软件的开发效率。 阅读详情

相关推荐

HyperMesh二次开发实战:自动化Comp与材料属性批量绑定

本文详细介绍了HyperMesh二次开发中自动化Comp与材料属性批量绑定的实战技巧。通过TCL脚本实现批量创建属性卡、智能材料分配和异常处理,大幅提升前处理效率,适用于汽车、航空航天等领域的复杂模型处理。

weixin_29214691的博客 688

C语言程序——表达式语句的使用

C语言中的顺序程序表达式的使用。

glx6666的博客 3492

RGB接口电容触摸屏驱动(一)模块介绍

RGB接口电容触摸屏驱动(一)模块介绍

杰克拉力船长个人博客 7872

获取宏定义_嵌入式C语言自我修养 03:宏构造利器:语句表达式

3.1 基础复习:表达式、语句和代码块表达式表达式和语句是 C 语言中的基础概念。什么是表达式呢?表达式就是由一系列操作符和操作数构成的式子。操作符可以是 C 语言标准规定的各种算术运算符、逻辑运算符、赋值运算符、比较运算符等。操作数可以是一个常量,也可以是一个变量。表达式也可以没有操作符,单独的一个常量甚至是一个字符串,也是一个表达式。下面的字符序列都是表达式:2 + 32i = 2 + 3i ...

weixin_39562998的博客 170

Cheat Engine实战:多级指针逆向破解游戏内存的5个关键步骤(附C++模拟代码)

本文详细解析了使用Cheat Engine进行多级指针逆向破解游戏内存的完整流程。通过5个关键步骤,从定位动态地址到逐层追踪指针链,最终构建稳定指针路径。文章结合C++模拟代码,深入剖析了指针链的底层汇编原理,并提供了高级排查技巧,帮助读者掌握逆向工程中的核心内存追踪技术。

weixin_29239279的博客 306

ubuntu17.04 UHD 驱动出错

ubuntu 17.04 源码安装UHD

xrh003的博客 1万+

C语言中常用的表达式

1.表达式的定义 表达式是一种有值的语法结构,它由运算符和常量、变量、函数调用返回值等结合而成,每个表达式一定有一个值 2.常量表达式 例如:a , 12 , 12.4 常量表达式的值,就是变量或者常量本身的值。 作为条件的时候,非0即真,0即假。 3.算术表达式 例如:a+b , c*a+b , 12/3+d , i++ , --a 算术表达式的值

weixin_55772584的博客 1万+

Logisim实战:手把手教你搭建GB2312汉字字库(附完整电路图)

本文提供了一份详尽的Logisim实战指南,手把手教你如何利用存储芯片,通过位扩展和字扩展技术,构建一个完整的GB2312汉字字库。文章深入浅出地讲解了从编码原理、系统架构设计到具体电路实现与调试的全过程,是学习计算机组成原理和数字电路设计的绝佳实践项目。

k6l7m8n9的博客 807

【C语言初阶】表达式和基本语句(布尔型与0比较,浮点型与0比较,switch语句,提高循环语句的效率)

运算符的优先级、复合表达式、if语句(布尔型与0比较,浮点型与0比较等)、switch语句、循环语句的效率、for语句、goto语句

52Herzt的博客 1906

Sigrity Power SI Model Extraction模式如何提取电源网络的S参数和阻抗操作指导(一)

34. 默认格式是BNP format格式,会被Sigrity识别的S参数文件,在仿真完成后,默认会在文件夹中生成bnp和ckt(MCP)文件,无需这步操作。由于port设置的ref Z是50ohm,实际上电源网络的阻抗是没有这么高的,所以S参数回损曲线是基本上全反射的一个状态。Sigrity PowerSI是频域电磁场仿真工具,以下图为例介绍如果用它观测电源的网络的S参数以及阻抗的频域曲线.42. Spice格式的加载进来图标是橙色的,BNP格式的图标是灰色的。

weixin_54787054的博客 1277

self‑improving‑agent 安装与配置指南(OpenClaw)

长期记忆错误反思用户偏好学习持续进化AI → 反思 → 学习 → 改进。

tiantiantbtb的博客 1337

Linux C 语言高级编程

(1)GNU工具编译工具:把一个源程序编译为一个可执行程序调试工具:能对执行程序进行源码或汇编级调试软件工程工具:用于协助多人开发或大型软件项目的管理,如make、CVS、Subvision其他工具:用于把多个目标文件链接成可执行文件的链接器,或者用作格式转换的工具。(2)部分相关资源(3)GCC简介全称为GNU CC ,GNU项目中符合ANSI C标准的编译系统。

qq_52049228的博客 549

Far planner 代码系列(33) 关于real_world_contour和contour_graph

我们知道far planner前部分的流程是这样子的: 对于特定的地图,如下: 我们要从surround_obs_cloud里抽取生成realworld_contour_,如下图所示: 在这时候,每个node的z坐标都是以机器人的高度定的。如图所示: 先去吃饭啦~待会回来在写 ...

博客 762

嵌入式C语言自我修养 (03):宏构造利器:语句表达式

3.1 基础复习:表达式、语句和代码块 表达式 表达式和语句是 C 语言中的基础概念。什么是表达式呢?表达式就是由一系列操作符和操作数构成的式子。操作符可以是 C 语言标准规定的各种算术运算符、逻辑运算符、赋值运算符、比较运算符等。操作数可以是一个常量,也可以是一个变量。表达式也可以没有操作符,单独的一个常量甚至是一个字符串,也是一个表达式。下面的字符序列都是表达式: 2 + 3 2 i...

宅学部落-王利涛 1020

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

C语言基本语句

文章目录@[TOC](文章目录)前言C语言基本语句一、表达式语句二、函数调用语句三、控制语句1)分支语句(选择语句)2)循环语句四、复合语句五、空语句 前言 关于自己的第一篇文章: 记录自己C语言语句的学习成果,着重控制语句 提示:以下是本篇文章正文内容,下面案例可供参考 C语言基本语句 C语言语句被分为以下五类: 1.表达式语句 2.函数调用语句 3.控制语句 4.复合语句 5.空语句 3)控制语句用于控制程序的走向 一、表达式语句 表达式语句一般由分号结尾,其形式表示为:表达式; 表达式语句中最常用

J2410480224的博客 1万+

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

C语言中常见的表达式

文章目录前言一、pandas是什么?二、使用步骤2.读入数据总结 前言 本文主要介绍什么是表达式,表达式在C语言中非常常见希望大家可以好好学习。 一、pandas是什么? 二、使用步骤 2.读入数据 总结 ...

m0_49476241的博客 2万+

C语言:表达式

表达式就是表示如何计算值的公式,最简单的表达式就是变量和常量,复杂的表达式还包括运算符 变量:程序运行时计算的值 常量:不变的值 运算符:用于构建表达式 运算符一共分为以下几种 1)算数运算符:如,加减乘除 2)关系运算符:如,大于小于这种比较运算符 3)逻辑运算符:如,与或非等 算数运算符: 一元运算符: + 一元正号运算符 - 一元负号运算符 二元运算符: 加法类: + 加法运算符 - 减法运算符 乘法类: * 乘法运算符 / 除法运算符 % 取余运算符 这里解.

weixin_45728756的博客 6226

C中表达式、语句、代码块

表达式、语句、代码块

Black_Cat_33的博客 2243

【Linux】基础篇五--linux C语言高级编程

Linux学习5--linux C语言高级编程一、GCC与GDB1、GCC编译器2、GDB调试工具二、条件编译与结构体1、条件编译2、结构体3、结构体数组与结构体指针三、共用体及typedef1、共用体2、typedef四、内存管理1、储存模型2、内存管理3、malloc/free注意事项 一、GCC与GDB 1、GCC编译器 (1)GNU工具 编译工具:把一个源程序编译为一个可执行程序 调试工具:能对执行程序进行源码或汇编级调试 软件工程工具:用于协助多人开发或大型软件项目的管理,如make、CVS、S

修成真的博客 1万+

C语言学习【表达式和语句】

* 定义一个带一个参数的函数 *//* ANSI函数原型声明 *//* ASCII 码是33 *//* int 类型的参数 */pound(ch);/* 和pound((int)ch)相同 */pound(f);/* 和pound((int)f)相同 */void pound(int n) /* ANSI 风格函数头 */程序运行结果###########

qq_50930131的博客 1089

C语言学习3——表达式与语句 表达式的含义和分类:赋值表达式、算术表达式、关系表达式、逻辑表达式 语句的基本类型:赋值语句、条件语句、循环语句、函数调用语句、复合语句

表达式是C语言程序设计中最基本的概念之一,其本质是由运算符、操作数和函数调用组成的语句。常见的表达式类型包括算术表达式、关系表达式、逻辑表达式、条件表达式等。语句是C语言程序设计中的基本单元,通常由一个或多个表达式、关键字、标识符和分号组成。常见的语句类型包括赋值语句、条件语句、循环语句等。好的,对于C语言中表达式和语句的相关知识,我就讲解到这里了。条件语句用于根据条件来决定执行哪条语句,主要有if-else语句和switch语句两种形式。逻辑表达式用于组合和操作多个关系表达式的结果,其结果为真或假。

weixin_64568489的博客 1125

Linux基础、C语言高级基础知识总结(嵌入式系列学习3)

嵌入式学习系列之Linux基础、C语言高级基础知识,本篇主要讲述了Linux基本操作,以及C高级基础知识,Linux基本操作主要涉及到Linux基本命令及使用、链接文件、通配符和网络配置、shell基础知识等,C高级包括gdb调试、指针、递归等,都是用于嵌入式基础巩固。整篇是本人通过学习进行笔记记载,如有不足,还请不要吝啬指出。另外我们提供嵌入式项目毕设资料与服务,远程部署、代码讲解、全方位服务。我们有专门的毕设项目辅导群

2303_78453210的博客 1326

Linux嵌入式开发——C编程

Linux嵌入式开发——C编程 一、编写C程序 1.1、设置vim编辑器 1.2、编写C程序 二、编译C程序 三、make工具和Makefile文件 3.1、编写C程序 C文件 H文件 3.2、不使用make工具 3.3、使用make工具和Makefile文件编译

weixin_66578482的博客 2491

c语言表达式语句有,C语言中语句种类有哪些?

C语句可分为以下五类:1 表达式语句;2 函数调用语句;3 控制语句;4 复合语句;5 空语句。1) 表达式语句表达式语句由表达式加上分号“;”组成。其一般形式为:表达式;执行表达式语句就是计算表达式的值。例如 c=a+a;2) 函数调用语句由函数名、实际参数加上分号“;”组成。其一般形式为:函数名(实际参数表);例如 printf("Hello !");3) 控制语句控制语句用于控制程序的流程...

weixin_39605455的博客 1364
上一篇: Linux 内核C -- 第02课:驱动中的指定初始化
下一篇: linux内核C -- 第04课:Linux内核第一宏——container_of
废言Pro
博客等级 码龄16年 1092粉丝 142原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值