Мой указатель char указывает на недопустимое значение после того, как он был запущен из int *
Я изучаю язык программирования C, я только начал изучать массивы с указателями. У меня проблема в этом вопросе, я надеюсь, что этот вывод должен быть 5
, но это 2
. Может ли кто-нибудь объяснить, почему?
int main(){
int arr[] = {1, 2, 3, 4, 5};
char *ptr = (char *) arr;
printf("%d", *(ptr+4));
return 0;
}
Ответы
Ответ 1
Предположим, что небольшая эндианная архитектура, где int - 32 бита (4 байта), отдельные байты int arr[]
выглядят следующим образом (младший байт на нижнем адресе. Все значения в шестнадцатеричном формате):
|01 00 00 00|02 00 00 00|03 00 00 00|04 00 00 00|05 00 00 00
char *ptr = (char *) arr;
Теперь ptr
указывает на первый байт - так как вы нажали на char*
, он обрабатывается массивом char и далее:
|1|0|0|0|2|0|0|0|3|0|0|0|4|0|0|0|5|0|0|0
^
+-- ptr
Затем *(ptr+4)
обращается к пятому элементу массива char и возвращает соответствующее значение char
:
|1|0|0|0|2|0|0|0|3|0|0|0|4|0|0|0|5|0|0|0
^
+-- *(ptr + 4) = 2
Следовательно, printf()
печатает 2
.
В системе Big Endian порядок байтов в каждом int
отменяется, что приводит к
|0|0|0|1|0|0|0|2|0|0|0|3|0|0|0|4|0|0|0|5
^
+-- *(ptr + 4) = 0
Ответ 2
Это потому, что размер char
равен единице, а размер int
равен четырем. Это означает, что добавление 4
в ptr
приводит к тому, что результат указывает на вторую запись в массиве int
.
Если вы скомпилировали это в большой endian, вы бы напечатали 33554432 вместо этого.
Ответ 3
int main(){
int arr[] = {1,2,3,4,5};
char *ptr = (char *) arr;
printf("%d",*(ptr+4));
return 0;
}
Каждый случай arr
имеет размер sizeof(int)
(который может быть 4 в вашей реализации).
Так как ptr
является указателем на char
, арифметика указателя делает ptr + 4
точками 4 байта после &arr[0]
, что может be &arr[1]
.
В памяти это выглядит примерно так:
Address | 0 1 2 3 | 4 5 6 7 | ...
Value | arr[0] | arr[1] | ...
Ответ 4
На 32-битной платформе int
в четыре раза превышает размер char
. Когда вы добавляете 4 в ptr
, вы добавляете в 4 раза больше того, что указывает ptr на ptr (что само по себе является местом памяти). Это будет адрес второго элемента в массиве int
.
На 64-битной платформе int
имеет размер восемь размер char
; и ваш результат будет совсем другим.
Чтобы сократить длинный рассказ, ваш код не переносится (также см. Joachim Pileborg answer re endianness), но забавный, чтобы разблокировать.
Ответ 5
То, что вы делаете, определенно не рекомендуется в производственном коде, но, безусловно, отлично подходит для понимания указателей, бросков и т.д. в процессе обучения, поэтому для этого ваш пример замечательный. Итак, почему вы получаете 2. Это потому, что ваш массив представляет собой массив ints, который в зависимости от вашей архитектуры имеет разный размер (в вашем случае sizeof(int)
равно 4). Вы определяете ptr
как указатель char, char имеет размер 1 байт. Арифметика указателя (то, что вы делаете, когда пишете ptr+4
) работает с размером объектов, на которые ссылается указатель, в вашем случае с символами. Таким образом, ptr+4
находится на расстоянии 4 байта от начала вашего массива и, следовательно, на 2-й позиции вашего массива int
. Вот и все. Попробуйте ptr+5
, вы должны получить 0.
Ответ 6
Поскольку вы скрываете int * до char *, ptr [0] = 1, ptr [4] = 2, ptr [8] = 3, ptr [12] = 4, ptr [16] = 5 и все остальные равны 0. ptr + 4 указывает на 4-й элемент массива ptr. Таким образом, результат равен 2.
Ответ 7
int main(){
int arr[] = {1,2,3,4,5};
char *ptr = (char *) arr;
printf("%d",*(ptr+4));
return 0;
}
Представьте, что arr
хранится по адресу 100
(полностью тупой адрес). Так что у тебя есть:
arr[0]
хранится по адресу 100.
arr[1]
хранится по адресу 104. (из-за типа int
существует +4)
arr[2]
хранится по адресу 108.
arr[3]
хранится по адресу 112. Etc и т.д.
Теперь вы делаете char *ptr = (char *) arr;
, поэтому ptr
= 100 (то же, что и arr
).
Следующее утверждение интересно, особенно второй аргумент printf
: *(ptr+4)
.
Помните, что ptr
= 100. Итак ptr + 4
= 104, тот же адрес, что arr[1]
! Таким образом, он напечатает значение arr[1]
, которое равно 2.