C语言中的指针算术

11

我有以下的代码。也许我对指针运算的理解不够充分,但为什么 int_pointer 要增加 4 而不是 1?对于 char_pointer ,为什么它不是增加 4 而是 1 呢?

 #include <stdio.h>

 int main() {
    int i;

    char char_array[5] = {'a', 'b', 'c', 'd', 'e'};
    int int_array[5] = {1, 2, 3, 4, 5};

    char *char_pointer;
    int *int_pointer;

    char_pointer = int_array; // The char_pointer and int_pointer now
    int_pointer = char_array; // point to incompatible data types.

    for(i=0; i < 5; i++) { // Iterate through the int array with the int_pointer.
        printf("[integer pointer] points to %p, which contains the char '%c'\n",
            int_pointer, *int_pointer);
        int_pointer = int_pointer + 1;
    }

    for(i=0; i < 5; i++) { // Iterate through the char array with the char_pointer.
        printf("[char pointer] points to %p, which contains the integer %d\n",
            char_pointer, *char_pointer);
        char_pointer = char_pointer + 1;
    }
 }

输出:

[integer pointer] points to 0xbffff810, which contains the char 'a'
[integer pointer] points to 0xbffff814, which contains the char 'e'
[integer pointer] points to 0xbffff818, which contains the char ' '
[integer pointer] points to 0xbffff81c, which contains the char '
[integer pointer] points to 0xbffff820, which contains the char ' '
[char pointer] points to 0xbffff7f0, which contains the integer 1
[char pointer] points to 0xbffff7f1, which contains the integer 0
[char pointer] points to 0xbffff7f2, which contains the integer 0
[char pointer] points to 0xbffff7f3, which contains the integer 0
[char pointer] points to 0xbffff7f4, which contains the integer 2

2
char_pointer = int_array; 这是不好的。我认为将不兼容类型的指针赋值给另一个指针是未定义行为。 - user3920237
@ShafikYaghmour:OP在指针赋值过程中混合了类型,但这并不会改变指针算术行为。虽然这违反了严格别名规则。另一个问题是在后续循环迭代中解引用int_pointer时触发的越界数组访问。我有遗漏什么吗? - jweyrich
@jweyrich 这段代码存在两种不同形式的未定义行为,如果不涵盖这些问题,就无法正确回答问题,因为代码是错误的。 - Shafik Yaghmour
char_pointer = int_array;-- 这不仅是未定义行为,而且是约束违规。基本上是非法的;任何符合标准的编译器都必须发出诊断,尽管许多编译器会(不幸的是,在我看来)将其作为非致命警告。但如果您将其更改为char_pointer =(char *)int_array;,则不再是约束违规,但根据指针的使用方式,您可能会遇到未定义的行为。对于int_pointer = char_array;也是同样的情况,只是您还可能遇到对齐问题。 - Keith Thompson
我认为这是一个重复问题。这个问题的核心已经在其他地方得到了回答。除此之外,它还是无效代码(由于取消引用),但这是次要的,它可以很容易地重写以避免UB。 - Oliver Charlesworth
显示剩余2条评论
3个回答

17

指针算术运算的工作原理是这样的:如果你将指针增加1,那么地址将增加指针类型的大小。因此,由于在您的机器上int类型是4个字节,所以增加整数指针会将地址增加4个字节。


9

未定义行为

你所拥有的是 未定义行为,首先你违反了严格别名规则,这基本上使得通过指向不同类型的指针访问对象非法,虽然通过char *访问是允许的。我将引用我的答案,更详细地介绍了这一点:

严格别名规则禁止通过指向不同类型的指针访问对象,虽然通过char *访问是允许的。编译器可以假设不同类型的指针不指向相同的内存,并进行相应的优化。这也意味着代码会产生未定义行为,可能真的会做任何事情。

第二个不同指针可能具有不同的对齐要求,因此通过int指针访问char数组可能会违反此要求,因为char数组可能未对int类型进行正确对齐。draft C99 standard在第6.3.2.3指针中涵盖了这一点,其中说道(我强调):

对象或不完整类型的指针可以转换为指向不同对象或不完整类型的指针。如果所得到的指针未正确对齐57)于所指向的类型,则其行为是未定义的。

一个好的编译器应该使用正确的标志来帮助解决这个问题,使用clang和以下标志-std=c99 -fsanitize=undefined -Wall -Wextra -Wconversion -pedantic,我看到以下警告信息(请点击查看实时效果):

warning: incompatible pointer types assigning to 'char *' from 'int [5]' [-Wincompatible-pointer-types]
char_pointer = int_array; // The char_pointer and int_pointer now
             ^ ~~~~~~~~~

warning: incompatible pointer types assigning to 'int *' from 'char [5]' [-Wincompatible-pointer-types]
int_pointer = char_array; // point to incompatible data types.
            ^ ~~~~~~~~~~

并且在运行时我看到以下错误:

runtime error: load of misaligned address 0x7fff48833df3 for type 'int', which requires 4 byte alignment
0x7fff48833df3: note: pointer points here
00  e0 3e 83 61 62 63 64 65  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  6d 47 60 5a 1d 7f 00
             ^ 
指针算术 指针算术基于所指向类型的大小。否则,基于指针算术的数组访问将无法正确工作,因为它基本上是语法糖。您可以在此处阅读更详细的描述,并在相关讨论中了解更多。

2

在进行指针算术运算时,它将按照您尝试增加的大小递增。以此为例。

int a[2];
a[0] = 1;
a[1] = 3;
a = a + 1
printf("%d\n",*a) \\ 3

需要沿着所指向的物体大小前进。帮助我的方法是首先将指针转换为 char 类型以处理字节。

int a[2];
a[0] = 1;
a[1] = 3;
a = (char)a + sizeof(int)*1
printf("%d\n",*a) \\ 3

那就更容易理解了,它会精确地产生你想要的结果。

1
我认为数组的名称是一个常量指针,其值无法更改。在这里,我认为行“a = a + 1”应该会导致编译错误。 - Jack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接