解释c / ++中阴影变量初始化的令人惊讶的行为

时间:2017-01-23 02:41:41

标签: c gcc clang

通过重构一些代码(因此,丑陋)的一半,我得到的东西相当于以下内容:

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char const *argv[]) {
    int *h = NULL;
    int size = 1024;
    int p = 4;
    h = (int*)malloc(sizeof(int) * size * p);

    printf("%p\n\n", h );

    for (int i = 0; i < p; ++i) {
        int *h = &h[i * size];
        printf("%p %d\n", h, i );
    }
    return 0;
}

输出:

0x11f3010

0x11f7020 0
0x11f7020 1
0x11f7020 2
0x11f7020 3

有两件事我没想到,其中一件我甚至无法合理化。

第一个是h的循环局部初始化的RHS令人惊讶地引用了刚创建的h而不是外部作用域中的h。这有点令人惊讶,因为我希望在创建变量之前对RHS进行评估,但我认为init跟随创建,然后就好了。此外,这是有道理的,考虑到有时我们需要初始化循环数据结构,如链接列表,其中init值可能故意包含自引用。

我不能解释第二个问题,除非是一个错误。尽管将具有不同偏移的循环局部指针初始化为h,但h始终指向同一地址,即自身。对比:

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char const *argv[]) {
    int *h2 = NULL;
    int size = 1024;
    int p = 4;
    h2 = (int*)malloc(sizeof(int) * size * p);

    printf("%p\n\n", h2 );

    for (int i = 0; i < p; ++i) {
        int *h = &h2[i * size];
        printf("%p %d\n", h, i );
    }
    return 0;
}

输出:

0x65e010

0x65e010 0
0x65f010 1
0x660010 2
0x661010 3

...它给出了地址h指向的预期步幅。是什么给了什么?

更新:Curiouser和Curiouser

# clang --version    
# lang version 3.8.0 (tags/RELEASE_380/final)

# clang -O3 untitled.cpp -o 1 && ./1
0x11a0010

0x11a4020 0
0x11a4020 1
0x11a4020 2
0x11a4020 3

# gcc -O3 untitled.cpp -o 1  && ./1
0x2494010

0x4005a0 0
0x4015a0 1
0x4035a0 2
0x4065a0 3

# gcc --version
gcc (GCC) 6.3.1 20161221 (Red Hat 6.3.1-1)

所以看来clang是单向的,而gcc则是另一种方式(注意gcc中的步幅在size*sizeof(int)=0x1000不是常数,而是0x1000,0x2000,0x3000。

这里有没有语言律师可以保证这里有什么正确的东西?这只是未定义的行为吗?

0 个答案:

没有答案