混淆二维数组时strlen的意外优化


28

这是我的代码:

#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]

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

标准中并未明确阐明这一点,但是我认为指针来源的主流解释是,对于任何对象X,代码(char *)&X都应生成一个可以迭代整个对象的指针X-即使X碰巧具有子数组作为内部结构。

(奖金问题,是否有gcc标志来关闭此特定优化?)



4
参考:我的gcc 7.4.0 2 2在各种选项下报告。
chux-恢复莫妮卡

2
@Ale标准保证它们在同一地址(结构不能具有初始填充)
MM

3
@ DavidRankin-ReinstateMonica“导致char(*)[8]的边界被限制为b [0]。但是就我所知,”我认为这很合意。因为s.b被限制在b[0]它被限制为8个字符,并因此两个选项:(1)外的束缚的情况下,访问有8个非空字符,这是UB,(2)有一个空字符,其中len小于8,因此除以8得出零。因此,将(1)+(2)编译器放在一起可以使用UB为两种情况提供相同的结果
user2162550

3
给定&s ==&s.b,结果是不可能不同的。如@ user2162550所示,未调用strlen(),并且即使在Godbolt.org/z/dMcrdy编译器无法识别其结果的情况下,编译器也会对其结果进行猜测。 这是一个编译器错误
Ale

Answers:


-1

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

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

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


-2

代码中有错误。

 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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.