Почему функция (char * array []) является допустимым определением функции, а не (char (* array) [] в C? - PullRequest
5 голосов
/ 04 августа 2011

I думаю , что это потому, что первый является массивом указателей на char, а второй - указателем на массив символов, и нам нужно правильно указать размер объекта, на который указываетдля нашего определения функции.В первом случае

function(char * p_array[])

размер объекта, на который указывает указатель, уже включен (указатель на символ), но во втором

function(char (*p_array)[])

необходим размер массиваp_array указывает на как часть определения p_array?Я нахожусь на этапе, когда я слишком долго думал об этом и просто запутался, кто-то, пожалуйста, дайте мне знать, если мои рассуждения верны.

Ответы [ 4 ]

14 голосов
/ 04 августа 2011

Оба действительны в C , но не в C ++ . Вы бы обычно были бы правы:

char *x[]; // array of pointers to char
char (*y)[]; // pointer to array of char

Однако массивы распадаются на указатели, если они появляются в качестве параметров функции. Так они становятся:

char **x; // Changes to pointer to array of pointer to char
char (*y)[]; // No decay, since it's NOT an array, it's a pointer to an array

В типе массива в C допускается не указывать один из размеров. Это, должно быть, самый левый (к сожалению, сначала я сказал самый правый). Таким образом,

int valid_array[][5]; // Ok
int invalid_array[5][]; // Wrong

(Вы можете связать их ... но у нас редко есть причина для этого ...)

int (*convoluted_array[][5])[][10];

Существует ловушка, , и ловушка в том, что тип массива с [] является неполным типом. Вы можете передать указатель на неполный тип, но некоторые операции не будут работать, так как им нужен полный тип. Например, это не будет работать:

void func(int (*x)[])
{
    x[2][5] = 900; // Error
}

Это ошибка, потому что для того, чтобы найти адрес x[2], компилятор должен знать, насколько велики x[0] и x[1]. Но x[0] и x[1] имеют тип int [] - неполный тип без информации о его размере. Это станет более понятным, если вы представите, какой будет «не затухшая» версия типа, которая является int x[][] - очевидно, недействительной C. Если вы хотите передать двумерный массив в C, у вас есть несколько вариантов :

  • Передать одномерный массив с параметром размера.

    void func(int n, int x[])
    {
        x[2*n + 5] = 900;
    }
    
  • Использовать массив указателей на строки. Это немного неуклюже, если у вас есть подлинные 2D-данные.

    void func(int *x[])
    {
        x[2][5] = 900;
    }
    
  • Используйте фиксированный размер.

    void func(int x[][5])
    {
        x[2][5] = 900;
    }
    
  • Использовать массив переменной длины (только C99, поэтому он, вероятно, не работает с компиляторами Microsoft).

    // There's some funny syntax if you want 'x' before 'width'
    void func(int n, int x[][n])
    {
        x[2][5] = 900;
    }
    

Это частая проблемная область даже для ветеранов Си. Во многих языках отсутствует встроенная поддержка "из коробки" для реальных, многомерных массивов переменного размера (C ++, Java, Python), хотя у некоторых языков она есть (Common Lisp, Haskell, Fortran). Вы увидите много кода, который использует массивы массивов или вычисляет смещения массива вручную.

3 голосов
/ 04 августа 2011

Эти две декларации очень разные.В объявлении параметра функции объявление [], непосредственно примененное к имени параметра, полностью эквивалентно *, поэтому ваше первое объявление точно такое же, как и это:

function(char **p_array);

Однако это не применяется рекурсивно к типам параметров.Ваш второй параметр имеет тип char (*)[], который является указателем на массив неизвестного размера - это указатель на неполный тип.Вы можете счастливо объявлять переменные с этим типом - следующее допустимое объявление переменной:

char (*p_array)[];

Так же, как указатель на любой другой неполный тип, вы не можете выполнять арифметику указателей для этой переменной (или вашего параметра функции) - вот где у вас возникает ошибка.Обратите внимание, что оператор [] указан как a[i] и идентичен *(a+i), поэтому оператор нельзя применить к указателю.Конечно, вы можете с радостью использовать его в качестве указателя, поэтому это действительно:

void function(char (*p_array)[])
{
    printf("p_array = %p\n", (void *)p_array);
}

Этот тип также совместим с указателем на любой другой массив фиксированного размера, равный char, поэтому вы можететакже сделайте это:

void function(char (*p_array)[])
{
    char (*p_a_10)[10] = p_array;

    puts(*p_a_10);
}

... и даже это:

void function(char (*p_array)[])
{
    puts(*p_array);
}

(хотя в этом нет особого смысла: вы можете просто объявить параметр с типомchar *).

Обратите внимание, что, хотя *p_array разрешено, p_array[0] не разрешено.

2 голосов
/ 04 августа 2011

ПРИМЕЧАНИЕ:
Следующий ответ был добавлен, когда Q был помечен как C ++, и он отвечает с точки зрения C ++.Если пометка помечена только на C, оба упомянутых примера действительны для C.

Да, ваши рассуждения верны.
Если вы попытаетесь скомпилировать ошибку, указанную компилятором:

parameter ‘p_array’ includes pointer to array of unknown bound ‘char []’

В C ++ размеры массивов должны быть исправлены во время компиляции.Стандарт C ++ также запрещает использование переменной переменной длины (VLA).Некоторые компиляторы поддерживают это как расширение, но это не соответствует стандарту.

2 голосов
/ 04 августа 2011

Потому что

(1) function(char * p_array[])

эквивалентно char **p_array;т.е. действительный двойной указатель.

(2) function(char (*p_array)[])

Вы правы, что p_array является указателем на массив char.Но это должно быть фиксированного размера в случае, когда он появляется в качестве аргумента функции.Вам необходимо указать размер, который также станет действительным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...