为什么现在"{static const char a [] = {...}"和"{const char a [] = {...}"之间存在差异?

Joh*_*ter 27 c c++

看一下godbolt 上这个C代码C++代码的小片段......

void b( char const *c);

void a(void)
{
   char const z[] = {0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf, 0xa};

   b(z);
}

void c(void)
{
   static char const z[] = {0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf, 0xa};

   b(z);
}
Run Code Online (Sandbox Code Playgroud)

早期版本的gcc将a()和c()编译为两条指令,加载z的地址,跳转到b.

所有现代编译器我都试图"悲观化"a()以"制作堆栈帧,将z复制到堆栈,调用b,拆除堆栈帧,但将c()作为两个指令简单版本.

实际上没有任何改变,在实践中现代编译器现在对这个用例来说更慢.....

有谁知道为什么?

gez*_*eza 17

C++有以下规则:

除非对象是零字段或零大小的子对象,否则该对象的地址是它占用的第一个字节的地址.如果一个嵌套在另一个中,则两个具有重叠生命周期且不是位字段的对象可以具有相同的地址,或者如果至少一个对象是零大小的子对象并且它们具有不同类型; 否则,它们具有不同的地址并占据不相交的存储字节.

现在,看看这段代码:

#include <stdio.h>

void c();

void b(const char *a) {
    static const char *p = 0;

    if (!p) {
        p = a;
        c();
    } else {
        if (a==p) {
            printf("problem!\n");
        }
    }
}

void c() {
    const char a[] = { 0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf };

    b(a);
}

int main() {
    c();
}
Run Code Online (Sandbox Code Playgroud)

这里,c递归调用一次,因此根据规则,数组a在每个递归级别应该有不同的地址.在第一次调用时b存储a,在第二次调用时,它会检查它是否相同.使用符合标准的编译器,它不应该打印"问题!".但实际上,使用旧的编译器(GCC 4.1,clang 6.0),它会打印出"问题!",因此这些编译器违反了标准.

a只有在可以证明此更改不可观察的情况下,才允许编译器生成静态:

在"as-if"规则下,允许实现在同一个机器地址存储两个对象,或者如果程序无法观察到差异,则根本不存储对象