深入探讨C语言中printf函数与自加自减的问题!

2024-12-14 12:55:58
推荐回答(5个)
回答1:

LS注意,不是“没有明确规定”,而是明确规定LZ的这种用法会导致未定义行为(undefined behavior)。未定义行为基于用户对语言或数据的错误使用,具有未定义行为的程序的行为完全不可靠(就算是导致编译器崩溃也是自找的——虽然不会有厂商会让编译器表现出这种不可靠性)。被ISO C/C++定义为未定义行为的用法,可以认为不论给出什么明确的结果都是愚蠢的,于是标准不对其行为作出任何保证,用户也不应该指望有任何结果。
会出现这种问题又不说清楚undefined behavior的教材根本就是不合格。
对于这里的用法为什么属于未定义行为的严格依据(LZ需要补关于序列点的知识):
ISO C99:
3.4.3
1 undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data,
for which this International Standard imposes no requirements
2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).
3 EXAMPLE An example of undefined behavior is the behavior on integer overflow.
4. Conformance
2 If a ‘‘shall’’ or ‘‘shall not’’ requirement that appears outside of a constraint is violated, the
behavior is undefined. Undefined behavior is otherwise indicated in this International
Standard by the words ‘‘undefined behavior’’ or by the omission of any explicit definition
of behavior. There is no difference in emphasis among these three; they all describe
‘‘behavior that is undefined’’.
5.2.1.3
2 Accessing a volatile object, modifying an object, modifying a file, or calling a function
that does any of those operations are all side effects,11) which are changes in the state of
the execution environment. Evaluation of an expression may produce side effects. At
certain specified points in the execution sequence called sequence points, all side effects
of previous evaluations shall be complete and no side effects of subsequent evaluations
shall have taken place. (A summary of the sequence points is given in annex C.)
6.5
2 Between the previous and next sequence point an object shall have its stored value
modified at most once by the evaluation of an expression. Furthermore, the prior value
shall be read only to determine the value to be stored.
题外话,关于表达式求值顺序是未确定行为(unspecified behavior),可以是正确的行为,但具体行为取决于实现(这里就是编译器),不能与此混淆。而入栈出栈顺序涉及函数的调用约定(calling convention),这并不是在语言的规范中决定的,需要找其它的(例如硬件架构)编程模型规范(包括ABI)定义——事实上,C语言根本就没明确函数必需使用栈实现(尽管所有的主流硬件架构乃至虚拟机实现类C语言都使用栈来表达内建静态函数的行为)。 至于结合性,则彻底可以从表达式的语法规则(一大长串,不引用了,太罗嗦)完全、明确地推导出来,只是用来说明哪个操作数进行哪个运算,和具体求值的顺序之类更是没关系。

回答2:

对于你的问题的回答如下:(都是在linux 下运行后的答案)
#include
main()
{
int i=1;
printf("%d %d\n",i++,++i);
}
这道题我用VC++6.0编译结果是2 2 ,我的思路是从printf函数的参数自右向左计算,// 就是因为是从右到左才是2 2.因为首先++i,则此时i的值为2, 然后计算i++,因为是在下一次运行i++时i值为3,此时应该仍为2;
因此对于此题运行结果应为2 2 ,而不是1 3;
#include
int main()
{
int a[5] = {1,2,3,4,5};
int* p = a;
int i;
printf("%d %d \n", (*(++p))++, *p++);
for(i=0;i<5;i++)
printf("%4d\n", a[i]);
return 0;
}
我运行的结果是 3 1
1
2
4
4
5
即,按照你的说法是后面一种 是3 1 4;我的理解是
(*(++p))++, *p++); 从右到左:首先执行*p++,就是先执行*p, 然后p++,所以得1,然后P指向了a[1],接着*(++p)即p指向了啊【2】,求值得3,然后a【2】++,即a[2]自加,但是输出时并未加,此时输出值为3,最后执行输出a[2],即为4了,因此答案为 3 1 4;
另外我在vc6.0上运行答案也是 3 1 4啊 ,不是你说的 什么 2 1 4????

#include
main()
{
int p,q;
int x=8,y=8;
p=(x++)+(x++)+(x++);
q=(++y)+(++y)+(++y);
printf("%d %d %d %d\n",p,q,x,y);
}

至于这道题,我在linux及vc6.0中都是 24 31 11 11
分析
引用以上一人回答:
我也只能作出很简陋的说明。
在p=(x++)+(x++)+(x++);中在进行加法运算的时候,由于尾++的运算优先级最低,所以x的值总是8,所以p=3*8=24;
在q=(++y)+(++y)+(++y);中由于在计算第一个加法的时候,头++的优先级最高,所以相当于10+10=20,然后进行第二个加法的运算,得到20+11=31

其实我觉得你不必那么纠结于此了,还是好好看看别的吧。
哈哈 ,答了这么多,你可以把分给我吗?

回答3:

#include
main()
{
int p,q;
int x=8,y=8;
p=(x++)+(x++)+(x++);
q=(++y)+(++y)+(++y);
printf("%d %d %d %d\n",p,q,x,y);
}
个人认为:p=(x++)+(x++)+(x++);中:x++这也操作是在该语句结束之后进行的,也就是3个x++都是在p=x+x+x之后执行。依据是X++是先用X的值,等语句结束后才自加,就像j=i++;是先j=i;再i++的。
q=(++y)+(++y)+(++y);
同理,如果j=++i;是先i++;再j=i的。所以其实q=(++y)+(++y)+(++y);
是q=9+10+11;第一个Y++之后y=9,第二个时y=10,第三个y=11;
同样的,个人认为:
#include
main()
{
int i=1;
printf("%d %d\n",i++,++i);
}
中,执行printf("%d %d\n",i++,++i);
时,i++实在printf("%d %d\n",i++,++i);
之后执行,而++i是在printf("%d %d\n",i++,++i);
打印之前执行,i自加完成后i=2。然后打印,再i++。
至于:
#include
int main()
{
int a[5] = {1,2,3,4,5};
int* p = a;
printf("%d %d \n", (*(++p))++, *p++);
printf("%d\n", a[2]);
return 0;
}
这一个,我就不太明白了,不过可以肯定,printf("%d\n", a[2])绝对是3,因为自始自终都没对a[2]进行过任何操作。

以上是我个人的理解。最近发现VC++6.0总有些出人意料的地方。昨天还被一个题给难倒了。

回答4:

我也只能作出很简陋的说明。
在p=(x++)+(x++)+(x++);中在进行加法运算的时候,由于尾++的运算优先级最低,所以x的值总是8,所以p=3*8=24;
在q=(++y)+(++y)+(++y);中由于在计算第一个加法的时候,头++的优先级最高,所以相当于10+10=20,然后进行第二个加法的运算,得到20+11=31

回答5:

不要探讨多重赋值的问题, 自加自减都有赋值动作,
这类的在标准中属于未定义行为, 所以不同的编译器结果不同, 具体要在编译器试试,
编写不依赖编译器的代码时不要用这类未定义行为
至于遇到这类脑残的题时,你可以在TC里试试,或着定义变量时增加volatile关键字