通过重构一些代码(因此,丑陋)的一半,我得到的东西相当于以下内容:
#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。
这里有没有语言律师可以保证这里有什么正确的东西?这只是未定义的行为吗?