c - 不相关代码的变量值变化
问题描述
几个小时以来,我一直在与一个错误作斗争。基本上,我对 main.c 中的 uint64_t 数组进行了一些简单的位操作(没有函数调用)。它在 Debug 中的 gcc (Ubuntu)、MSVS2019 (Windows 10) 上正常工作,但在 Release 中不能正常工作。但是我的目标架构是 x64/Windows,所以我需要让它与 MSVS2019/Release 一起正常工作。除此之外,我很好奇问题的原因是什么。没有一个编译器显示错误或警告。
现在,只要我在循环中添加一个完全不相关的命令(注释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()
循环之前的某个地方弄乱了堆栈,但我多次检查它......一切都很好!
- 检查所有使用的变量以进行初始化
- 没有局部变量的指针返回(在范围内)
- 数组索引(读取和写入)都在声明限制(范围内)内
所有 Google/SE 帖子都根据上述一些原因解释了主题 UB,但这些都不适用于我的代码。此外,它在 MSVS2019/Debug 和 gcc 中工作的事实表明代码工作正常。
我想念什么?
--- 更新 (24.08.2021 12:00) ---
我完全被卡住了,因为添加printf()
修改了结果并且 MSVS/Debug 有效。那么我怎样才能检查变量呢?!
for()
@Lev M 在显示的循环之前和之后有很多计算。这就是为什么我跳过了大部分代码,只展示了我可以影响代码正常工作的片段。我知道最终结果应该是什么(它只是一个 uint64_t),并且它与 MSVS 的 Release 版本有问题。我还检查了没有for()
循环。它没有“离开”优化。如果我完全忽略它,结果会再次不同。
@tstanisl 这只是一个 uint64_t 数字的问题。我知道输入 A 应该输出 B。
@Steve Summit 这就是我发帖的原因(有点绝望)。我检查了各个方向,尽可能多地隔离了代码,但是……没有未初始化的变量或数组越界。使我抓狂。
@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)
--- 更新 (24.08.2021 18:00) ---
我找到了问题的原因(经过无数次反复试验),但还没有解决方案。我发布了更多代码。
...
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()
提供了一个人为的修复,因为编译器看到了打印中间结果的要求。
另一种解决方法是将类型限定符volatile
用于a[]
.
问题的根源在于,MSVS 优化不考虑索引q
在两个循环中保持不变,这意味着第一个循环需要在第二个循环开始之前完成。
推荐阅读
- javascript - 如何根据某些条件更改对象值
- r - 如何使用 ggplot2 绘制这张图片?
- c++ - C++中空结构体的使用
- python - TypeError(f'Object of type {o.__class__.__name__} ' TypeError: Object of type bytes is not JSON serializable
- reactjs - 是否可以在 react 组件库中使用 redux 和 axios?
- c++ - 如何根据另一个向量的排序对向量进行排序?
- reactjs - 使用 mapStateToProps 时状态为空
- node.js - 如何在 Node.js 中处理比特币支付?
- bootstrap-4 - 使用 Promise 防止关闭 Bootstrap 模式
- angular - 在 ionic 项目中使用 Angular 库时,必须从注入上下文调用 inject()