变量的值随着不相关的代码而变化
Value of variable changes with unrelated code
我几个小时以来一直在与错误作斗争。基本上,我对 main.c 中的 uint64_t 数组执行一些简单的位操作(无函数调用)。它在调试中的 gcc (Ubuntu)、MSVS2019 (Windows 10) 上正常工作, 但在 Release 中不正常。但是我的目标架构是 x64/Windows,所以我需要让它与 MSVS2019/Release 一起正常工作。除此之外,我很好奇问题的原因是什么。 None 个编译器显示错误或警告。
现在,一旦我将一个完全不相关的命令添加到循环中(注释 printf()
),它就会正常工作。
...
int q = 5;
uint64_t a[32] = { 0 };
// a[] is filled with data
for (int i = 0; i < 32; i++) {
a[q] = (a[q] << 2) | 8;
// printf("%i \n", i); // that's the line which makes it work
}
...
最初我认为我在 for()
循环之前的某个地方弄乱了堆栈,但我检查了很多次...没问题!
- 检查所有使用的变量是否已初始化
- 没有指针 returns 局部变量(在范围内)
- 数组索引(读取和写入)都在声明限制内(在范围内)
所有 Google/SE post 都解释了上述一些原因的主题 UB,但其中 none 适用于我的代码。此外,它在 MSVS2019/Debug 中有效,而 gcc 显示代码有效。
我错过了什么?
---更新(2021 年 8 月 24 日 12:00)---
我完全卡住了,因为添加 printf()
修改了结果并且 MSVS/Debug 有效。那么我该如何检查变量呢?!
@Lev M 在显示的 for()
循环前后有相当多的计算。这就是为什么我跳过了大部分代码,只展示了我可以影响代码正常工作的片段。我知道最终的结果应该是什么(就是一个uint64_t),而MSVS的Release版本是错误的。我还检查了 w/o for()
循环。它没有优化“离开”。如果我完全忽略它,结果又会不同。
@tstanisl 这只是一个 uint64_t 数字的问题。我知道输入A应该输出B.
@Steve Summit 这就是我 posted(有点绝望)的原因。我检查了所有方向,尽可能多地隔离代码,但是……没有未初始化的变量或数组越界。快把我逼疯了。
@Craig Estey 不幸的是代码非常广泛。我想知道......错误是否也出现在代码的一部分而不是 运行?
@Eric Postpischil 同意!
@Nate Eldredge 我在 valgrind 上测试过(见下文)。
...
==13997== HEAP SUMMARY:
==13997== in use at exit: 0 bytes in 0 blocks
==13997== total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==13997==
==13997== All heap blocks were freed -- no leaks are possible
==13997==
==13997== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
--- 更新(2021 年 8 月 24 日 18:00)---
我找到了问题的原因(经过无数次尝试和错误),但还没有解决方案。我post更多的代码。
...
int q = 5;
uint64_t a[32] = { 0 };
// a[] is filled with data
for (int i = 0; i < 32; i++) {
a[q] = (a[q] << 2) | 8;
// printf("%i \n", i); // that's the line which makes it work
}
for (int i = 0; i < 32; i++) {
a[q] = (a[q] << 3) | 3;
}
...
事实上,MSVS/Release 编译器是这样做的:
...
int q = 5;
uint64_t a[32] = { 0 };
// a[] is filled with data
for (int i = 0; i < 32; i++) {
a[q] = (a[q] << 2) | 8;
a[q] = (a[q] << 3) | 3;
}
...
...这是不一样的。没见过这种东西!
如何强制编译器将 2 个 for()
循环分开?
总结:
MSVS/Release(默认解决方案属性)优化将更改此代码...
// Code 1
...
int q = 5;
uint64_t a[32];
// a[] is filled with data
for (int i = 0; i < 32; i++) {
a[q] = (a[q] << 2) | 8;
// printf("%i \n", i); // that's the line which makes it work
}
for (int i = 0; i < 32; i++) {
a[q] = (a[q] << 3) | 3;
}
...
...进入下一个,和...
不一样
// Code 2
...
int q = 5;
uint64_t a[32];
// a[] is filled with data
for (int i = 0; i < 32; i++) {
a[q] = (a[q] << 2) | 8;
a[q] = (a[q] << 3) | 3;
}
...
以上摘录略有简化,因为不限于常量 32 循环,而是保持可变 (% 8)。因此 64 位常量不能像用户评论的那样使用。
发现:
MSVS/Release - 失败
MSVS/Debug - 有效
gcc/Release - 有效
gcc/Debug - 有效
MSVS/Release 优化将两个 for()
循环(代码 1)合并为一个 for()
循环(代码 2)。
修复:
评论 printf()
提供了一个人为的修复,因为编译器看到了打印中间结果的要求。
另一种解决方法是对 a[]
.
使用类型限定符 volatile
问题的根源在于,MSVS 优化没有考虑索引 q
在两个循环中保持相同,这意味着第一个循环需要在第二个循环开始之前完成。
我几个小时以来一直在与错误作斗争。基本上,我对 main.c 中的 uint64_t 数组执行一些简单的位操作(无函数调用)。它在调试中的 gcc (Ubuntu)、MSVS2019 (Windows 10) 上正常工作, 但在 Release 中不正常。但是我的目标架构是 x64/Windows,所以我需要让它与 MSVS2019/Release 一起正常工作。除此之外,我很好奇问题的原因是什么。 None 个编译器显示错误或警告。
现在,一旦我将一个完全不相关的命令添加到循环中(注释 printf()
),它就会正常工作。
...
int q = 5;
uint64_t a[32] = { 0 };
// a[] is filled with data
for (int i = 0; i < 32; i++) {
a[q] = (a[q] << 2) | 8;
// printf("%i \n", i); // that's the line which makes it work
}
...
最初我认为我在 for()
循环之前的某个地方弄乱了堆栈,但我检查了很多次...没问题!
- 检查所有使用的变量是否已初始化
- 没有指针 returns 局部变量(在范围内)
- 数组索引(读取和写入)都在声明限制内(在范围内)
所有 Google/SE post 都解释了上述一些原因的主题 UB,但其中 none 适用于我的代码。此外,它在 MSVS2019/Debug 中有效,而 gcc 显示代码有效。
我错过了什么?
---更新(2021 年 8 月 24 日 12:00)---
我完全卡住了,因为添加 printf()
修改了结果并且 MSVS/Debug 有效。那么我该如何检查变量呢?!
@Lev M 在显示的 for()
循环前后有相当多的计算。这就是为什么我跳过了大部分代码,只展示了我可以影响代码正常工作的片段。我知道最终的结果应该是什么(就是一个uint64_t),而MSVS的Release版本是错误的。我还检查了 w/o for()
循环。它没有优化“离开”。如果我完全忽略它,结果又会不同。
@tstanisl 这只是一个 uint64_t 数字的问题。我知道输入A应该输出B.
@Steve Summit 这就是我 posted(有点绝望)的原因。我检查了所有方向,尽可能多地隔离代码,但是……没有未初始化的变量或数组越界。快把我逼疯了。
@Craig Estey 不幸的是代码非常广泛。我想知道......错误是否也出现在代码的一部分而不是 运行?
@Eric Postpischil 同意!
@Nate Eldredge 我在 valgrind 上测试过(见下文)。
...
==13997== HEAP SUMMARY:
==13997== in use at exit: 0 bytes in 0 blocks
==13997== total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==13997==
==13997== All heap blocks were freed -- no leaks are possible
==13997==
==13997== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
--- 更新(2021 年 8 月 24 日 18:00)---
我找到了问题的原因(经过无数次尝试和错误),但还没有解决方案。我post更多的代码。
...
int q = 5;
uint64_t a[32] = { 0 };
// a[] is filled with data
for (int i = 0; i < 32; i++) {
a[q] = (a[q] << 2) | 8;
// printf("%i \n", i); // that's the line which makes it work
}
for (int i = 0; i < 32; i++) {
a[q] = (a[q] << 3) | 3;
}
...
事实上,MSVS/Release 编译器是这样做的:
...
int q = 5;
uint64_t a[32] = { 0 };
// a[] is filled with data
for (int i = 0; i < 32; i++) {
a[q] = (a[q] << 2) | 8;
a[q] = (a[q] << 3) | 3;
}
...
...这是不一样的。没见过这种东西!
如何强制编译器将 2 个 for()
循环分开?
总结:
MSVS/Release(默认解决方案属性)优化将更改此代码...
// Code 1
...
int q = 5;
uint64_t a[32];
// a[] is filled with data
for (int i = 0; i < 32; i++) {
a[q] = (a[q] << 2) | 8;
// printf("%i \n", i); // that's the line which makes it work
}
for (int i = 0; i < 32; i++) {
a[q] = (a[q] << 3) | 3;
}
...
...进入下一个,和...
不一样// Code 2
...
int q = 5;
uint64_t a[32];
// a[] is filled with data
for (int i = 0; i < 32; i++) {
a[q] = (a[q] << 2) | 8;
a[q] = (a[q] << 3) | 3;
}
...
以上摘录略有简化,因为不限于常量 32 循环,而是保持可变 (% 8)。因此 64 位常量不能像用户评论的那样使用。
发现:
MSVS/Release - 失败
MSVS/Debug - 有效
gcc/Release - 有效
gcc/Debug - 有效
MSVS/Release 优化将两个 for()
循环(代码 1)合并为一个 for()
循环(代码 2)。
修复:
评论 printf()
提供了一个人为的修复,因为编译器看到了打印中间结果的要求。
另一种解决方法是对 a[]
.
volatile
问题的根源在于,MSVS 优化没有考虑索引 q
在两个循环中保持相同,这意味着第一个循环需要在第二个循环开始之前完成。