当前位置:  开发笔记 > 编程语言 > 正文

后增量运算符行为

如何解决《后增量运算符行为》经验,为你挑选了4个好方法。

这是未指定行为的示例.该标准没有说明应该评估哪些订单参数.这是一个编译器实现决策.编译器可以按任何顺序自由地计算函数的参数.

在这种情况下,它看起来实际上是从右到左处理参数而不是从左到右的预期处理.

一般来说,在参数中做副作用是糟糕的编程习惯.

而不是foo(test ++,test); 你应该写foo(test,test + 1); 试验++;

它在语义上等同于您要完成的任务.

编辑:正如安东尼正确指出的那样,在没有插入序列点的情况下读取和修改单个变量是未定义的.所以在这种情况下,行为确实是未定义的.因此编译器可以自由地生成它想要的任何代码.



1> Benoit..:

这是未指定行为的示例.该标准没有说明应该评估哪些订单参数.这是一个编译器实现决策.编译器可以按任何顺序自由地计算函数的参数.

在这种情况下,它看起来实际上是从右到左处理参数而不是从左到右的预期处理.

一般来说,在参数中做副作用是糟糕的编程习惯.

而不是foo(test ++,test); 你应该写foo(test,test + 1); 试验++;

它在语义上等同于您要完成的任务.

编辑:正如安东尼正确指出的那样,在没有插入序列点的情况下读取和修改单个变量是未定义的.所以在这种情况下,行为确实是未定义的.因此编译器可以自由地生成它想要的任何代码.



2> Anthony Will..:

这不仅仅是未指定的行为,它实际上是未定义的行为.

是的,参数评估的顺序是未指定的,但是在没有插入序列点的情况下读取和修改单个变量是未定义的,除非读取仅用于计算新值.函数参数的评估之间没有序列点,因此f(test,test++)未定义的行为:test正在为一个参数读取并为另一个参数进行修改.如果你将修改移动到一个函数,那么你没关系:

int preincrement(int* p)
{
    return ++(*p);
}

int test;
printf("%d %d\n",preincrement(&test),test);

这是因为在进入和退出时有一个序列点preincrement,因此必须在简单读取之前或之后评估调用.现在订单没有说明.

另请注意,逗号运算符提供了一个序列点,因此

int dummy;
dummy=test++,test;

很好---增量发生在读取之前,因此dummy设置为新值.


你不是指后代吗?或者你的意思是帮助程序员更富有?

3> Mike Thompso..:

我原先说的一切都是错的!在该侧影响被计算的时间点不确定的.如果test是局部变量,Visual C++将在调用foo()之后执行增量,但如果test被声明为static或global,它将在调用foo()之前递增并产生不同的结果,尽管最终值为测试是正确的.

在调用foo()之后,增量应该在单独的语句中完成.即使在C/C++标准中指定了行为,也会令人困惑.你会认为C++编译器会将此标记为潜在错误.

以下是序列点和未指定行为的良好描述.

<----错误的错误开始---->

在调用foo之后执行"test ++"的"++"位.所以你传入(0,0)到foo,而不是(1,0)

这是Visual Studio 2002的汇编程序输出:

mov ecx, DWORD PTR _i$[ebp]
push    ecx
mov edx, DWORD PTR tv66[ebp]
push    edx
call    _foo
add esp, 8
mov eax, DWORD PTR _i$[ebp]
add eax, 1
mov DWORD PTR _i$[ebp], eax

在调用foo()之后完成增量.虽然这种行为是设计的,但它对于随意的读者来说肯定是令人困惑的,应该可以避免.在调用foo()之后,增量应该在单独的语句中完成

<----错误的错误结束---->



4> Benjamin Aut..:

这是"未指定的行为",但在实践中,通过指定C调用堆栈的方式,它几乎总能保证您将其视为0,0而不是1,0.

正如有人指出的那样,VC的汇编器输出首先推送堆栈上最右边的参数.这是在汇编程序中实现C函数调用的方式.这是为了适应C的"无限参数列表"功能.通过按从右到左的顺序推送参数,第一个参数保证是堆栈中的顶部项.

以printf的签名:

int printf(const char *format, ...);

这些椭圆表示未知数量的参数.如果从左到右推送参数,则格式将位于堆栈的底部,我们不知道其大小.

知道在C(和C++)中从左到右处理参数,我们可以确定解析和解释函数调用的最简单方法.到达参数列表的末尾,并开始推送,评估任何复杂的语句.

但是,即使这样也无法保存,因为大多数C编译器都可以选择解析函数"Pascal样式".所有这些意味着函数参数以从左到右的方式被压入堆栈.例如,如果使用Pascal选项编译printf,那么输出很可能是1,0(但是,因为printf使用椭圆,我认为它不能编译为Pascal样式).

推荐阅读
牛尾巴2010
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有