首页 > 解决方案 > 为什么 GCC 会优化重要的赋值,使其变成不同的程序行为?

问题描述

因此,我正在为我的控制台解释器制作一个 C 程序来检测它是哪种类型,然后将其返回typeret并将指针存储到 void 指针retpoint中。现在,我要问的不是代码中的内容,而是为什么GCC 在我的函数中优化这个赋值eval。我想知道为什么 GCC 会优化一些实际上很重要的东西。

int eval(wchar_t *expr) {
    // ....
    
    if (expr[0] == L'\'' && expr[csize - 1] == L'\'') { // <-- Ignore this.
        expr[csize - 1] = 0;
        typeret         = 0;
        
        retpoint    = (expr + 1);
    } else if (strin(L".", expr)) { // <-- Ignore this.
        typeret     = 2;
    } else {
        typeret     = 1;
        tvar        = _wtoi(expr); // <-- Here is the problem.
        retpoint    = &tvar;
    }
    
    return 0;
}

当我使用 GCC 命令编译代码时,gcc -Wsomewarning -o cim.exe cim.c -Os -s -ffunction-sections -fdata-sections -O3未检测到分配(即使没有-ffunction-sectionsand -fdata-sections,也未检测到分配)。

但是,当我更改涉及 around tvarOR around的代码时_wtoi,分配不会被忽略。这是不忽略分配的示例代码。

int eval(wchar_t *expr) {
    int
        tvar; // If I set tvar to N, then the assignment is still ignored (making tvar = N).

    // ....

    if (expr[0] == L'\'' && expr[csize - 1] == L'\'') { // <-- Ignore this.
        expr[csize - 1] = 0;
        typeret         = 0;

        retpoint    = (expr + 1);
    } else if (strin(L".", expr)) { // <-- Ignore this.
        typeret     = 2;
    } else {
        typeret     = 1;
        tvar        = _wtoi(expr); // <-- Here is the problem.
        wprintf(L"%d", tvar); // or wprintf(L"%d", _wtoi(expr);
        retpoint    = &tvar;
    }

    return 0;
}

现在,当然,如果我使用gcc -o cim.exe cim.c(没有优化),第一个代码可以正常工作;但是,尺寸是优化后的四倍。

笔记

我确信这个程序中没有未定义的行为,并且我确信我已经正确地给出了数据。

和是全局数据,而typeret是局部变量。retpointtvar

编辑

我忘了提到我在 Windows 10 中使用了 GCC 版本 (tdm64-1) 4.9.2。

提前致谢。

标签: cgccoptimizationvariable-assignment

解决方案


在您的第一个示例中,在赋值和函数返回之间tvar,既不直接也不通过指针访问变量,之后由于它是本地的,所以生命周期结束。而且您只能在其生命周期内访问。因此编译器完全有权优化分配,因为它不会对正确的程序产生任何影响。retpointtvar = _wtoi(expr);evaltvartvar

如果它确实改变了你的程序的行为,那么你可能在某处有未定义的行为。也许您在返回retpoint后取消引用指针。eval那绝对是UB。一般来说,设置一个全局指针指向一个局部变量,并在函数返回后将其保留在那里,是非常可疑的。


推荐阅读