указатель argv на массив указателей

Я смущен тем, как следующий отрывок совпадает с кодом, который следует за ним:

Поскольку argv — это указатель на массив указателей, мы можем манипулировать указателем, а не индексировать массив. Этот следующий вариант основан на увеличении argv, который является указателем на указатель на char, в то время как argc отсчитывается:

#include <stdio.h>
/* echo command-line arguments; 2nd version */
main(int argc, char *argv[])
{
    while (--argc > 0)
        printf("%s%s", *++argv, (argc > 1) ? " " : "");
    printf("\n");
    return 0;
}

Разве char *argv[] не просто массив указателей? Разве указатель на массив указателей не будет записан как char *(*argv[]) или что-то подобное?

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


person ericgrosse    schedule 22.06.2013    source источник


Ответы (6)


Такие термины, как "указатель на массив" или "указывать на массив" часто трактуются довольно свободно в терминологии C. Они могут означать как минимум две разные вещи.

В самом строгом и педантичном смысле этого термина «указатель на массив» должен быть объявлен с типом «указатель на массив», как в

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

В приведенном выше примере p объявлен как указатель на массив из 10 int и фактически инициализирован для указания на такой массив.

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

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

p объявлен как простой указатель на int. Он инициализируется, чтобы указать на первый элемент массива a. Часто можно услышать и увидеть, как люди говорят, что p в этом случае также "указывает на массив" int, хотя эта ситуация семантически отличается от предыдущей. «Указывает на массив» в данном случае означает «обеспечивает доступ к элементам массива посредством арифметики указателей», как в p[5] или *(p + 3).

Это именно то, что подразумевается под фразой "...argv является указателем на массив указателей...", которую вы цитируете. Объявление argv в списке параметров main эквивалентно char **argv, что означает, что argv на самом деле является указателем на указатель char *. Но поскольку физически он указывает на первый элемент некоторого массива char * указателей (поддерживаемого вызывающим кодом), правильно полунеформально сказать, что argv указывает на массив указателей.

Это именно то, что подразумевается под текстом, который вы цитируете.

person AnT    schedule 22.06.2013

Там, где функции C утверждают, что принимают массивы, вместо этого они строго принимают указатели. Язык не различает void fn(int *foo) {} и void fn(int foo[]). Это даже не важно, если у вас есть void fn(int foo[100]), а затем передать этот массив int [10].

int main(int argc, char *argv[])

такой же как

int main(int argc, char **argv)

Следовательно, argv указывает на первый элемент массива char указателей, но сам по себе не является типом массива и (формально) не указывает на весь массив. Но мы знаем, что массив существует, и мы можем индексировать его, чтобы получить другие элементы.

В более сложных случаях, например при приеме многомерных массивов, только первый [] возвращается к указателю (и размер которого можно оставить без изменений). Остальные остаются частью типа, на который указывают, и они влияют на арифметику указателя.

person sh1    schedule 22.06.2013

Эквивалентность указателя массива верна только только для аргументов функции, поэтому, хотя void fn(const char* argv[]) и void fn(const char** argv) эквивалентны, она не верна, когда речь идет о переменных, которые вы могли бы хотите передать функцию.

Рассмотреть возможность

void fn(const char** argv)
{
    ...
}

int main(int argc, const char* argv[])
{
    fn(argv); // acceptable.

    const char* meats[] = { "Chicken", "Cow", "Pizza" };

    // "meats" is an array of const char* pointers, just like argv, so
    fn(meats); // acceptable.

    const char** meatPtr = meats;
    fn(meatPtr); // because the previous call actually cast to this,.

    // an array of character arrays.
    const char vegetables[][10] = { "Avocado", "Pork", "Pepperoni" };
    fn(vegetables); // does not compile.

    return 0;
}

"овощи" не являются указателем на указатель, они указывают непосредственно на первый символ в непрерывной последовательности символов 3*10. Замените fn(овощи) выше, чтобы получить

