考虑以下代码:
void process(int *);
int func(void) {
int arr[2][2] = {{1,2},{3,4}};
process(arr[0]);
return arr[1][0];
}
假设函数
process
是用汇编语言实现的。
如访问多维数组的元素是否越界未定义行为?中所述,使用
arr[1][0]
访问元素 arr[0][2]
是未定义行为,通过指针算术应指向相同的内存位置。
编译上述代码的 C 编译器是否可以假设
arr[1][0]
在调用 process
后保持不变,因此例如:将最后一行优化为 return 3;
,因为 process
函数只能访问 arr[0][0]
和 arr[0][1]
?
但是,如果编译器假设
process
首先在内部将 arr[0]
转换为 int[2][2]
指针,这可能不是未定义的行为,类似于常见的 container_of
宏如何使用指向结构体的指针来访问结构体指针。成员值,因此编译器将不被允许执行上面的优化?
编译器应该从数组中读取数据。这就是语言所要求的。所以你不必太担心你期望的错误。
用常量替换数组访问不是特定于语言的,而是特定于编译器的。大多数当前的编译器都足够聪明,可以正确执行此操作。
为了获得更好的图片,请尝试编译以下代码:
#include <stdio.h>
void process(int *p) {
p[2] = -1;
}
void process2(int *p);
int main() {
int arr[2][2] = {{1,2},{3,4}};
printf("%d\n", arr[1][0]);
process(arr[0]);
printf("%d\n", arr[1][0]);
int arr2[2][2] = {{5,6},{7,8}};
printf("%d\n", arr2[1][0]);
process2(arr2[0]);
printf("%d\n", arr2[1][0]);
return 0;
}
无需任何优化 (
gcc -S foo.c
),您可以在堆栈上有两个数组,并从数组中读取四次,再加上一个带有主体的实际 process
函数。
但是一旦你打开优化器(
gcc -S -O1 foo.c
),你可以看到第一个数组完全消失,只剩下process
函数的标签。打印前两个 printf
中的常量,在堆栈上为 arr2
定义一个数组,然后是常量的 printf
,调用 process2
,最后从数组中定义 printf
。