C语言中,数组访问运算符和指针的行为有一个有趣的性质,即表达式 a[5] 和 5[a] 是等价的。此种现象背后有一些指针运算的基本原理和数组访问的特性,并且可以帮助我们更好地理解C语言中数组和指针的关系。

1、数组下标运算符的工作原理

C语言中,数组下标运算符 [] 的行为基于指针运算。表达式 a[i] 的实际含义如下,

*(a + i) // 等价于 a[i]

a[i] 是指向 a 开始的第 i 个元素的值,即从 a 的起始地址移动 i 个位置,并解引用这个新位置的值。基本数组访问如下,

#include <stdio.h>

int main() {
    int a[5] = {1, 2, 3, 4, 5};
    
    printf("a[2] = %d\n", a[2]);  // 输出 3
    printf("*(a + 2) = %d\n", *(a + 2));  // 等价,输出 3

    return 0;
}

a[2] 实际上就是 *(a + 2),它意味着从数组 a 的起始地址移动2个元素,并返回该位置的值。

2、反转的数组下标

a[i] 等价于 *(a + i),这种关系是对称的。换句话说,a[i] 可以重写为 i[a],如下,

a[i] == *(a + i) 5[a] == *(5 + a)

5[a] 等价于 *(5 + a),这在指针运算中是完全合法的。C语言可以使用这种形式,因为在指针运算中,加法的顺序并不重要,a + 55 + a 是等价的。反转的数组访问如下,

#include <stdio.h>

int main() {
    int a[5] = {1, 2, 3, 4, 5};
    
    printf("a[2] = %d\n", a[2]);    // 输出 3
    printf("2[a] = %d\n", 2[a]);    // 输出 3,等价于 a[2]
    
    printf("a[4] = %d\n", a[4]);    // 输出 5
    printf("4[a] = %d\n", 4[a]);    // 输出 5,等价于 a[4]

    return 0;
}

3、数组和指针的对称性

C语言中的数组名可以视为指向数组首元素的指针。数组和指针有许多相似之处,尤其是当进行指针加法运算时。数组的下标访问运算符只是对这种加法运算的语法糖。数组和指针的等价性,数组名 a 可以看作是指向数组首元素的指针,即 a == &a[0]。指针加法 a + i 计算的是数组中第i 个元素的地址,5 + a 同样有效,并且与 a + 5 等价。

#include <stdio.h>

int main() {
    // 定义一个数组
    int a[] = {10, 20, 30, 40, 50};
    
    // 通过数组名和下标访问元素
    printf("通过数组名访问 a[2] = %d\n", a[2]);  // 输出 30
    
    // 使用指针访问数组元素
    int *p = a;  // 数组名 a 相当于 &a[0],即指向数组第一个元素的指针
    printf("通过指针访问 *(p + 2) = %d\n", *(p + 2));  // 输出 30
    
    // 使用指针并使用数组下标访问
    printf("通过指针和下标访问 p[2] = %d\n", p[2]);  // 输出 30
    
    // 使用 i[a] 访问数组元素
    printf("通过 i[a] 访问 2[a] = %d\n", 2[a]);  // 输出 30

    return 0;
}

4、编译器对 a[i] 和 i[a]的处理

编译器在处理 a[i]i[a] 时,都会将它们转换为指针运算。因此,无论是 a[i] 还是 i[a],编译器都会将其解释为指针加法和解引用操作。

编译器转换 a[i] 被编译器转换为 *(a + i)i[a] 被编译器转换为 *(i + a)。两种转换在效果上完全相同。通过编译器生成的汇编代码来查看编译器对 a[i]i[a] 的具体处理。代码如下,

#include <stdio.h>

int main() {
    int a[] = {10, 20, 30, 40, 50};
    
    // 使用 a[i] 和 i[a] 访问数组
    int x = a[2];   // 等价于 *(a + 2)
    int y = 2[a];   // 等价于 *(2 + a)
    
    printf("x = %d, y = %d\n", x, y);
    
    return 0;
}

推荐文档