你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C语言的一些"令人震惊"的结构

[复制链接]
gaosmile 发布时间:2020-12-1 21:17

C语言会同意一些"令人震惊"的结构,下面的结构是合法的吗,我们来看看几个例子。

c = a+++b;

以下代码是合法的吗,咋的一看不禁有这样的疑问?

int a = 5, b = 7, c;
: K2 k7 p. L0 `% j1 [  G" xc = a+++b;
7 o# W! e8 A9 q9 g' x% o  x

这个代码确实不咋符合习惯的写法,但是不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?

根据最处理原则,编译器应该能够尽可能处理所有合法的用法。因此,上面的代码会被处理成:

c = a++ + b;
, W( P' F7 R" Z$ U/ H# e1 {

我们来测试一下:

//https://tool.lu/coderunner/  o0 }8 f  s& \6 G+ Q6 n0 J: h
//来源:技术让梦想更伟大
3 Z! W" {6 c! |* W; C//作者:李肖遥
: }7 W9 M( F) D% K1 h4 q( q3 G8 `# r; ~6 @8 y- h
#include <stdio.h>
5 T" f) x$ A; P3 b- J- v( l
+ d3 l6 n! J- H, x$ r  G) M8 sint main()  [8 I0 h3 z+ n/ f6 ~: O" ~
{
) F& I) ~1 H* `8 E* {9 R9 b) k0 z  int a = 5, b = 7, c;
5 v0 B( E* u+ m" a7 S  c = a+++b;3 M) ^% m7 \7 S+ B
  printf("a = %d,b = %d,c = %d",a,b,c);
9 T" c: Y( ~0 R% B# s2 ]& D/ `  return 0;
; k- n9 A' V8 l. |}
& v+ X* O' ~. z" R+ x" \# o
( }% H  Z, x/ D9 g# D2 W

输出结果如下:

微信图片_20201201211339.png