int main(int argc, const char* argv[])
{
    // an array of character arrays.
    const char vegetables[][10] = { "Avocado", "Pork", "Pepperoni" };
    printf("*vegetables = %c\n", *(const char*)vegetables);

    return 0;
}

и выход "A": сами овощи указывают прямо - без косвенности - на символы, а не на промежуточные указатели.

Назначение овощей в основном является ярлыком для этого:

const char* __vegetablesPtr = "Avocado\0\0\0Pork\0\0\0\0\0\0Pepperoni\0";
vegetables = __vegetablesPtr;

а также

const char* roni = vegetables[2];

переводится как

const char* roni  = (&vegetables[0]) + (sizeof(*vegetables[0]) * /*dimension=*/10 * /*index=*/2);
person kfsone    schedule 22.06.2013

Since argv is a pointer to an array of pointers.

Это не верно. argv — это массив указателей.

person ouah    schedule 22.06.2013
comment
Зашел сюда, чтобы опубликовать это, так что +1 вам. Кроме того, может иметь смысл записать его как char* argv[], чтобы его было легче читать как массив указателей. - person austin; 23.06.2013
comment
Нет, это тоже неправильно, argv — это указатель на указатели. Рассмотрим sizeof(), возможность присвоить переменную argv чему-то другому и т. д. - person ; 23.06.2013
comment
@ H2CO3 Well C ссылается на argv как на массив указателей. Но, строго говоря, да, формальный параметр main приспосабливается к указателю на указатели. - person ouah; 23.06.2013
comment
@ouah Значит, формулировка Стандарта неверна? Аргумент функции не может возможно быть массивом в C, он всегда распадается на указатель. - person ; 23.06.2013
comment
@ H2CO3 Мне тоже не очень нравится их формулировка в этом конкретном случае. - person ouah; 23.06.2013

Поскольку argv является указателем на массив указателей,

Нет, даже не близко.

Разве char *argv[] не просто массив указателей?

Нет, это указатель на указатели.

person Community    schedule 22.06.2013

«Указатель на первый элемент массива» — распространенная конструкция. Его использует каждая строковая функция, включая функции stdio, которые вводят и выводят строки. main использует его для argv.

«Указатель на массив» — редкая конструкция. Я не могу найти его применения в стандартной библиотеке C или POSIX. grepping все заголовки, которые я установил локально (для '([^)]*\*[^)]) *\['), я нахожу ровно 2 законных экземпляра указателя на массив, один в libjpeg и один в gtk. (Оба являются членами структуры, а не параметрами функции, но это не относится к делу.)

Итак, если мы придерживаемся официального языка, у нас есть редкая вещь с коротким именем и похожая, но гораздо более распространенная вещь с длинным именем. Это противоположно тому, как естественно хочет работать человеческий язык, поэтому возникает напряжение, которое разрешается во всех ситуациях, кроме самых формальных, с помощью короткого имени «неправильно».

Причина, по которой мы не говорим просто «указатель на указатель», заключается в том, что есть еще одно распространенное использование указателей в качестве параметров функции, в котором параметр указывает на один объект, который не является членом массива. Например, в

long strtol(const char *nptr, char **endptr, int base);

endptr — это точно такой же тип, как argv в main, оба являются указателями, но используются по-разному. argv указывает на первый char * в массиве char *s; внутри main вы должны использовать его с такими индексами, как argv[0], argv[optind] и т. д., или проходить через массив, увеличивая его с помощью ++argv.

endptr указывает на один char *. Внутри strtol бесполезно увеличивать endptr или ссылаться на endptr[n] для любого значения n, отличного от нуля.

Это семантическое различие выражается в неформальном использовании «argv — указатель на массив». Возможная путаница с тем, что означает «указатель на массив» на формальном языке, игнорируется, потому что естественный инстинкт использовать краткий язык сильнее, чем желание придерживаться формального определения, которое говорит вам не использовать самую очевидную простую фразу, потому что она зарезервирована. для ситуации, которая почти никогда не произойдет.

person Community    schedule 22.06.2013