为什么 GCC 优化了重要的分配,使其成为不同的程序行为?
Why did GCC optimizes important assignment, making it into a different program behavior?
所以,我正在为我的控制台解释器制作一个 C 程序来检测它是哪种类型,然后 return 它到 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-sections
和 -fdata-sections
,也未检测到赋值)。
但是,当我更改涉及 tvar
或 _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
和retpoint
是全局数据,而tvar
是局部变量。
编辑
忘了说我在Windows10.
中使用的是GCC版本(tdm64-1)4.9.2
提前致谢。
在您的第一个示例中,在赋值 tvar = _wtoi(expr);
和函数 eval
之间,变量 tvar
未被直接访问,也未通过指针 retpoint
访问returns,然后 tvar
的生命周期结束,因为它是本地的。并且您只能在其生命周期内访问 tvar
。所以编译器完全有权优化分配,因为它不会对正确的程序产生任何影响。
如果它确实改变了你程序的行为,那么你可能在某处有未定义的行为。也许您在 eval
returns 之后取消引用指针 retpoint
。那绝对是UB。一般来说,将全局指针设置为指向局部变量,并在函数之后将其留在那里returns,是非常可疑的。
所以,我正在为我的控制台解释器制作一个 C 程序来检测它是哪种类型,然后 return 它到 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-sections
和 -fdata-sections
,也未检测到赋值)。
但是,当我更改涉及 tvar
或 _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
和retpoint
是全局数据,而tvar
是局部变量。
编辑
忘了说我在Windows10.
中使用的是GCC版本(tdm64-1)4.9.2提前致谢。
在您的第一个示例中,在赋值 tvar = _wtoi(expr);
和函数 eval
之间,变量 tvar
未被直接访问,也未通过指针 retpoint
访问returns,然后 tvar
的生命周期结束,因为它是本地的。并且您只能在其生命周期内访问 tvar
。所以编译器完全有权优化分配,因为它不会对正确的程序产生任何影响。
如果它确实改变了你程序的行为,那么你可能在某处有未定义的行为。也许您在 eval
returns 之后取消引用指针 retpoint
。那绝对是UB。一般来说,将全局指针设置为指向局部变量,并在函数之后将其留在那里returns,是非常可疑的。