其执行顺序:

  • b不变,c = a + b;,则c = 5 + 7 = 12
  • a++,那么a = 6;
    9 `) Z# M7 i; }! b- Z: i( ]
c = a+++++b;

上面那么可能只是开胃菜,那么这个表达式呢,有什么想法?咱们二话不说,直接上代码测试。

//https://tool.lu/coderunner/& k* Q$ N3 |7 t# @  f$ Y
//来源:技术让梦想更伟大+ N& d+ j5 T  @3 b6 s* z3 G
//作者:李肖遥
5 ?1 _9 k3 y/ o4 T( A1 I6 _, j: m% s8 b
#include <stdio.h>
- T) k+ j% j3 f1 z* I/ z7 A
% \- L  [0 b1 P* N/ mint main()9 _6 U, B/ o) d1 _
{4 W' V" h6 J1 P  ?1 s6 g2 b5 b1 E
  int a = 5, b = 7, c;: L( U7 Y+ \' U% B9 K+ j
  c = a+++++b;# }: S1 d: _6 b) j7 m) a5 m- |
  //c = (a++) + (++b);( n( ~' n" J6 r4 q# b7 s3 a
  //c = ((a++)++) + b;
) I$ A# x1 J, G: |7 I  o' ]* _* T  printf("a = %d,b = %d,c = %d",a,b,c);
% h+ g; S, ]# r" B  return 0;
$ w) I, |$ Q2 v}9 W+ p7 K) |: z/ h5 e! w

编译结果如下:

微信图片_20201201211342.png

虽然一看就知道意思是(a++)+(++b);,但是编译就通不过,我们把括号加上,c = (a++) + (++b);,编译通过,那么出错信息大概是:括号影响了优先级?我们从侧面以及原理来解析一下。

侧面解析一下

对于a+++++b这一段代码,编译系统从左至右扫描整条语句,先遇到a++,判断出来是一个a的后缀自加运算;

然后接着扫描,遇到一个+,+是一个二目运算符,它的左边已经有一个运算数a++了,系统就向右搜索第二个运算数;

又遇到一个+,++比+的运算级别要高,这时,编译系统就将两个+看成一个整体来处理;

既然是++,编译系统就认定,肯定它的左边或右边有一个变量,编译系统先搜索左边,发现++,不是变量;

再搜索右边,发现+b,+b是什么东西?编译系统是无法搞明白的;

因此它就认为++是一个缺少左值的自增运算符,于是提示错误给用户:lvalue required as increment operand

原理解析一下

C语言在这里遵循词法解析的贪婪匹配原则。优先匹配尽可能多字符的符号,无论是否有语法错误(因为词法分析时还没有语法检查)。

于是a+++++b会被当作a ++ ++ + b,这是非法的表达式,因此产生编译错误。

这个问题在ISO C99标准中直接以示例描述,原文如下:

EXAMPLE 2 The program fragment x+++++y is parsed as x ++ ++ + y, which violates a constraint on increment operators, even though the parse x ++ + ++ y might yield a correct expression.

那么,也许是c = ((a++)++) + b;这个意思呢?

我们编译结果依然报错,如下:

微信图片_20201201211349.png

这涉及到“左值”的问题。百度百科定义如下:

左值(lvalue) 是B语言/C语言/C++语言等类C语言中的一类表达式。“左”(left)的原意是指可以放在赋值符号“=”的左边,但其实也表示能作为&和++等操作符的操作数(B语言中已经如此)。而且,现代C/C++中的含义已经不局限于此。lvalue的l被重新解释为location。这也对应于ISO C11/ISO C++11的内存位置(memory location)。

总结:a存在左值,可以有表达式:a++。但是(a++)不存在左值,无法继续执行(a++)++操作,所以最终报错。

怎么样才能编译正确呢?

a+++++b在编译时会报错,那么a++ + ++b呢?来看以下代码:

//https://tool.lu/coderunner/
6 l/ p+ n1 A" T1 k) T; F: k//来源:技术让梦想更伟大  `7 s& z0 G# S5 C* ~4 L# c0 G' s
//作者:李肖遥
; a' H% j- {6 p1 T  Q  L# i3 w0 ]! t. N& J
#include <stdio.h>
# n" X3 a; Y. u. e; O% v
2 [3 f* O' u  J7 H, y6 t, S) i3 _( xint main()( p* E5 B4 z) X
{
, T. {2 _8 O4 O/ s- V+ k. |- b int a = 5, b = 7, c;( }7 ^6 H/ P  c: a" ]2 j  R
c = a++ + ++b;
9 ]$ E. U- b, R; ]$ A- F7 y' e printf("a = %d,b = %d,c = %d",a,b,c);
5 S5 H7 T6 B' y& |/ V" b/ l# Q! _ return 0;
4 V7 T; K% k  R5 U# {. i) l$ T5 }}7 v& |; U- R$ @4 g3 j- o

结果如下:

微信图片_20201201211352.png

其代码与c = (a++) + (++b);结果一样,说明是正确的,其按照下面顺序执行:

  • 先执行b自加,b变为8;相当于:b = b+ 1;
  • 求a与b之和,赋给c;相当于:c = a + b ;//c = 5+8;
  • 执行第二步之后,a自加1:a++;7 k; c8 W$ T! @- d
c=(++a,b++,a++,++b);

这个表达式看着爽不爽?我们知道自增自减运算,表示对自身进行a=a+1或者a=a-1的运算。

++a表示在调用前就a+1,a++表示在调用后+1。

int c=(++a,b++,a++,++b);这个逗号隔开的表示用最后一个式子对C进行赋值,测试如下:

//https://tool.lu/coderunner/
/ {; T. s2 Z1 O: u2 Y+ B//来源:技术让梦想更伟大
- Z: n5 t! `0 E8 t3 J0 c2 A//作者:李肖遥
7 f  c+ y( ^* h' t' s4 w, i: L- S2 l$ {  f  E4 ~$ k
#include <stdio.h>4 Z6 e' z+ j7 d; ~9 I7 Y' S  R
7 H- I! U$ i3 }0 n( [8 o6 R) u
int main()* j; Y  z, d/ ]# u% u3 j
{
7 B0 H' k0 v7 U int a = 5, b = 7, c;% c: H7 ^; i8 D

+ U4 v4 [5 S; n$ F/ v8 |& E4 `% n) p c=(++a,b++,a++,++b);
( X  o/ j! A' B5 h( z8 V+ C" R" _# ^ printf("a = %d,b = %d,c = %d",a,b,c);7 K6 d$ J: d! U3 x' d7 S* s; k
return 0;, t  @( `# x  [+ F0 F. H- W
}
6 ^6 s% r' A: v- M% C

输出的结果如下:

微信图片_20201201211355.png

这段执行的顺序如下

  • 先执行++a,a=6;
  • 再执行b++,b=8;
  • 接下来a++,a=7;
  • 再执行++b,b=9;
  • 把最后一个的式子b=9的值赋给c,所以c等于9。如果改成c=(++a,b++,a++,b++); ,那么c就是等于8,因为是调用后再自增。
    ! |) z8 y  w/ |' m
总结

我们看到这些有趣且不常见的代码的时候,无需怀疑,只需要根据经验一步一步分析,就会发现其中的玄妙之处。在此我总结以下几点。

  • 如果你一看就知道了答案,或猜出正确答案,说明基础做得好,点个赞。
  • 如果你很犹豫,不知道答案,这也是正常的,因为很少见到这样写代码的,但是我们也需要去了解,才能究其根源。
  • 我总结这个问题,是想提起一个关于代码编写风格,代码的可读性,代码的可修改性的话题,这样我们在写代码的时候才能够注意到,更好的编码。
    , M' G6 q$ {% X- m7 E* V$ ]$ q0 g
' q5 w" w" w5 u
收藏 评论2 发布时间:2020-12-1 21:17

举报

2个回答
fafa1 回答时间:2020-12-2 13:40:04
我从来不这么干!不给自己个别人找麻烦!
liang118038 回答时间:2020-12-3 21:50:49
这种装模作样的代码语法就国人喜欢拿来装逼用而已,代码是拿来阅读改进的,浪费时间去增加阅读难度还不如好好想想你的应用逻辑怎么改进优化

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版