Синтаксис указателя / массива (char ** p, * p [n]) в C / C ++ - PullRequest
4 голосов
/ 21 февраля 2010

Что касается указателей, я путаюсь с объявлениями и параметрами функций о том, когда использовать char ** или char * или * array [n] и т. Д. Например, если функция принимает параметр (* array [n]), я могу передать это ** тип?

Я пытаюсь использовать правило Right-Left и знаю, что p будет указателем на указатель на символ (char ** p), а p является массивом из n указателей (* p [n]), но кто-то сказал, что * p [n] и ** p по существу эквивалентны. Это правда?

Ответы [ 3 ]

8 голосов
/ 21 февраля 2010

В правильном контексте (а именно, в аргументах функции) следующие объявления эквивалентны:

int main(int argc, char *argv[]);
int main(int argc, char **argv);
int main(int argc, char *argv[12]);  // Very aconventional!

Аналогичные комментарии применяются к определениям функций (в которых вместо фигурных скобок есть блокточка с запятой).

В любом другом контексте существуют важные различия между обозначениями.Например:

extern char *list1[];
extern char **list2;
extern char *list3[12];

Первый говорит, что где-то есть массив неопределенного размера, содержащий значения 'char *'.Второе говорит, что где-то - возможно, здесь - есть единственное значение, содержащее указатель на указатель на символ.Третий говорит, что где-то - возможно, здесь - есть массив из 12 символьных указателей.

Однако на все три списка можно ссылаться примерно одинаково - при условии, что они на самом деле были определены и инициализированы.

list1[0][0] = '1';
list2[0][0] = '2';
list3[0][0] = '3';

Далее, если они передаются в функцию, подобную этой:

function(list1, list2, list3);

, тогда функция может быть объявлена ​​как:

void function(char **list1, char **list2, char **list3);

Массивы (list1, list3) распад из массива на указатель на первый элемент массива;list2, конечно, уже является указателем на указатель.

Одна деталь, которую следует отметить в функции, такой как:

void otherfunction(char *list[12])
{
    ...
}

Компилятор C не обрабатывает это объявление иначе, чем:

void otherfunction(char **list)
{
    ...
}

или

void otherfunction(char *list[])
{
    ...
}

В частности, он не проверяет границы массива, а что касается функции, то 12 также может отсутствовать.


C99 представляет типы VLA (массив переменной длины), а также вводит нотацию с «static» и размером в границах массива.Вам необходимо прочитать стандарт, чтобы понять их полностью.

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

void vla_function(size_t m, int vla[m][m]);

Цитирование из стандарта (раздел 6.7.5.3):

void f(double (* restrict a)[5]);
void f(double a[restrict][5]);
void f(double a[restrict 3][5]);
void f(double a[restrict static 3][5]);

(Обратите внимание, что в последнем объявлении также указывается, что аргумент, соответствующий a в любом вызове f, должен быть ненулевым указателем на первый из как минимум трех массивов по 5 двойных, чего нет у других.)

5 голосов
/ 21 февраля 2010

Чтение деклараторов Си (это часть переменной с * и []) довольно нюансировано. Есть несколько сайтов с советами:

A char** - указатель на (возможно, несколько) указателей на (возможно, несколько) символов. Например, это может быть указатель на строковый указатель или указатель на массив строковых указателей.

A char*[] - это массив указателей на char. Когда у вас есть функция, которая принимает это в качестве параметра, компилятор C превращает его в «распад» в char**. Это происходит только с первым слоем ... поэтому, если взять сложный пример, char*[4][] становится char*(*)[4]. Прочитайте ссылки выше, чтобы вы могли понять, что, черт возьми, это значит.

Или вы можете сделать (очень разумную) вещь и сделать кучу typedef. Я этого не делаю, но пока вы не умеете читать деклараторы, это хорошая идея.

typedef char * stringp;
void func(stringp array[]) { ... }
static stringp FOUR_STRINGS[4] = { ... };
0 голосов
/ 21 февраля 2010

Если n==0, то они ссылаются на одну и ту же память. Индексирование массива в основном указатель плюс смещение. *(p[n]) будет таким же, как **(p+n). Вы можете сами убедиться, насколько это просто в C, потому что array[4] и 4[array] дадут вам то же самое.

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