Каков результат следующей программы, написанной на C?
Это 2 0
или 0 2
и почему?
int main()
{
int arr[] = {2,3,4}; // predefined pointer
char *p;
p=(char *)arr;
printf("%d\n",*p);
printf("%p\n",p);
p=p+1;
printf("%d\n",*p);
printf("%p\n",p);
return 0;
}
Какой вывод на вашей машине? Настоящий вопрос - это 2 0
или 0 0
. Зачем вообще печатать 0 2
?
Почему вы не можете выполнить это и убедиться в этом сами? Вы что, не понимаете, что получаете, что ли?
@Lundin, это не лучший совет для неопределенного или определяемого реализацией поведения, такого как это. Результаты не зависят от языка и зависят от размера и компоновки char
и int
.
@Gerhadh, 0 2
- вероятный результат на платформах MC6800 (возможно, также на VAX; моя память там немного туманная).
@Gerhardh Почему 68000 актуален? Я знаю, что это 32-битная версия. Я упомянул 6800, где int
обычно 16 бит, а char
8.
@Gerhardh: 68000 - это 16-битный процессор, и обычно C на этой арке использовал 16-битный int
. Только 68020 и выше использовали 32-битный int
на некоторых платформах (обычно в системах Unix).
@TobySpeight извините, я испортил процессоры.
Результат зависит от порядок байтов вашей системы, а также от размера int
(он также зависит от количества бит в байте, но пока мы предполагаем, что это 8).
Порядок байтов определяет порядок байтов в таких типах, как целые числа. Процессоры на базе x86 - это прямой порядок байтов, что означает, что младший байт идет первым, а другие - прямой порядок байтов, что означает, что старший байт идет первым.
Например, для переменной типа int
со значением 2 и при условии, что int
32-битный, память в системе с прямым порядком байтов выглядит следующим образом:
-----------------
| 0 | 0 | 0 | 2 |
-----------------
В системе с прямым порядком байтов это выглядит так:
-----------------
| 2 | 0 | 0 | 0 |
-----------------
Переходим к тому, что происходит, когда вы берете char *
и указываете его на int (или член массива int
). Обычно использование указателя на один тип для указания на другой тип и чтения значения, хотя другой указатель является строгим нарушением псевдонима, которое вызывает неопределенное поведение, однако в стандарте C есть исключение для символьных типов, позволяющее вам получить доступ к байтам в объекте представление. Так что в этом случае это разрешено.
Когда вы это сделаете:
p=(char *)arr;
Это заставляет p
указывать на первый байт первого члена массива arr
.
В системах с прямым порядком байтов:
-----
| . | p
-----
|
v
-------------------------------------------------
| 0 | 0 | 0 | 2 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 4 | arr
-------------------------------------------------
| arr[0] | arr[1] | arr[2] |
-------------------------------------------------
С прямым порядком байтов:
-----
| . | p
-----
|
v
-------------------------------------------------
| 2 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 4 | 0 | 0 | 0 | arr
-------------------------------------------------
| arr[0] | arr[1] | arr[2] |
-------------------------------------------------
Итак, когда вы прочитаете значение *p
, вы получите 0 в системах с прямым порядком байтов и 2 в системах с прямым порядком байтов.
Когда вы затем выполняете p=p+1
, вы увеличиваете адрес, на который указывает p
, на 1 персонаж, то есть на 1 байт, поэтому теперь это выглядит так:
Порядок байтов с прямым порядком байтов:
-----
| . | p
-----
|----
v
-------------------------------------------------
| 0 | 0 | 0 | 2 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 4 | arr
-------------------------------------------------
| arr[0] | arr[1] | arr[2] |
-------------------------------------------------
Младший порядок байтов:
-----
| . | p
-----
|----
v
-------------------------------------------------
| 2 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 4 | 0 | 0 | 0 | arr
-------------------------------------------------
| arr[0] | arr[1] | arr[2] |
-------------------------------------------------
Теперь *p
содержит значение 0 в системах с прямым и обратным порядком байтов. Однако это предполагает, что int
32-битный. Если int
16-битный, он выглядит так:
Порядок байтов с прямым порядком байтов:
-----
| . | p
-----
|----
v
-------------------------
| 0 | 2 | 0 | 3 | 0 | 4 | arr
-------------------------
|arr[0] |arr[1] |arr[2] |
-------------------------
Младший порядок байтов:
-----
| . | p
-----
|----
v
-------------------------
| 2 | 0 | 3 | 0 | 4 | 0 | arr
-------------------------
|arr[0] |arr[1] |arr[2] |
-------------------------
В этом случае *p
равен 2 в системах с прямым порядком байтов и 0 в системах с прямым порядком байтов после увеличения.
Технически ответ также зависит от CHAR_BIT
- его легко забыть, если вы работаете только с системами с маленькими символами ... Например, с char
и int
шириной 16 бит, 2 3
- еще один возможный результат.
Этот
int arr[] = {2,3,4};
выглядит так, как показано ниже, если ваша система поддерживает прямой порядок байтов, в случае прямого байта вывод может отличаться.
arr[2] arr[1] |------------arr[0]-----------------------------|
----------------------------------------------------------------------
| 4 | 3 | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0010 |
----------------------------------------------------------------------
0x108 0x104 0x103 0x102 0x101 0x100 -- assume arr base address starts from 0x100
arr
MSB LSB
Теперь, когда вы это сделаете
char *p;
p=(char *)arr;
Здесь p
- это тип указатель на символ и arr
, отлитый как char*
, что означает, что указатель p
указывает на по одному байту памяти за раз, то есть впервые 0x100
на 0x101
.
Когда заявление
printf("%d\n",*p);
выполняет, он печатает, какие данные есть в месте 0x100-0x101
, то есть 2
, следовательно, он печатает 2
.
А потом, когда вы это сделаете
p=p+1;
указатель p
увеличивается на один байт, т.е. теперь p
указывает на ячейку памяти 0x101
, и когда оператор printf("%d\n",*p);
выполняет, он печатает, какие данные находятся в ячейке 0x101-0x102
, то есть 0
, следовательно, он печатает 0
.
Также при использовании %p
вы должны указать тип переменной-указателя как void*
как printf ("% p") и приведение к (void *)
printf("%p\n",(void*)p);
Откуда этот код? это похоже на собеседование или тестовый вопрос. Вы тестировали программу и теперь не можете объяснить себе результат?