【问题标题】:Why does this C code work fine when debugging but not when run normally?为什么这段 C 代码在调试时可以正常工作,但在正常运行时却不行?
【发布时间】:2021-02-25 13:13:58
【问题描述】:

我正在用 C 语言创建一个函数,该函数使用一个 for 循环检查重复字符,该循环更新一个数组以说明以前是否见过一个字符。

int duplicate(string key, int key_length)
{
    int seen[256];

    for (int i = 0; i < key_length; i++)
    {
        seen[(int)key[i]] += 1;
    }

    for (int i = 0; i < 256; i++)
    {
        printf("%i ", seen[i]);
    }

    return 1;
}

当我使用测试字符串(例如 fhfkkdkdjrbrhrjrotorrjekwl)运行代码时,程序的输出是:

-268375615 32516 1286666352 32765 1286666368 32765 -268328471 32516 4 0 -268415848 32516 9 0 0 0 1 0 -268187160 32516 1286666436 32765 -268415848 32516 -268187160 32516 -268184328 32516 0 0 1287397800 32765 -268225857 32516 1 0 -1 0 1286666436 32765 -274155632 32516 -268419360 32516 1286666928 32765 1286666416 32765 1286666672 32765 -268309317 32516 -268191943 32516 -268301364 32516 -268191934 32516 -268374096 32516 1286666768 32765 7 0 7 8 -268376704 32516 -268187160 32516 -268321004 32516 9 0 -268318823 32516 1286666592 32765 -274072912 32516 -268419360 32516 0 0 1286666720 32765 0 0 0 0 0 0 -268376080 32516 -268184328 32516 1286666720 32765 -268378111 32516 -268375530 32517 -268376894 32516 -268189622 32516 3 4 2 0 -274155632 32518 -268375296 32516 1230 0 43 0 0 1 0 0 0 0 0 0 0 0 -274197488 32516 899256888 1615131 0 0 -268185200 32516 -8 -1 -268271904 32516 1286667280 32765 -268359765 32516 0 0 0 0 0 0 0 0 0 0 1 0 -268183808 32516 -274195584 32516 -268185248 32516 1286666753 32765 -268187160 32516 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 32 0 0 0 0 0 0 1 0 61765110 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 576 832 896 896 960 1472 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 2496 0 0 256 64 0 64 512 1024 0 0 0 0 0 0 0 0 0 0 0 0

但是,当我在调试器中单步执行该函数时,它会输出:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 2 1 2 0 2 0 3 4 1 0 0 2 0 0 6 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 

这是我期望它做的。

我在这里做错了什么,为什么它在调试器中可以正常工作,但在代码正常运行时却不行?

【问题讨论】:

  • 启用所有警告,您会立即看到问题
  • 如果没有有关您正在使用的软件和您用于构建的开关的信息,就无法确定性地回答为什么代码在调试器中表现不同的问题。如果您为调试而构建程序的方式不同,那么所涉及的编译器开关可能会改变一些程序数据布局......
  • … 一些调试器在被调试程序“外部”运行,使用操作系统特性来控制目标程序。对于那些调试器,是否使用调试器不应该影响程序行为,除了时序。但是有些调试器可能会在被调试的程序“内部”执行,它们更容易影响程序的行为。不过,我希望现在在现代通用操作系统中这种情况会越来越少。
  • @EricPostpischil:也有可能调试 build 恰好为此阵列使用了之前未触及的新堆栈空间,但优化的构建使用了之前被弄脏的空间. (MSVC 会在调试版本中使用memset(buf, 0xCC, size)“毒化”未初始化的内存,但其他编译器不会。)

标签: arrays c function output


【解决方案1】:

您通过使用未初始化的非静态局部变量int seen[256]; 的值调用了未定义的行为,这是不确定的。当未定义的行为被调用时,任何事情都可以发生。

像这样初始化

int seen[256] = {0};

为了避免这种错误。

【讨论】:

  • 在 C 2018 6.3.2.1 中使访问未定义的自动存储持续时间的未初始化对象的规则不适用于数组,因为它仅适用于可能已声明为 register 的对象。所以这里没有未定义的行为,只是不确定的值。
  • @EricPostpischil:真的吗?所以 _Bool arr[10] 必须由编译器在 ABI 保证 01 位模式的大多数主流实现上初始化? (Does the C++ standard allow for an uninitialized bool to crash a program?)
  • @EricPostpischil:godbolt.org/z/WWcv87 是带有_Bool seen[] 而不是int 的OP 代码。它显示 GCC10.2 not 初始化堆栈内存,因此未触及的元素将打印为一些未知字节零扩展到 int,不一定是 0 或 1。所以 GCC 基本上给了我们不确定的 位模式(无效的 _Bool 值)。 可能是专门用于 bool 的 UB,尽管它对大多数其他类型都很好。
  • @PeterCordes:C 标准并没有说它必须被初始化。它说内容是不确定的,并没有说访问它们的行为是不确定的。这意味着每次使用数组中的元素都必须表现得好像它具有某个值(每次使用可能不同),但程序不能仅仅因为使用不确定的值而捕获或做任意事情。
  • @EricPostpischil:好的,如果标准说类型值是不确定的,而不是位模式,那么 GCC 在这里违反了标准。 GCC 可以将_Bool 转换为int 并获得123(或任何其他8 位零扩展值),而不仅仅是01。为了防止这种情况,唯一实际的行为是初始化并非所有位模式都对该类型有效的数组。 (在这种情况下,另一个理智的好像行为是在读取时进行布尔化,在这里可行,因为这个 _Bool 数组不会转义函数。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-11
  • 1970-01-01
  • 1970-01-01
  • 2017-03-01
  • 2019-08-24
  • 2012-06-10
  • 1970-01-01
相关资源
最近更新 更多