《嵌入式C语言自我修养》(6)GNU C编译器拓展语法精讲

3、语句表达式

3.1 表达式、语句、代码块

  • 复习:什么是表达式、语句和代码块?
    • 表达式:是由一系列操作符操作数构成的式子

      • 操作符:可以是C语言标准规定的各种算术运算符逻辑运算符赋值运算符比较运算符
      • 操作数:可以是一个常量,也可以是一个变量
      • 表达式也可以没有操作符,单独的一个常量甚至一个字符串,都是一个表达式

      一个例子
      在这里插入图片描述

    • 语句:表达式后面加一个;,就是语句

      一个例子
      在这里插入图片描述

    • 代码块:不同的语句,使用大括号{}括起来,就构成了一个代码块。C语言允许在代码块内定义一个变量,这个变量的作用域也仅限于这个代码块内。

      一个例子
      《嵌入式C语言自我修养》(6)GNU C编译器拓展语法精讲_第1张图片

      程序运行结果如下:
      在这里插入图片描述

3.2、语句表达式

  • GNU CC语言标准作了扩展,允许在表达式内部使用局部变量for循环goto跳转语句。这种类型的表达式,我们称为语句表达式。语句表达式的格式如下。
    在这里插入图片描述

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

  • 和一般表达式一样,语句表达式也有自己的值。语句表达式的值为内嵌语句中最后一个表达式的值

    • 实例:使用语句表达式求值,该例中语句表达式的值为s
      《嵌入式C语言自我修养》(6)GNU C编译器拓展语法精讲_第2张图片
      该例中在语句表达式内定义了局部变量,使用了for循环语句,下面展示使用goto进行跳转。
    • 实例:在语句表达式中使用goto语句
      《嵌入式C语言自我修养》(6)GNU C编译器拓展语法精讲_第3张图片

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

  • 语句表达式常用于何处?
    • 语句表达式的主要用途在于定义功能复杂的宏。使用语句表达式来定义宏,不仅可以实现复杂的功能,还能避免宏定义带来的歧义和漏洞。
  • 实例:在GCC编译环境中定义一个宏,求两个相同数据类型数的最大值。
    • 解法1
      在这里插入图片描述
      验证一下该解法:
      《嵌入式C语言自我修养》(6)GNU C编译器拓展语法精讲_第4张图片
      测试第4行语句,当宏的参数是一个表达式,发现实际运行结果为max=0,和我们预期结果max=1不一样。宏展开后为如下结果。
      在这里插入图片描述
      因为比较运算符>的优先级为6,大于!=(优先级为7),所以在展开的表达式中,运算顺序发生了改变,结果就和预期不一样了。
    • 解法2
      在这里插入图片描述
      这种解法规避了解法1中的漏洞,但依旧不合格。对该解法进行测试:
      《嵌入式C语言自我修养》(6)GNU C编译器拓展语法精讲_第5张图片
      在程序中,我们打印表达式3+MAX(1,2)的值,预期结果应该是5,但实际运行结果却是1。宏展开后,我们发现同样有问题。
      在这里插入图片描述
      因为运算符+的优先级大于比较运算符>,所以这个表达式就变为4>2?1:2,最终导致结果错误。
    • 解法3
      在这里插入图片描述
      这种解法规避了解法2中的漏洞,但还是不完美。对该解法进行测试:
      《嵌入式C语言自我修养》(6)GNU C编译器拓展语法精讲_第6张图片
      实际运行结果发现max=7,而不是预期结果max=6。这是因为变量ij在宏展开后,做了两次自增运算,导致打印出的i值为7
    • 解法4
      《嵌入式C语言自我修养》(6)GNU C编译器拓展语法精讲_第7张图片
      这次发现效果非常好,不妨测试一下:
      《嵌入式C语言自我修养》(6)GNU C编译器拓展语法精讲_第8张图片
      在语句表达式中,我们定义了2个局部变量_x_y来存储宏参数x和y的值,然后使用_x_y来比较大小,这样就避免了ij带来的2次自增运算问题。但是这依旧不完美,因为当前只能比较两个整形数据。
    • 解法5
      《嵌入式C语言自我修养》(6)GNU C编译器拓展语法精讲_第9张图片
      在这个宏中,我们添加一个参数type,用来指定临时变量_x_y的类型。这样,我们在比较两个数的大小时,只要将2个数据的类型作为参数传给宏,就可以比较任意类型的数据了。当然在这里依旧可以进行优化,因为这里多了一个参数,可以想办法避免增加此参数
    • 解法6
      《嵌入式C语言自我修养》(6)GNU C编译器拓展语法精讲_第10张图片
      在这个宏定义中,我们使用了typeof关键字来自动获取宏的两个参数类型。需要理解的是(void)(&x==&y);,它的作用有两个
      • 用来给用户提示一个警告,对于不同类型的指针比较,编译器会发出一个警告,提示两种数据的类型不同。
        在这里插入图片描述
      • 两个数进行比较运算,运算的结果却没有用到,有些编译器可能会给出一个warning,加一个(void)后,就可以消除这个警告。

3.4、内核中的语句表达式

  • 在内核中,尤其在内核的宏定义中被大量使用。使用语句表达式定义宏,不仅可以实现复杂的功能,还可以避免宏定义带来的一些歧义和漏洞。如在Linux内核中,max_tmin_t的宏定义,就使用了语句表达式。
    《嵌入式C语言自我修养》(6)GNU C编译器拓展语法精讲_第11张图片

你可能感兴趣的:(《嵌入式C语言自我修养》,c语言,算法,开发语言)