当前位置: 首页 > 知识库问答 >
问题:

C 编译错误?

端木承业
2023-03-14

我有以下代码

#include <iostream>
#include <complex>
using namespace std;

int main() {
    complex<int> delta;
    complex<int> mc[4] = {0};

    for(int di = 0; di < 4; di++, delta = mc[di]) {
        cout << di << endl;
    }

    return 0;
}

我希望它输出“0,1,2,3”并停止,但它输出的是一系列无穷无尽的“0、1、2、3、4、5……”

看起来比较< code>di

如果我只是注释掉< code >,delta=mc[di],我会正常得到“0,1,2,3”。无辜作业有什么问题?

我正在使用Ideone.com g14带-O2选项。

共有3个答案

裘光启
2023-03-14

你有这个:

for(int di=0; di<4; di++, delta=mc[di]) {
  cout<<di<<endl;
}

试试这个:

for(int di=0; di<4; delta=mc[di++]) {
   cout<<di<<endl;
}

编辑:

为了澄清正在发生的事情,让我们分解一下 For 循环的迭代:

第一次迭代:最初di设置为0。比较检查:di小于4吗?是的,好的,继续。将di增加1。现在di=1。抓取mc[]的“第n个”元素并将其设置为delta。这次我们抓取第二个元素,因为这个索引值是1而不是0。最后执行for循环中的代码块/s。

第二次迭代:现在di设置为1。比较检查:di小于4吗?是的,然后继续。将di增加1。现在di=2。抓取mc[]的“第n”个元素并将其设置为delta。这次我们抓取第三个元素,因为这个索引值是2。最后在for循环中执行代码块/s。

第三次迭代:现在di设置为2。比较检查:di小于4吗?是的,然后继续。将di递增1。现在di=3。抓取mc[]的“nth”元素并将其设置为delta。这次我们抓取第4个元素,因为这个索引值是3。最后执行for循环内的代码块/s。

第 4 次迭代:现在 di 设置为 3。比较检查:di是否小于4?是的,然后继续。Increment di 乘以 1.现在 di = 4.(你能看出这是怎么回事吗?获取 mc[] 的“第 n 个”元素,并将其设置为增量。这次我们抓取第 5 个元素,因为这个索引值为 4。哦,哦,我们有一个问题;我们的数组大小只有 4。Delta现在有垃圾,这是未定义的行为或损坏。最后使用“垃圾增量”在 for 循环内执行代码块。

第 5 次迭代。现在 di 设置为 4。比较检查:di是否小于4?不,打破循环。

超过连续内存(数组)的边界而损坏。

步建茗
2023-03-14

由于您在使用di来索引mc之前会递增di,因此第四次通过循环,您将引用mc[4],这已经过了数组的末尾,这反过来可能导致麻烦的行为。

长孙永思
2023-03-14

这是由于未定义的行为,您在循环的最后一次迭代中越界访问数组mc。一些编译器可能会围绕没有未定义行为的假设执行积极的循环优化。逻辑类似于以下内容:

  • 越界访问 mc 是未定义的行为
  • 假设没有未定义的行为
  • 因此,我们

打开优化并使用 -fno-激进循环优化标志的 gcc 会导致无限循环行为消失(实时查看)。虽然一个具有优化但没有-fno-激进循环优化的实时示例展示了您观察到的无限循环行为。

该代码的一个godbolt实时示例显示了di

jmp .L6

这与 GCC 4.8 之前打破已破损的 SPEC 2006 基准测试中概述的情况几乎完全相同。对本文的评论非常好,非常值得一读。它指出,叮当声在文章中使用-fsanitize=未定义捕获了这种情况,在这种情况下我无法重现,但是使用-fsanitize=未定义的gcc确实如此(请参阅实时)。围绕围绕未定义行为进行推理的优化器最臭名昭着的错误可能是 Linux 内核空指针检查删除。

尽管这是一种激进的优化,但需要注意的是,正如C标准所说,未定义的行为是:

本国际标准没有要求的行为

这基本上意味着一切皆有可能,它指出(强调我的):

[...]允许的未定义行为的范围从完全忽略具有不可预测结果的情况,到在翻译或程序执行期间以环境特有的记录方式表现(发出或不发出诊断消息),到终止翻译或执行(发出诊断消息)。[...]

为了从 gcc 获得警告,我们需要将 cout 移到循环之外,然后我们看到以下警告(实时查看):

warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations]
     for(di=0; di<4;di++,delta=mc[di]){ }
     ^

这可能足以为OP提供足够的信息来弄清楚发生了什么。像这样的不一致是典型的行为类型,我们可以在未定义的行为中看到。为了更好地理解为什么面对未定义的行为时,这种警告可能是不一致的 为什么在基于未定义行为进行优化时不能发出警告?是一本好书。

注意,-fno aggressive loop optimizations在gcc 4.8发行说明中有记录。

 类似资料:
  • 我正在尝试编译这里提到的桥接模式C示例代码 但是,当我使用g-o桥时,我会遇到编译错误。C 计划: 错误: ]#g-o桥桥。C桥。C: 在成员函数“virtual void TimeImp::tell()”中:Bridge。C: 14:错误:“运算符”不匹配 这是错误的一部分,它以相同的模式扩展。 我复制粘贴了相同的代码,分析了错误行,但无法得到它。 有人能告诉我我哪里错了吗?

  • 我试图用Qt Creator为Android编译一个普通的C++静态库。然而,我收到了这个错误: 我需要包括上述库的路径。 如果有人能给我指明方向,我将不胜感激。

  • 本文向大家介绍C++ 用Clang编译,包括了C++ 用Clang编译的使用技巧和注意事项,需要的朋友参考一下 示例 由于锵前端被设计为是与GCC兼容,当你交换可通过GCC编译大多数程序将编译g++通过clang++在构建脚本。如果没有-std=version给出,将使用gnu11。 习惯了MSVC的Windows用户可以cl.exe与交换clang-cl.exe。默认情况下,clang尝试与已安

  • 问题内容: 在编译C 时,您当然会为要编译的目标平台使用编译器。是否有针对JVM的C 编译器(因此,不是使用Java“本机”接口,而是将C ++代码编译为Java字节码)? 问题答案: NestedVM为Java字节码提供二进制转换。这是通过让GCC编译为MIPS二进制文件然后将其转换为Java类文件来完成的。因此,任何用C,C ++,Fortran或GCC支持的任何其他语言编写的应用程序都可以在

  • 问题内容: 当我在Netbeans中构建并运行程序时,它可以正常工作。但是当我尝试“ mvn compile”时,使用相同的pom.xml文件会出现以下错误: 我的Java版本不是1.3,这里是“ mvn -version”的结果 这是第53行: 问题答案: 问题是在Maven2中默认使用和 您可以通过将其添加到pom中来解决此问题: 将其放在最顶层的父pom中是很实际的,这样您派生的pom不需要

  • 我遇到了一个初学者的编译错误: 我的简单程序: 我尝试使用以下命令编译它: gcc-g-Wall-ansi launch_瓷砖。c-o tiles\u程序 并得到这些错误: 启动_tiles。c: 在函数“main”中: launch_tiles. c: 17:19:错误:预期')'之前';'令牌 launch_tiles. c: 17:19:错误:太少的参数函数'fget' /usr/inclu