Вывод с использованием char * для указания на массив int

Каков результат следующей программы, написанной на 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;

}

Откуда этот код? это похоже на собеседование или тестовый вопрос. Вы тестировали программу и теперь не можете объяснить себе результат?

Iharob Al Asimi 10.08.2018 14:13

Какой вывод на вашей машине? Настоящий вопрос - это 2 0 или 0 0. Зачем вообще печатать 0 2?

Gerhardh 10.08.2018 15:09

Почему вы не можете выполнить это и убедиться в этом сами? Вы что, не понимаете, что получаете, что ли?

Lundin 10.08.2018 15:11

@Lundin, это не лучший совет для неопределенного или определяемого реализацией поведения, такого как это. Результаты не зависят от языка и зависят от размера и компоновки char и int.

Toby Speight 10.08.2018 15:21

@Gerhadh, 0 2 - вероятный результат на платформах MC6800 (возможно, также на VAX; моя память там немного туманная).

Toby Speight 10.08.2018 15:23

@Gerhardh Почему 68000 актуален? Я знаю, что это 32-битная версия. Я упомянул 6800, где int обычно 16 бит, а char 8.

Toby Speight 10.08.2018 16:57

@Gerhardh: 68000 - это 16-битный процессор, и обычно C на этой арке использовал 16-битный int. Только 68020 и выше использовали 32-битный int на некоторых платформах (обычно в системах Unix).

too honest for this site 10.08.2018 17:22

@TobySpeight извините, я испортил процессоры.

Gerhardh 10.08.2018 17:32
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
8
84
2

Ответы 2

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

Toby Speight 10.08.2018 17:00

Этот

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);

Другие вопросы по теме