Арифметика указателей в C

У меня есть следующий код. Возможно, я не понял арифметику указателей, как и должен, но почему 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

Ответ 1

То, как работает арифметика указателей: если вы увеличиваете указатель на 1, адрес увеличивается на размер типа указателя. Так как в вашей машине ints составляет 4 байта, приращение указателя int увеличивает адрес 4 байта.

Ответ 2

Undefined Поведение

У вас есть Undefined поведение, сначала вы нарушаете строгое правило aliasing, которое в основном делает незаконным доступ к объекту с помощью указателя другого типа, хотя допускается доступ через char *. Я приведу здесь мой ответ, который охватывает это более подробно:

правила строгого сглаживания, которые делают незаконным доступ к объекту через указатель другого типа, хотя доступ через char* позволено. Компилятору разрешено предполагать, что указатели разных типов не указывают на одну и ту же память и оптимизируют соответственно. Это также означает, что код вызывает поведение undefined и действительно может что-то сделать.

Вторые разные указатели могут иметь разные требования к выравниванию, поэтому доступ к вашему массиву char с помощью указателя int может очень сильно нарушить это требование, поскольку массив char может не быть правильно выровнен для int. черновик стандарта C99 описывает это в разделе 6.3.2.3 Указатели, в котором говорится (выделено мной):

Указатель на объект или неполный тип может быть преобразован в указатель на другой объект или неполный тип. Если результат указатель неправильно выровнен 57) для указанного типа, поведение undefined.

Хороший компилятор с правильным набором флагов должен помочь здесь, используя 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
             ^ 

Арифметика указателя

Итак, арифметика указателя основана на размере указанного типа. Каким образом доступ к массиву, который в основном состоит из синтаксического сахара для арифметики указателя, работает в противном случае? Вы можете прочитать более подробное описание здесь и обсуждение здесь.

Ответ 3

При выполнении арифметики указателя он будет увеличиваться на размер того, что вы пытаетесь увеличить. Возьмите это, например.

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

Это немного понятнее, и это создаст именно то, что вы думаете.