在指针算术中进行额外的缩放

在指针运算中,要添加或减去指针的整数不是解释为地址的变化,而是解释为要移动的元素的数量。

#include <stdio.h>

int main(void) {
    int array[] = {1, 2, 3, 4, 5};
    int *ptr = &array[0];
    int *ptr2 = ptr + sizeof(int) * 2; /* wrong */
    printf("%d %d\n", *ptr, *ptr2);
    return 0;
}

此代码在计算分配给 ptr2 的指针时进行了额外的缩放。如果 sizeof(int) 是 4,这在现代 32 位环境中是典型的,则表达式代表“array[0] 之后的 8 个元素”,这是超出范围的,并且它调用未定义的行为

要让 ptr2 指向 array[0] 之后的 2 个元素,你应该简单地添加 2。

#include <stdio.h>

int main(void) {
    int array[] = {1, 2, 3, 4, 5};
    int *ptr = &array[0];
    int *ptr2 = ptr + 2;
    printf("%d %d\n", *ptr, *ptr2); /* "1 3" will be printed */
    return 0;
}

使用加法运算符的显式指针算法可能会令人困惑,因此使用数组下标可能会更好。

#include <stdio.h>

int main(void) {
    int array[] = {1, 2, 3, 4, 5};
    int *ptr = &array[0];
    int *ptr2 = &ptr[2];
    printf("%d %d\n", *ptr, *ptr2); /* "1 3" will be printed */
    return 0;
}

E1[E2](*((E1)+(E2))) 相同( N1570 6.5.2.1,第 2 段),&(E1[E2]) 相当于 ((E1)+(E2))(N1570 6.5.3.2,脚注 102)。

或者,如果首选指针运算,则将指针转换为寻址不同的数据类型可以允许字节寻址。但请注意: endianness 可能会成为一个问题,并且转换为指向字符的指针以外的类型会导致严格的别名问题

#include <stdio.h>

int main(void) {
    int array[3] = {1,2,3};  // 4 bytes * 3 allocated
    unsigned char *ptr = (unsigned char *) array;  // unsigned chars only take 1 byte
    /*
     * Now any pointer arithmetic on ptr will match
     * bytes in memory.  ptr can be treated like it
     * was declared as: unsigned char ptr[12];
     */

    return 0;
}