Действуют ли скобки при объявлении массива указателей?

Недавно я читал учебник Брэйна и Денниса «Язык программирования C» и обнаружил, что существует разница между определениями массива указателей, показанными ниже:

int (*a)[10];

int *a [10];

(https://i.sstatic.net/82lnWvfT.png)

В учебнике об этом написано так:

Из учебника по языку программирования C

Я не совсем мог понять разницу. Если

 int (*a)[10];

определяет указатель на массив из 10 целых чисел, для чего используется '*'. И в чем разница между

 int (*a)[10];

 int a [10];

(https://i.sstatic.net/kE2ruHVb.png)

Прочтите этот ответ на аналогичный вопрос. Короткий ответ: да, круглые скобки важны.

axiac 09.07.2024 21:32
int (*a)[10]; — это указатель на int массив размера 10. int *a [10]; — это массив 10int*.
NathanOliver 09.07.2024 21:32

Это printf("%zu\n", sizeof a); может что-то раскрыть. Первый — это размер указателя, второй — размер массива.

Weather Vane 09.07.2024 21:32

Полагаю, путаница возникает из-за того, что во многих ситуациях массивы распадаются на указатели. Некоторые учителя заходят так далеко, что говорят, что массив — это указатель на первый элемент, но это неправда. Массив — это массив, а указатель — это указатель.

Yksisarvinen 09.07.2024 21:37

@NathanOliver, так в чем же разница с int a [10];, если оба представляют собой целочисленный массив размером 10? Означает ли это, что они одинаковы?

Abenaki 09.07.2024 21:47

@Abenaki Нет, они разные. int a[10] дает вам массив из 10 int. int (*a)[10]; — указатель на массив из 10 int. Это не массив, но он может указывать на массив.

NathanOliver 09.07.2024 21:50

точно так же, как int * ptr объявляет указатель на одиночный int, int (*a)[10]; создает указатель на массив int,

NathanOliver 09.07.2024 21:51

@NathanOliver да, теперь я понял. Когда я печатаю размеры int (*a)[10], они занимают всего 4 байта, а переменная int a[10] занимает 40 байт. Спасибо за помощь.

Abenaki 09.07.2024 21:58
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
2
9
104
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вот как я интерпретирую это, чтобы упростить - правило справа налево (рекурсивно). Итак, для первого случая

int (*a)[10];

а — справа ничего нет из-за закрывающих скобок. Теперь слева написано, что это указатель. До сих пор (*a) означает, что a является указателем. Теперь снова идем вправо, поэтому массив [10] затем влево типа int. Итак, чтобы завершить это — a — это указатель на массив int[10].

int *b [10];

b - справа a находится массив размером 10, а затем слева от указателей int. Итак, b — это массив int* размера 10.

Вы можете убедиться, проверив размер a и b.

На моей машине:

 size of a:8
 size of b:80

Большое спасибо. Это потрясающий способ запомнить их.

Abenaki 09.07.2024 22:07
Ответ принят как подходящий

В этой декларации

int a [10];

объявлен массив из 10 объектов типа int.

Вы можете проверить размер массива следующим образом

printf( "sizeof( a ) = %zu\n", sizeof( a ) );

Если sizeof( int ) равно 4, то приведенный выше вызов printf выведет 40: размер всего массива.

В этой декларации

int (*a)[10];

объявлен указатель на массив элементов 10.

Это переименовать идентификатор a в p, тогда вы можете написать с учетом объявления массива a выше:

int a [10];
int ( *p )[10] = &a;

Размер указателя p равен 4 или 8 в зависимости от используемой системы. Вы можете проверить это, используя следующий вызов printf:

printf( "sizeof( p ) = %zu\n", sizeof( p ) ):

Разыменовав указатель, вы можете получить массив как один объект, на который указывает указатель p. Так что если ты напишешь

printf( "sizeof( *p ) = %zu\n", sizeof( *p ) );

вы получите размер массива a.

Что касается этой декларации

int *a [10];

затем он объявляет массив, элементы которого имеют тип указателя int *. Было бы более читабельно переписать эту строку так:

int * a [10];

На самом деле это объявление эквивалентно

int * ( a [10] );

Опять же, вы можете использовать вызов printf как

printf( "sizeof( a ) = %zu\n", sizeof( a ) );

И результат будет равен 40 или 80 в зависимости от того, равно ли sizeof( int * )4 или 8.

Чтобы объявить указатель на этот массив, вы можете написать

int * a [10];
int * ( *p )[10] = &a;

И в объявлениях, и в выражениях постфиксные [] и () имеют более высокий приоритет, чем унарные *, поэтому *a[i] анализируется как *(a[i]) — мы индексируем в a, а затем разыменовываем результат.

Напротив, с помощью (*a)[i] мы разыменовываем a, а затем индексируем результат.

Некоторые примеры кода и изображения могут помочь:

int x, y, z;
int *a[3] = { &x, &y, &z };

   +---+                 +---+ 
a: |   | a[0]  -----> x: |   | 
   +---+                 +---+   
   |   | a[1]  ---+ 
   +---+          |      +---+
   |   | a[2]  -+ +-> y: |   |
   +---+        |        +---+        
                |        
                |        +---+
                +---> z: |   |
                         +---+


   a[0] == &x // int * == int *
  *a[0] ==  x // int   == int

Контраст с:

int v[3];
int (*a)[3] = &v;

   +---+           +---+
a: |   | -----> v: |   | v[0]  
   +---+           +---+       
                   |   | v[1]
                   +---+
                   |   | v[2]
                   +---+

 a == &v // int (*)[3] == int (*)[3]
*a ==  v // int [3]    == int [3]

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