剖析C语言中a=a+++++a的无聊问题

本文探讨了C语言中不推荐使用的表达式a=a+++++a,指出其可能导致的编译错误和不确定性。通过实验展示了不同编译器对此表达式的处理方式,并强调了代码规范和清晰性的重要性,特别是在涉及自增运算符和多目运算符时。文章以实际编译过程为例,解释了为何不应在项目代码中使用此类语句。
今天中午在QQ群里和同僚们闲聊,突然就聊到了a+++++a的问题。这种纯属C语言 “二” 级的问题应该是从a+++a引申出来的吧。于是乎兄弟姐妹们开始讨论它的运算结果,以及改如何理解。更有人写出(a++)+(++a)   a+(++(++a)) ((a++)++)+a这样的东西,问应该如何计算 。我表示鸭梨很大...


针对这样的问题我的观点是,“绝不小心求证,只管大胆胡说!” 哈哈,当然了,我还是要对我的师兄弟们负责的,所以我下面的“胡说”中会尽量有理有据。


看法一:

a=a+++++a这个东西可以用来讨论,甚至是讨论它的无所事处,作为增长知识和发现自身理解问题的漏洞是可以的。但是绝对不能拿来作为考试题目,特别是选择题或填空题等客观题目。但是如果作为一道主观探讨题还是挺有趣的,理解深刻的人一定可以写的很好。


看法二:

a=a+++++a的编译和执行结果是随机的,可能有些屌丝编译器自认为自己很牛,可以处理这样的语句,并把它编译出来而不报任何警告。那么我首先建议这样的编译器别用了,其次我要说这个东西的编译结果并不重要,重要的是千万不要在项目代码中这样写。


下面让我们来看一下试验:

试验环境:

发行版:

[zorro@dhcp-65-110 tmp]$ cat /etc/issue
Fedora release 19 (Schrödinger’s Cat)
Kernel \r on an \m (\l)

内核和体系结构:

[zorro@dhcp-65-110 tmp]$ uname -a
Linux dhcp-65-110.nay.redhat.com 3.11.9-200.fc19.x86_64 #1 SMP Wed Nov 20

