gpt4 book ai didi

c - 对二维数组进行别名时 strlen 的意外优化

转载 作者:行者123 更新时间:2023-12-03 07:27:21 27 4
gpt4 key购买 nike

这是我的代码:

#include <string.h>
#include <stdio.h>

typedef char BUF[8];

typedef struct
{
BUF b[23];
} S;

S s;

int main()
{
int n;

memcpy(&s, "1234567812345678", 17);

n = strlen((char *)&s.b) / sizeof(BUF);
printf("%d\n", n);

n = strlen((char *)&s) / sizeof(BUF);
printf("%d\n", n);
}

使用具有除 -O0 之外的任何优化级别的 gcc 8.3.0 或 8.2.1,当我期待 0 2 时,它​​会输出 2 2 。编译器决定 strlenb[0] 绑定(bind),因此永远不能等于或超过被除的值。

这是我的代码中的错误还是编译器中的错误?

这在标准中没有明确说明,但我认为指针出处的主流解释是,对于任何对象 X ,代码 (char *)&X 应该生成一个可以迭代整个 X 的指针——这个概念应该成立,即使 X 恰好有子数组作为内部结构。

(额外的问题,是否有一个 gcc 标志来关闭这个特定的优化?)

最佳答案

我对此进行了检查,并在 gcc 8.3 上使用 -O1 进行了复制,因此我只打开了 gcc 优化标志 here 列表并开始一一进行试验。事实证明,使用 -fno-tree-ccp 仅禁用 稀疏条件常量传播 会使问题消失(哦,幸运的是,如果一个一个测试没有结果,我计划测试几个标志)。
然后我切换到 -O2 但没有删除 -fno-tree-ccp 标志。它再次重现。我说“OK”,然后开始测试额外的 -O2 标志。似乎再次禁用单个 值范围传播 会导致预期的 2 2 输出。
然后我删除了第一个 -fno-tree-ccp 标志,但它再次开始复制。因此,对于 -O2,您可以指定 -O2 -fno-tree-ccp -fno-tree-vrp 以使您的程序按预期工作。
我没有删除这些标志,而是切换到 -O3。问题没有重现。
因此 gcc 8.3 中的这两种优化技术都会导致这种奇怪的行为(也许它们在内部使用了一些常见的东西):

  • 树上的稀疏条件常数传播
  • 树上的值范围传播

  • 我不是所有这些东西的专业人士来解释那里发生的事情和原因,也许其他人可以解释。但可以肯定的是,您可以指定 -fno-tree-ccp -fno-tree-vrp 标志来禁用这些优化技术,以使您的代码按预期工作。
    “我越努力,我就越幸运。”
    ——塞缪尔·戈德温
    编辑
    正如@KamilCuk 在问题评论中指出的那样, -fno-builtin-strlen 也会导致 inteded 行为,因此很可能存在一个编译器错误,结合了内置 strlen 和另一个优化,旨在切断死代码,静态确定可能的表达式值并传播常量通过程序。我认为编译器很可能错误地考虑了一些东西,它在其 strlen 实现 中确定字符串长度(可能与 整数除法 和/或 二维数组 结合使用)作为死代码并将其计算为 0在编译时。所以我决定用代码来检查一下理论并消除其他可能的错误“参与者”。我来到了这个行为的最小例子,它证实了我的想法:
    int main()
    {
    // note that "7" - inner arrays size, you can put any other number here
    char b[23][7]; // local variable, no structs, no typedefs
    memcpy(&b[0][0], "12345678123456781234", 21);

    printf("%d\n", strlen(&b[0][0]) / 8); // greater than that "7" !!!
    printf("%d\n", strlen(&b[0][0]) / 7);
    printf("%d\n", strlen(&b[0][0]) / 6); // less than that "7" !!!
    printf("%d\n", strlen(&b[0][0])); // without division
    }

    0


    0


    3


    20


    我认为我们可以认为这是 gcc 中的一个错误。
    我认为 -fno-builtin-strlen 是解决该问题的更好解决方案,因为它仅适用于所有优化级别,并且内置 strlen 似乎不太强大的优化技术,特别是如果您的程序不经常使用 strlen()。仍然 -fno-tree-ccp -fno-tree-vrp 也是一种选择。

    关于c - 对二维数组进行别名时 strlen 的意外优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58759591/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com