Какая разница между типами - int * и int * [100] в C?

Природу нуба, так что не убивай меня здесь.

Какая разница между следующими кодами?

int *p;         //As i understand, it creates a pointer to an variable of size int.
int *p[100];    //Don't really know what this is.
int (*p)[100];  // I have come to understand that this is a pointer to an array. 

Ответ 1

  • Это указатель на int:

    int *p;
    
    ┌────┐
    │int*│
    └────┘
    

    Он должен указывать на int, что-то вроде этого:

    ┌────┐
    │int*│
    └─┃──┘
      ▼
    ┌───┐
    │int│
    └───┘
    
  • Это массив из 100 указателей на int:

    int *p[100];
    

    То есть, это дает вам 100 указателей.

    ┌────┬────┬────┬────┬────┬────┬┄
    │int*│int*│int*│int*│int*│int*│
    └────┴────┴────┴────┴────┴────┴┄
    

    Каждый указатель должен указывать int, возможно, так:

    ┌────┬────┬────┬────┬────┬────┬┄
    │int*│int*│int*│int*│int*│int*│
    └─┃──┴─┃──┴─┃──┴─┃──┴─┃──┴─┃──┴┄
      ▼    ▼    ▼    ▼    ▼    ▼
    ┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐┌┄
    │int││int││int││int││int││int││
    └───┘└───┘└───┘└───┘└───┘└───┘└┄
    

    Конечно, нет причин, по которым они не могут указывать на один и тот же int или что-то еще.

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

    p[0] = new int(0);
    p[1] = new int(0);
    // ...
    

    Возможно, динамическое выделение int - не лучший пример, но я думаю, что точка понятна.

  • Это указатель для массива из 100 int:

    int (*p)[100];
    

    То есть, он дает вам всего один указатель:

    ┌───────────┐
    │int(*)[100]│
    └───────────┘
    

    Он должен указывать на массив, содержащий 100 int s:

    ┌───────────┐
    │int(*)[100]│
    └─┃─────────┘
      ▼
    ┌───┬───┬───┬───┬───┬───┬┄
    │int│int│int│int│int│int│
    └───┴───┴───┴───┴───┴───┴┄
    

    Вы получите указатель на массив, когда вы используете адрес-оператора (&) для имени массива. Например:

    int arr[100] = { /* some initial values */ };
    int (*p)[100] = &arr;
    

    Здесь я взял адрес массива arr, который дает мне указатель на этот массив. Если вы хотите получить доступ к элементу массива, сначала нужно разыменовать указатель: (*p)[3] получит доступ к элементу 3.


Боковое примечание:

Всегда помните, что массивы не являются указателями. Как мы только что видели, мы можем взять адрес массива, чтобы получить указатель на него, как и любой другой (невременный) объект в С++. Единственное специальное соединение между массивами и указателями состоит в том, что имя массива может быть неявно преобразовано в указатель на первый элемент массива. Это означает, что действует следующее:

int arr[100] = { /* some initial values */ };
int* p = arr;

Указатель p будет указывать на первый элемент в arr. Обратите внимание: p не является указателем на массив, а указателем на элемент массива.

(Также обратите внимание, что аргумент функции типа массива отсутствует. Если вы пишете что-то вроде int p[] как аргумент функции, он преобразуется компилятором как int*.)

Ответ 2

Похоже, вы могли бы использовать введение в Спиральное правило.

Начните с переменной и "спирально" со своим направлением справа налево:

              +-------+
              | +--+  |             // So we have:
              | |  |  |                    p    // p     
          int * p  |  |                  * p    // p is a pointer
           ^  ^    |  |              int * p    // p is a pointer to an int
           |  +----+  |
           +----------+

Следующий:

              +--------+
              | +--+   |         p       // p
              | |  V   |         p[100]  // p is an array of 100
          int * p[100] |       * p[100]  // p is an array of 100 pointers
           ^  ^    |   |   int * p[100]  // p is an array of 100 pointers to ints
           |  +----+   |
           +-----------+

Наконец, новая часть правила сначала сделайте в скобках:

                   +-----+            
                   | +-+ |    
                   | ^ | |         ( p)       // p
              int (* p)  [100];    (*p)       // p is a pointer
               ^   ^   | |         (*p)[100]  // p is a pointer to an array of 100
               |   +---+ |     int (*p)[100]  // p is a pointer to an array of 100 ints
               +---------+    

Если вы в сети/имеете доступ к компьютеру, он всегда использует fule для использования cdecl.org сайта, но важно быть возможность чтения кода в автономном режиме, и это правило позволит вам сделать именно это.

Ответ 3

Природу нуба, так что не убивай меня здесь.

Разработка того, что тип означает в C, может быть сложным даже для экспертов. Не беспокойтесь.

Какая разница между следующими кодами?

Другие ответы хороши, и я не собираюсь им противоречить. Скорее, здесь есть еще один способ подумать об этом. Нам нужно определить три вещи:

  • Переменная - это вещь, которая поддерживает три операции. Операция fetch принимает переменную и производит ее текущее значение. Операция выборки не имеет обозначений; вы просто используете переменную. Операция хранилища принимает переменную и значение и сохраняет значение в переменной. Операция адреса принимает переменную и создает указатель.

  • Указатель - это то, что поддерживает одну операцию. Операция разыменования, написанная как префикс *pointer, принимает указатель и создает переменную. (Указатели поддерживают другие операции, такие как арифметика и индексирование, - это форма арифметики, но не туда.)

  • Массив - это вещь, которая поддерживает одну операцию. Операция index принимает массив и целое число и создает переменную. Это синтаксис postfix: array[index]

Хорошо, теперь мы подошли к вашему вопросу. Что делает декларация

int p;

означает? То, что выражение p является переменной типа int. Обратите внимание, что это переменная; вы можете хранить вещи до p. Что делает декларация

int *p;

означает? То, что выражение *p является переменной типа int. Теперь, когда мы знаем, что мы можем определить, что такое p. Поскольку *p является разыменованием и создает переменную, p должен быть указателем на int. Что делает декларация

int *p[100];

означает? Это означает, что * выражение *p[i] является переменной типа int при условии, что i является целым значением от 0 до 99. Мы получили переменную, но мы могли бы получить ее от указателя или массива, поэтому нам нужно выяснить, что. Мы консультируемся с таблицей приоритетов операторов и обнаруживаем, что оператор индексирования связывает "более жесткий", чем оператор разыменования. То есть:

*p[i]

- это то же самое, что и

*(p[i])

и помните, что это переменная типа int. Содержимое parens разыменовывается, чтобы создать переменную типа int, поэтому содержимое parens должно быть указателем на int. Поэтому

p[i]

является указателем на int. Как это возможно? Это должно быть выборка переменной типа pointer-to-int! Итак, p[i] - это переменная типа указателя на int. Поскольку это операция индекса, p должен быть массивом указателей на int.

Теперь вы выполните следующий.

int (*p)[100];

означает что?

Ответ 4

int *p; → Объявляет указатель на целочисленный тип.
int *p[100]; → Объявляет массив из 100 указателей на целочисленный тип.
int (*p)[100]; → Объявляет указатель на массив из 100 целых чисел.

Используйте cdecl для перевода таких типов.