这个邪恶的演员是否被邪恶的编辑器打败了?

时间:2009-04-03 02:20:15

标签: c++

这不是学术代码或假设的问题。最初的问题是将代码从HP11转换为HP1123 Itanium。基本上它归结为HP1123 Itanium上的编译错误。在Windows上进行复制时,我真的很抓头。除了最基本的方面之外我已经删除了......如果按原样运行它,你可能必须按下控制器D退出控制台窗口:

#include "stdafx.h"
#include <iostream>
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    char blah[6];
    const int IAMCONST = 3;
    int *pTOCONST;
    pTOCONST = (int *)  &IAMCONST;
    (*pTOCONST) = 7;
    printf("IAMCONST %d \n",IAMCONST);
    printf("WHATISPOINTEDAT %d \n",(*pTOCONST));
    printf("Address of IAMCONST %x   pTOCONST %x\n",&IAMCONST, (pTOCONST));
    cin >> blah;    
    return 0;
}

这是输出

IAMCONST 3
WHATISPOINTEDAT 7
Address of IAMCONST 35f9f0   pTOCONST 35f9f0

我只能说是什么?这样做是不明确的?对于这样一个简单的例子,这是我看到的最违反直觉的事情。

更新

确实在搜索了一下菜单调试&gt;&gt; Windows&gt;&gt;反汇编具有下面描述的优化。

    printf("IAMCONST %d \n",IAMCONST);
0024360E  mov         esi,esp 
00243610  push        3    
00243612  push        offset string "IAMCONST %d \n" (2458D0h) 
00243617  call        dword ptr [__imp__printf (248338h)] 
0024361D  add         esp,8 
00243620  cmp         esi,esp 
00243622  call        @ILT+325(__RTC_CheckEsp) (24114Ah) 

谢谢大家!

5 个答案:

答案 0 :(得分:24)

看起来编译器正在优化

printf("IAMCONST %d \n",IAMCONST);

printf("IAMCONST %d \n",3);

因为您 IAMCONSTconst int

但是因为你正在使用IAMCONST的地址,所以它必须实际位于堆栈的某个地方,并且const不能强制执行,因此该位置的内存(毕竟*pTOCONST)是可变的。

简而言之:你遗弃了const,不要这样做。可怜,手无寸铁的C ......

附录

使用GCC for x86,-O0(无优化),生成的程序集

main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl   -4(%ecx)
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ecx
    subl    $36, %esp
    movl    $3, -12(%ebp)
    leal    -12(%ebp), %eax
    movl    %eax, -8(%ebp)
    movl    -8(%ebp), %eax
    movl    $7, (%eax)
    movl    -12(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    $.LC0, (%esp)
    call    printf
    movl    -8(%ebp), %eax
    movl    (%eax), %eax
    movl    %eax, 4(%esp)
    movl    $.LC1, (%esp)
    call    printf

从堆栈上的*(bp-12)复制到printf的参数。但是,使用-O1(以及-Os-O2-O3和其他优化级别),

main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl   -4(%ecx)
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ecx
    subl    $20, %esp
    movl    $3, 4(%esp)
    movl    $.LC0, (%esp)
    call    printf
    movl    $7, 4(%esp)
    movl    $.LC1, (%esp)
    call    printf

您可以清楚地看到使用常量3

如果您使用的是Visual Studio的CL.EXE,则/Od会禁用优化。这因编译器而异。

警告C编译器allows C编译器假设任何int *指针的目标永远不会重叠const int的内存位置,所以你真的不应该如果你想要可预测的行为,那么就这样做。

答案 1 :(得分:6)

常量值IAMCONST被内联到printf调用中。

你所做的事情充其量是错误的,并且很可能是C ++标准未定义的。我的猜测是C ++标准让编译器可以自由地内联一个const原语,它是函数声明的本地原语。原因是价值不应该改变。

然后,它是C ++应该和可以是非常不同的单词。

答案 2 :(得分:4)

你很幸运,编译器正在进行优化。另一种处理方法是将const整数放入只读内存中,然后尝试修改该值会导致核心转储。

答案 3 :(得分:2)

通过删除const的转换来写入const对象是未定义的行为 - 所以在你这样做的时候:

(*pTOCONST) = 7;
所有赌注都已关闭。

来自C ++标准7.1.5.1(cv-qualifiers):

  

除了可以修改声明为mutable(7.1.1)的任何类成员之外,任何修改const的尝试都是如此   对象在其生命周期(3.8)中导致未定义的行为。

因此,编译器可以自由地假设IAMCONST的值不会改变,因此它可以优化对实际存储的访问。实际上,如果从不采用const对象的地址,编译器可能会完全取消对象的存储。

另请注意(再次在7.1.5.1中):

  

由积分常量表达式初始化的非易失性const限定的整数或枚举类型的变量可用于积分常量表达式(5.19)。

这意味着IAMCONST可用于编译时常量表达式(即,为枚举或数组大小提供值)。在运行时改变它甚至意味着什么?

答案 4 :(得分:-3)

编译器是否优化无关紧要。你问了麻烦,你很幸运,你自己遇到了麻烦,而不是等待客户报告给你。

“我只能说是什么?这样做是不确定的?对于这样一个简单的例子,这是我见过的最违反直觉的事情。”

如果您真的相信那么您需要切换到您可以理解或改变职业的语言。为了您自己和您的客户,请停止使用C或C ++或C#。

const int IAMCONST = 3;

你说的。

int *pTOCONST;
pTOCONST = (int *)  &IAMCONST;

猜猜为什么编译器抱怨你是否省略了你的邪恶演员。在你撒谎之前,编译器可能已经说出了真相。

“邪恶的演员会被邪恶的编辑器打败吗?”

没有。邪恶的演员自己被打败了。无论你的编译器是否试图告诉你真相,编译器都不是邪恶的。