36

这是我的代码:

#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);
}

使用 gcc 8.3.0 或 8.2.1 与任何优化级别,除了我期待的-O0输出。编译器决定是有界的,因此永远不能等于或超过被除的值。0 22 2strlenb[0]

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

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

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

4

5 回答 5

1
于 2020-06-16T11:10:42.450 回答
0

您定义结构的方式起初让我感到困惑,因为我认为我从未尝试过创建数组类型。这样做也很危险,因为如果有人试图将它传递给函数,他们可能会认为他们是按值传递,但实际上会通过引用传递。不管风格如何,如果我需要创建一个这样的类型,我会做这样的事情:

//typedef char BUF[8];

//do it this way instead
typedef struct
{
    char x[8];
} BUF;

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

如果我以这种方式定义它,那么它会以任何一种方式返回预期值。在这里看到它。

于 2020-06-13T16:52:46.593 回答
-2

我可以看到一些问题,它们可能会受到编译器决定布局内存的方式的影响。

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

在上面的代码s.b中是一个 8 个字符数组的 23 个条目数组。当您仅引用时,s.b您将获得 23 字节数组中第一个条目的地址(以及 8 字符数组中的第一个字节)。当代码说&s.b时,这是在询问数组地址的地址。在幕后,编译器很可能会生成一些本地存储,将数组的地址存储在其中,并将本地存储的地址提供给strlen.

您有 2 种可能的解决方案。他们是:

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

或者

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

我还尝试运行您的程序并演示该问题,但是当我使用任何-O选项时,clang 和 gcc 版本仍然可以按您的预期工作。对于它的价值,我在 x86_64-pc-linux-gnu 上运行 clang 版本 9.0.0-2 和 gcc 版本 9.2.1)。

于 2020-02-01T21:54:49.907 回答
-3

我认为这可能是 gcc 中的一个错误。我找到了几个解决方案,但最简单的似乎是创建一个带有 noinline 属性的代理函数。这样您就不会失去任何其他优化,只是与 strlen 相关的优化。

int  __attribute__ ((noinline)) _strlen(char *x) { return strlen(x); }
#define strlen _strlen

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);
}

您可以在 Compiler Explorer 中看到输出。 https://godbolt.org/z/U2L9us

于 2020-05-06T23:05:50.013 回答
-5

代码中有错误。

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

例如,是有风险的,即使 s 以 b 开头应该是:

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

第二个 strlen() 也有错误

n = strlen((char *)&s) / sizeof(BUF);

例如,应该是:

n = strlen((char *)&s.b) / sizeof(BUF);

如果复制正确,字符串 sb 的长度应为 17 个字母。如果结构对齐,则不确定结构如何存储在内存中。您是否检查过 sb 实际上包含复制的 17 个字符?

所以 strlen(sb) 应该显示 17

printf 只显示整数,因为 %d 是整数,并且变量 n 被声明为整数。sizeof(BUF), 应该是 8

所以 17 除以 8 (17/8) 应该打印 2 因为 n 被声明为整数。由于 memcpy 用于将数据复制到 s 而不是 sb,我猜这与内存对齐有关;假设它是一台 64 位计算机,那么一个内存地址上可以有 8 个字符。

例如,假设有人调用了 malloc(1),而不是下一个“可用空间”没有对齐......

第二个 strlen 调用显示正确的数字,因为字符串复制到 s 结构而不是 sb

于 2020-01-29T22:53:26.907 回答