#include<stdio.h> #include<string.h> int main(){ char s[10000]; int cd,ag=0,bg=0,cg=0,dg=0,eg=0,fg=0,gg=0,hg=0,ig=0,jg=0,kg=0,lg=0,mg=0,ng=0,og=0,pg=0,qg=0,rg=0,sg=0,tg=0,ug=0,vg=0,wg=0,xg=0,yg=0,zg=0; scanf("%s",s); cd=strlen(s); for(int i=0;i<cd;i++){ if(s[i]=='a'){ ag++; } else if(s[i]=='b'){ bg++; } else if(s[i]=='c'){ cg++; } else if(s[i]=='d'){ dg++; } else if(s[i]=='e'){ eg++; } else if(s[i]=='f'){ fg++; } else if(s[i]=='g'){ gg++; } else if(s[i]=='h'){ hg++; } else if(s[i]=='i'){ ig++; } else if(s[i]=='j'){ jg++; } else if(s[i]=='k'){ kg++; } else if(s[i]=='l'){ lg++; } else if(s[i]=='m'){ mg++; } else if(s[i]=='n'){ ng++; } else if(s[i]=='o'){ og++; } else if(s[i]=='p'){ pg++; } else if(s[i]=='q'){ qg++; } else if(s[i]=='r'){ rg++; } else if(s[i]=='s'){ sg++; } else if(s[i]=='t'){ tg++; } else if(s[i]=='u'){ ug++; } else if(s[i]=='v'){ vg++; } else if(s[i]=='w'){ wg++; } else if(s[i]=='x'){ xg++; } else if(s[i]=='y'){ yg++; } else if(s[i]=='z'){ zg++; } } int zh[100000],a=0; zh[a]=ag; a++; zh[a]=bg; a++; zh[a]=cg; a++; zh[a]=dg; a++; zh[a]=eg; a++; zh[a]=fg; a++; zh[a]=gg; a++; zh[a]=hg; a++; zh[a]=ig; a++; zh[a]=jg; a++; zh[a]=kg; a++; zh[a]=lg; a++; zh[a]=mg; a++; zh[a]=ng; a++; zh[a]=og; a++; zh[a]=pg; a++; zh[a]=qg; a++; zh[a]=rg; a++; zh[a]=sg; a++; zh[a]=tg; a++; zh[a]=ug; a++; zh[a]=vg; a++; zh[a]=wg; a++; zh[a]=xg; a++; zh[a]=yg; a++; zh[a]=zg; a++; for(int i=0;i<a;i++){ if(i==a-1){ printf("%d\n",zh[i]); } else{ printf("%d ",zh[i]); } } return 0; }我这个程序哪里有问题
最新发布
09-02
<think>嗯,我现在要理解C/C++中的自增运算符和复合赋值表达式在表达式'a += a++ + ++b'中的行为。这个问题看起来有点复杂,因为涉及到运算符的优先级、求值顺序以及副作用的问题。让我先回忆一下相关知识,再逐步分析。 首先,自增运算符有前置和后置两种形式。前置++b会先增加b的值,然后返回增加后的值;后置a++则会先返回a的原始值,然后再增加a的值。复合赋值运算符+=是将右边的结果加到左边的变量上,比如a += expr等价于a = a + expr,不过这里的expr只计算一次。 接下来是运算符的优先级问题。在C/C++中,运算符的优先级决定了表达式各部分计算的顺序。后置自增的优先级高于前置自增,而前置自增又高于加法类运算符(比如+和+=)。不过,优先级只是决定了哪个运算符先和操作数结合,并不完全决定求值的顺序。例如,在a + b * c中,乘法优先级高,所以先计算b*c,但a和b*c各自的求值顺序是不确定的,除非有序列点存在。 但是问题中的表达式是a += a++ + ++b。这里涉及到的运算符包括后置++、前置++、+和+=。根据优先级,后置++的优先级最高,所以a++会先被处理。然后是前置++,所以++b。接着是加法运算符+,最后是复合赋值运算符+=。所以表达式可以分解为:a += ( (a++) + (++b) )。 不过,这里还有一个关键点:表达式中的求值顺序和副作用的发生时间。C/C++中,运算符的优先级和结合性规定了表达式的结构,但操作数的求值顺序大部分是未指定的(unspecified),除了有序列点的情况。例如,在a + b中,a和b的求值顺序是不确定的,编译器可以自由决定先计算a还是b。这可能导致不同编译器或不同情况下的结果不同。 现在来看这个具体的表达式:a += a++ + ++b。根据复合赋值运算符的规则,它等价于a = a + (a++ + ++b)。但这里的等号右边的所有操作数都是相对于原来的a吗?或者是否因为a++的存在导致中间a的值被修改? 这里需要分析各个子表达式的计算步骤。假设a和b的初始值分别是a0和b0: 1. 计算右边的表达式a++ + ++b: - 对于a++,其值为a0,计算后a变为a0+1。 - 对于++b,其值为b0+1,计算后b变为b0+1。 - 因此,右边的和是a0 + (b0+1)。 2. 然后,复合赋值a += ...,即a = a + (右边的和)。但此时左边的a是已经被a++修改后的值吗? 这里的问题在于,复合赋值运算符的左边a在计算右边表达式时已经被修改了(因为a++的副作用在序列点之前已经发生)。不过,在C/C++中,序列点的位置很重要。序列点存在于某些位置,比如完整表达式的结束处、函数调用、逻辑与/或的第一个操作数之后等。而在表达式a += a++ + ++b中,各个子表达式的求值顺序并没有明确的序列点,因此可能产生未定义行为(undefined behavior)。 根据C/C++标准,如果在两个序列点之间,同一个标量对象的修改次数超过一次,或者同时有修改和访问,那么结果是未定义的。例如,这里的表达式中的a被修改了两次:一次是a++,另一次是复合赋值运算a += ...。同时,在右边的表达式中,a同时被读取(在a++的右侧和复合赋值的右侧)。因此,这个表达式实际上导致了未定义行为,不同的编译器可能会有不同的处理方式,结果可能不一致。 例如,假设初始a=1,b=2: 一种可能的计算步骤是: - 计算a++:得到1,此时a变为2。 - 计算++b:得到3,此时b变为3。 - 右边总和为1+3=4。 - 复合赋值执行a = a +4,此时a已经是2,所以结果是2+4=6。因此最终a=6,b=3。 但是另一种可能的处理方式,比如编译器可能先计算所有右边的值,再进行赋值。比如: - 右边的总和是a的值(原始值1)加上++b(3),总和4。 - 然后a被递增到2,再执行a +=4,此时a=6。 这样结果也是一样的。 但另一种情况,如果编译器在计算右边的总和时,先处理++b,再处理a++,然后再执行复合赋值,结果可能相同。但如果有其他操作顺序,比如在计算右边的时候,a的值已经被修改,则可能产生不同的结果。例如,如果右边的表达式求值顺序是先计算++b,然后计算a++,那么总和还是4,结果同样。 不过,这里的关键是,根据C/C++标准,这种情况属于未定义行为,因为同一个变量在同一个表达式中被多次修改而没有序列点分隔。因此,这样的表达式应避免使用,因为结果不可预测,不同的编译器或不同的优化级别可能导致不同的结果。 总结来说,这个表达式的问题在于违反了C/C++的标准,导致了未定义行为。因此,在编程中应避免写出这样的表达式,而应分解为多个明确的步骤,确保每个变量在一个表达式中最多被修改一次,并且不同时进行读取和修改。</think>在C/C++中,表达式`a += a++ + ++b`的行为涉及**自增运算符的副作用**、**运算符优先级**和**未定义行为**,具体分析如下: --- ### 1. 运算符优先级与表达式分解 - **后置自增`a++`**优先级最高,先计算其原始值后自增; - **前置自增`++b`**次之,先自增`b`后返回值; - **加法`+`**最后计算,将`a++`和`++b`的结果相加; - **复合赋值`+=`**最后执行,将右侧结果累加到`a`。 表达式等价于: $$a = a + (a_{\text{原始值}} + (b_{\text{原始值}} + 1))$$ 但实际执行流程因编译器实现不同可能产生歧义[^1]。 --- ### 2. 潜在问题与未定义行为 C/C++标准规定:**同一变量在表达式中被多次修改且无序列点分隔时,行为未定义**。此处: 1. `a++`修改`a`; 2. `a += ...`再次修改`a`; 3. 右侧表达式同时读取和修改`a`。 例如,若初始值`a=1, b=2`: - 不同编译器可能输出`a=6`(先计算`a++`和`++b`,再执行`+=`)或产生意外结果[^2]。 --- ### 3. 安全实践建议 1. **避免混合自增与复合赋值**,拆分为独立语句: ```c int tmp1 = a++; int tmp2 = ++b; a += tmp1 + tmp2; ``` 2. **确保变量在表达式中仅被修改一次**,避免副作用干扰。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值