int n,a=3; n=a++*a的结果为什么是9,而不是12?
#include<stdio.h>
void main()
{
int n,a=3;
n=a++*a;
printf("%d\n",n);
}
如上代码,为什么n=3*3=9 而不是3*4=12呢? printf c
[解决办法]
置顶帖
这写法是错的 结果没意义
[解决办法]
//一般情况下
a++ ; //先执行语句运算,再作a+1操作
++a ; //先作a+1操作,在进行语句的运算
//所以
n=a++*a;
//先执行 n = 3*3 ,再执行 a = a+1;
[解决办法]
解释错了 误导别人
[解决办法]
我只是说了一般情况的!哪里有问题?
不要太迷恋置顶贴,这些题目虽然不同编译有不同的结果!
但是还有是一个方向的!不是什么都不可以的!
[解决办法]
++a * a 和 a * ++a
这个是确定的,结果一定是 (a+1)(a+1),++a必须先算,而且+1副作用立刻体现。
a++ * a
我认为是未定义,先做a++,至于a++的这个+1的副作用是发生在*a之前还是之后,没有规定。
实际上不会写这样的代码。考这样的代码也很无聊。
[解决办法]
我只是说了一般情况的!哪里有问题?
不要太迷恋置顶贴,这些题目虽然不同编译有不同的结果!
但是还有是一个方向的!不是什么都不可以的!
你想说一般情况 但是这没有一般情况 什么情况都是常态
你说了一般 但是你不能指望新手能记住你使用"一般"这个词的谨慎之处
选择忽视这个词 你就造成误导新人这个事实
你这里应该介绍的是 未定义行为 序列点 副作用 求值这些知识
而不是一句似是而非的 一般
最后你的一般的解释也不对
不是先运算 再+1 不是先+1 再运算
你混淆了 表达式的值和操作数的值
要知道 求值[你所谓的运算] 和 副作用[操作数+1]生效 之间是
没有联系的
没有先后关系
你说成有先后关系 是新手误以为 n=a++*a;之类未定义行为有确定结果的一大病因
[解决办法]
不存在“一般情况”。
那其实是你只试了很少几种编译器,很少几种影响语法树解析的情况。
我只是说了一般情况的!哪里有问题?
不要太迷恋置顶贴,这些题目虽然不同编译有不同的结果!
但是还有是一个方向的!不是什么都不可以的!
[解决办法]
n=a++*a;
009A38A5 mov eax,dword ptr [a]
009A38A8 imul eax,dword ptr [a]
009A38AC mov dword ptr [n],eax
009A38AF mov ecx,dword ptr [a]
009A38B2 add ecx,1
009A38B5 mov dword ptr [a],ecx
[解决办法]
++a * a 和 a * ++a
这个是确定的,结果一定是 (a+1)(a+1),++a必须先算,而且+1副作用立刻体现。
a++ * a
我认为是未定义,先做a++,至于a++的这个+1的副作用是发生在*a之前还是之后,没有规定。
实际上不会写这样的代码。考这样的代码也很无聊。
编译运行的结果吗?
补课去
[解决办法]
副作用立即体现的依据是什么 ?
++a * a 和 a * ++a
这个是确定的,结果一定是 (a+1)(a+1),++a必须先算,而且+1副作用立刻体现。
a++ * a
我认为是未定义,先做a++,至于a++的这个+1的副作用是发生在*a之前还是之后,没有规定。
实际上不会写这样的代码。考这样的代码也很无聊。
编译运行的结果吗?
补课去
前置++的即时效果可以从运算符优先级角度来考虑。
[解决办法]
前置++的即时效果可以从运算符优先级角度来考虑。
求值顺序只取决于 运算符本身[只有 ,?:
[解决办法]
&&确定求值顺序从左到右]
副作用只保证在到达序列点之前完成
优先级 只决定 运算符 和 哪些子表达式结合
------解决方案--------------------
优先级 和 求值顺序 以及 副作用的生效都没有关系
前置++的即时效果可以从运算符优先级角度来考虑。
求值顺序只取决于 运算符本身[只有 ,?:
[解决办法]
&&确定求值顺序从左到右]
副作用只保证在到达序列点之前完成
优先级 只决定 运算符 和 哪些子表达式结合
看的不是很懂,
运算符优先级在一个包含多个运算符的表达式中和求值顺序怎么会没有关系呢?这说法不知道你哪里看到的,不过显然不对吧?比如一个表达式expr: a+b*c+d,显然先是因为*(乘法)优先级高于+(加法),于是先评估b和c,然后才考虑+的左结合性先评估a,然后d的。
第二句彻底没看懂... 中括号里的“只有”是啥意思,左结合的操作符可不只这么几个。
第三句你怎么定义“副作用”的?后置++的作用时间我是不太清楚怎么规定的,但前置++的意思清楚明白,就是执行一个mov一个add,有什么问题?而且是在考虑运算符优先级的情况下立即执行的。
[解决办法]
副作用立即体现的依据是什么 ?
编译运行的结果吗?
补课去
想了一会儿,你是对的。
优先级 和 求值顺序 以及 副作用的生效都没有关系
前置++的即时效果可以从运算符优先级角度来考虑。
求值顺序只取决于 运算符本身[只有 ,?:
[解决办法]
&&确定求值顺序从左到右]
副作用只保证在到达序列点之前完成
优先级 只决定 运算符 和 哪些子表达式结合
看的不是很懂,
运算符优先级在一个包含多个运算符的表达式中和求值顺序怎么会没有关系呢?这说法不知道你哪里看到的,不过显然不对吧?比如一个表达式expr: a+b*c+d,显然先是因为*(乘法)优先级高于+(加法),于是先评估b和c,然后才考虑+的左结合性先评估a,然后d的。
第二句彻底没看懂... 中括号里的“只有”是啥意思,左结合的操作符可不只这么几个。
第三句你怎么定义“副作用”的?后置++的作用时间我是不太清楚怎么规定的,但前置++的意思清楚明白,就是执行一个mov一个add,有什么问题?而且是在考虑运算符优先级的情况下立即执行的。
我想明白了
++a * a
可以看作有2个子表达式,分别是 ++a 和 a ,对这2个表达式的求值顺序,c++没有规定
如果自己作一个简单的计算器
完全可以先求a的值,然后把这个值push进栈,再求++a的值,push进栈,碰到*再把2个操作数pop出来,求值。
如果先求++a表达式的值,push,再push a,那么最终的值和上面的结果不同。
2种做法都是可以的。
++的优先级比*高,只决定了如何划分子表达式,而不决定先求那个表达式。
所以 ++a * a也是未定义的。
个人的理解,说的不对还请指教。
[解决办法]
优先级 和 求值顺序 以及 副作用的生效都没有关系
前置++的即时效果可以从运算符优先级角度来考虑。
求值顺序只取决于 运算符本身[只有 ,?:
[解决办法]
&&确定求值顺序从左到右]
副作用只保证在到达序列点之前完成
优先级 只决定 运算符 和 哪些子表达式结合
看的不是很懂,
运算符优先级在一个包含多个运算符的表达式中和求值顺序怎么会没有关系呢?这说法不知道你哪里看到的,不过显然不对吧?比如一个表达式expr: a+b*c+d,显然先是因为*(乘法)优先级高于+(加法),于是先评估b和c,然后才考虑+的左结合性先评估a,然后d的。
第二句彻底没看懂... 中括号里的“只有”是啥意思,左结合的操作符可不只这么几个。
第三句你怎么定义“副作用”的?后置++的作用时间我是不太清楚怎么规定的,但前置++的意思清楚明白,就是执行一个mov一个add,有什么问题?而且是在考虑运算符优先级的情况下立即执行的。
刚去补了下课,关于表达式是搞错了许多东西。
一部分对我产生误导的因素是我用编译器debug汇编对于a+b*c+d的处理过程,当a,b,c,d都是int的情况下,是先处理了b*c的结果,即下面这段:
n = a+b*c+d;
00CE38BA mov eax,dword ptr [b]
00CE38BD imul eax,dword ptr [c]
00CE38C1 add eax,dword ptr [a]
00CE38C4 add eax,dword ptr [d]
00CE38C7 mov dword ptr [n],eax
于是臆断是运算符优先级的关系。
后来我用这个例子再去验证了一下:
template <int N> int foo()
{
cout << "Call foo<" << N << ">()" << endl;
return N;
}
int main()
{
int n = foo<1>()+foo<2>()*foo<3>()+foo<4>();
return 0;
}
得到:
n = foo<1>()+foo<2>()*foo<3>()+foo<4>();
0090366A call foo<1> (09015C3h)
0090366F mov esi,eax
00903671 call foo<2> (09015D2h)
00903676 mov edi,eax
00903678 call foo<3> (09015AAh)
0090367D imul edi,eax
00903680 add esi,edi
00903682 call foo<4> (09015A5h)
00903687 add esi,eax
00903689 mov dword ptr [n],esi
所以发现并不是我一直以来认为的那样。具体因由置顶帖已经写的十分清楚了,也就不多说了。
于是优先级、结合性我想是应该看成是词法/语法分析层面的东西,以目前资料来看,后端的计算过程一部分有对应标准,一部分则没有,具体哪些定义哪些未定义也不容易搞清楚,总之尽量避免编写依赖计算顺序的表达式会更加安全。
------解决方案--------------------
副作用立即体现的依据是什么 ?
编译运行的结果吗?
补课去
想了一会儿,你是对的。优先级 和 求值顺序 以及 副作用的生效都没有关系
前置++的即时效果可以从运算符优先级角度来考虑。
求值顺序只取决于 运算符本身[只有 ,?:
[解决办法]
&&确定求值顺序从左到右]
副作用只保证在到达序列点之前完成
优先级 只决定 运算符 和 哪些子表达式结合
看的不是很懂,
运算符优先级在一个包含多个运算符的表达式中和求值顺序怎么会没有关系呢?这说法不知道你哪里看到的,不过显然不对吧?比如一个表达式expr: a+b*c+d,显然先是因为*(乘法)优先级高于+(加法),于是先评估b和c,然后才考虑+的左结合性先评估a,然后d的。
第二句彻底没看懂... 中括号里的“只有”是啥意思,左结合的操作符可不只这么几个。
第三句你怎么定义“副作用”的?后置++的作用时间我是不太清楚怎么规定的,但前置++的意思清楚明白,就是执行一个mov一个add,有什么问题?而且是在考虑运算符优先级的情况下立即执行的。
我想明白了
++a * a
可以看作有2个子表达式,分别是 ++a 和 a ,对这2个表达式的求值顺序,c++没有规定
如果自己作一个简单的计算器
完全可以先求a的值,然后把这个值push进栈,再求++a的值,push进栈,碰到*再把2个操作数pop出来,求值。
如果先求++a表达式的值,push,再push a,那么最终的值和上面的结果不同。
2种做法都是可以的。
++的优先级比*高,只决定了如何划分子表达式,而不决定先求那个表达式。
所以 ++a * a也是未定义的。
个人的理解,说的不对还请指教。
帮到自己纠正了长期的错误,感谢二位。
[解决办法]
加一个括号一切都可以的,这样写完全难为自己了
[解决办法]
加括号有什么用,未定义行为,参看置顶贴
这种垃圾代码不要写,那这个做面试题的公司,如果不是考未定义行为,这种公司最好不去
[解决办法]
不要写这么无聊没有意义的表达式,多写几句占不了你多大的内存空间,程序也好理解。
[解决办法]
自己写代码搞不清楚算符优先级请多加括号。
看别人代码搞不清楚算符优先级能调试的话请单步调试对应汇编。
看别人代码搞不清楚算符优先级不能调试的话想办法照写一小段孤立的可调试的代码然后单步调试对应汇编。
看别人代码搞不清楚算符优先级不能调试的话且没有办法照写一小段孤立的可调试的代码然后单步调试对应汇编的话只能参考算符优先级表猜了(提醒:并不能100%猜对)。
//C++ Operators
// Operators specify an evaluation to be performed on one of the following:
// One operand (unary operator)
// Two operands (binary operator)
// Three operands (ternary operator)
// The C++ language includes all C operators and adds several new operators.
// Table 1.1 lists the operators available in Microsoft C++.
// Operators follow a strict precedence which defines the evaluation order of
//expressions containing these operators. Operators associate with either the
//expression on their left or the expression on their right; this is called
//“associativity.” Operators in the same group have equal precedence and are
//evaluated left to right in an expression unless explicitly forced by a pair of
//parentheses, ( ).
// Table 1.1 shows the precedence and associativity of C++ operators
// (from highest to lowest precedence).
//
//Table 1.1 C++ Operator Precedence and Associativity
// The highest precedence level is at the top of the table.
//+------------------+-----------------------------------------+---------------+
//
[解决办法]
Operator
[解决办法]
Name or Meaning
[解决办法]
Associativity
[解决办法]
//+------------------+-----------------------------------------+---------------+
//
[解决办法]
::
[解决办法]
Scope resolution
[解决办法]
None
[解决办法]
//
[解决办法]
::
[解决办法]
Global
[解决办法]
None
[解决办法]
//
[解决办法]
[ ]
[解决办法]
Array subscript
[解决办法]
Left to right
[解决办法]
//
[解决办法]
( )
[解决办法]
Function call
[解决办法]
Left to right
[解决办法]
//
[解决办法]
( )
[解决办法]
Conversion
[解决办法]
None
[解决办法]
//
[解决办法]
.
[解决办法]
Member selection (object)
[解决办法]
Left to right
[解决办法]
//
[解决办法]
->
[解决办法]
Member selection (pointer)
[解决办法]
Left to right
[解决办法]
//
[解决办法]
++
[解决办法]
Postfix increment
[解决办法]
None
[解决办法]
//
[解决办法]
--
[解决办法]
Postfix decrement
[解决办法]
None
[解决办法]
//
[解决办法]
new
[解决办法]
Allocate object
[解决办法]
None
------解决方案--------------------
//
[解决办法]
delete
[解决办法]
Deallocate object
[解决办法]
None
[解决办法]
//
[解决办法]
delete[ ]
[解决办法]
Deallocate object
[解决办法]
None
[解决办法]
//
[解决办法]
++
[解决办法]
Prefix increment
[解决办法]
None
[解决办法]
//
[解决办法]
--
[解决办法]
Prefix decrement
[解决办法]
None
[解决办法]
//
------解决方案--------------------
*
[解决办法]
Dereference
[解决办法]
None
[解决办法]
//
[解决办法]
&
[解决办法]
Address-of
[解决办法]
None
[解决办法]
//
[解决办法]
+
[解决办法]
Unary plus
[解决办法]
None
[解决办法]
//
[解决办法]
-
[解决办法]
Arithmetic negation (unary)
[解决办法]
None
[解决办法]
//
------解决方案--------------------
!
[解决办法]
Logical NOT
[解决办法]
None
[解决办法]
//
[解决办法]
~
[解决办法]
Bitwise complement
[解决办法]
None
[解决办法]
//
[解决办法]
sizeof
[解决办法]
Size of object
[解决办法]
None
[解决办法]
//
[解决办法]
sizeof ( )
[解决办法]
Size of type
[解决办法]
None
[解决办法]
//
[解决办法]
typeid( )
[解决办法]
type name
[解决办法]
None
[解决办法]
//
[解决办法]
(type)
[解决办法]
Type cast (conversion)
[解决办法]
Right to left
[解决办法]
//
[解决办法]
const_cast
[解决办法]
Type cast (conversion)
[解决办法]
None
[解决办法]
//
[解决办法]
dynamic_cast
[解决办法]
Type cast (conversion)
[解决办法]
None
[解决办法]
//
[解决办法]
reinterpret_cast
[解决办法]
Type cast (conversion)
------解决方案--------------------
None
[解决办法]
//
[解决办法]
static_cast
[解决办法]
Type cast (conversion)
[解决办法]
None
[解决办法]
//
[解决办法]
.*
[解决办法]
Apply pointer to class member (objects)
[解决办法]
Left to right
[解决办法]
//
[解决办法]
->*
[解决办法]
Dereference pointer to class member
[解决办法]
Left to right
[解决办法]
//
[解决办法]
*
[解决办法]
Multiplication
[解决办法]
Left to right
[解决办法]
//
[解决办法]
/
[解决办法]
Division
[解决办法]
Left to right
[解决办法]
//
[解决办法]
%
[解决办法]
Remainder (modulus)
[解决办法]
Left to right
[解决办法]
//
[解决办法]
+
[解决办法]
Addition
[解决办法]
Left to right
[解决办法]
//
[解决办法]
-
[解决办法]
Subtraction
[解决办法]
Left to right
[解决办法]
//
[解决办法]
<<
------解决方案--------------------
Left shift
[解决办法]
Left to right
[解决办法]
//
[解决办法]
>>
[解决办法]
Right shift
[解决办法]
Left to right
[解决办法]
//
[解决办法]
<
[解决办法]
Less than
[解决办法]
Left to right
[解决办法]
//
[解决办法]
>
[解决办法]
Greater than
[解决办法]
Left to right
[解决办法]
//
[解决办法]
<=
[解决办法]
Less than or equal to
[解决办法]
Left to right
[解决办法]
//
[解决办法]
>=
[解决办法]
Greater than or equal to
[解决办法]
Left to right
[解决办法]
//
[解决办法]
==
[解决办法]
Equality
[解决办法]
Left to right
[解决办法]
//
[解决办法]
!=
[解决办法]
Inequality
[解决办法]
Left to right
[解决办法]
//
[解决办法]
&
[解决办法]
Bitwise AND
[解决办法]
Left to right
[解决办法]
//
[解决办法]
^
[解决办法]
Bitwise exclusive OR
[解决办法]
Left to right
[解决办法]
//
[解决办法]
[解决办法]
[解决办法]
Bitwise OR
[解决办法]
Left to right
[解决办法]
//
[解决办法]
&&
[解决办法]
Logical AND
[解决办法]
Left to right
[解决办法]
//
[解决办法]
[解决办法]
[解决办法]
Logical OR
------解决方案--------------------
Left to right
[解决办法]
//
[解决办法]
e1?e2:e3
[解决办法]
Conditional
[解决办法]
Right to left
[解决办法]
//
[解决办法]
=
[解决办法]
Assignment
[解决办法]
Right to left
[解决办法]
//
[解决办法]
*=
[解决办法]
Multiplication assignment
[解决办法]
Right to left
[解决办法]
//
[解决办法]
/=
[解决办法]
Division assignment
[解决办法]
Right to left
[解决办法]
//
------解决方案--------------------
%=
[解决办法]
Modulus assignment
[解决办法]
Right to left
[解决办法]
//
[解决办法]
+=
[解决办法]
Addition assignment
[解决办法]
Right to left
[解决办法]
//
[解决办法]
-=
[解决办法]
Subtraction assignment
[解决办法]
Right to left
[解决办法]
//
[解决办法]
<<=
[解决办法]
Left-shift assignment
[解决办法]
Right to left
[解决办法]
//
[解决办法]
>>=
[解决办法]
Right-shift assignment
[解决办法]
Right to left
[解决办法]
//
[解决办法]
&=
[解决办法]
Bitwise AND assignment
[解决办法]
Right to left
[解决办法]
//
[解决办法]
[解决办法]
=
[解决办法]
Bitwise inclusive OR assignment
[解决办法]
Right to left
[解决办法]
//
[解决办法]
^=
[解决办法]
Bitwise exclusive OR assignment
[解决办法]
Right to left
[解决办法]
//
[解决办法]
,
[解决办法]
Comma
[解决办法]
Left to right
------解决方案--------------------
//+------------------+-----------------------------------------+---------------+
不要迷信书、考题、老师、回帖;
要迷信CPU、编译器、调试器、运行结果。
并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。
任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实!
有人说一套做一套,你相信他说的还是相信他做的?
其实严格来说这个世界上古往今来所有人都是说一套做一套,不是吗?
不要写连自己也预测不了结果的代码!
[解决办法]
是3*4=12啊,...楼主你好调皮.
[解决办法]
无聊的题目
直接忽视
[解决办法]
[解决办法]
int n,a=3;
n=a++*a;
System.out.println(n);
JAVA 的结果是 12.亲测
[解决办法]
int n,a=3;
n=a++*a;
System.out.println(n);
JAVA 的结果是 12.亲测
[解决办法]
又是这种UB表达方式。
C标准不对此类表达式的结果做任何保证。
所以是多少都没有错(也不代表正确),编译器也不对这类UB表达式的结果负责。
在一个表达式中(包括算数式和作为多参数函数的参数),同一个变量从表达式语义上如果对自身进行修改,则作为输入不得出现多于一次。否则就属于这种情况(结果未定义)。
比如
(i++)+(i++);(i修改自身且作为输入值出现了两次)
(++i)+(++i);(同上)
你这种表达式 (x修改自身,作为输入值出现了不止一次,一次是x++,另一次是作为x自加的输入)
i=i++;(i修改自身,作为输入值出现了不止一次,一次是i++,另一次是作为赋值的输入)
func(i++, i);(i修改自身,且作为函数参数出现了不止一次)
都属于此类;
而
(i+1)+(j++);(j修改自己,但只出现了一次)
i+=(i+1);(i出现了多次,但是仅修改自身一次:作为赋值的输入)
则不属于此类,而属于正常的表达式。
[解决办法]
这种代码实用性极低,千万别这么做
[解决办法]
这个可以用顺序点来解释
int b = 2;
b = b++ + b++;
printf("%d ",b);
结果是6什么呢?b = b++ + b++,执行时从内存中取值b = 2,但是此时只是记录了b会+1,再加b++,从内存中取值还是2到了顺序点b = 4,但是前面说过记录了两次b++,在到达顺序点的时候会会改变内存中的值,所以需加2,故b = 6
你的也是顺序点之前的a++结果不会反馈到内存中,所以结果是3*3
顺序点发生位置:
分号;
未重载的逗号运算符的左操作数赋值之后(即“,”处);
未重载的”
[解决办法]
”运算符的左操作数赋值之后(即“
[解决办法]
”处);
未重载的“&&”运算符的左操作数赋值之后(即"&&"处);
三元运算符“? : ”的左操作数赋值之后(即“?”处);
在函数所有参数赋值之后但在函数第一条语句执行之前;
在函数返回值已拷贝给调用者之后但在该函数之外的代码执行之前;
另外你那个函数写法不知道哪里学来的了,有问题int main(int argc,char *[argv])