Почему временный аргумент char ** незаконен?

У меня есть функция f(char **p), и я хотел вызвать ее как можно проще.

Я старался

char ** p = {"a", "b"};
f(p);

и получил:

скалярному объекту требуется один элемент в инициализаторе

поэтому я изменил это в

char * p[2] = {"a", "b"};
f(p);

и это прошло хорошо [было бы также хорошо с просто char * p[] ].

Почему я не могу создать массив указателей на лету, как показано ниже?

f({"a", "b"});

Ответы

Ответ 1

Это дает предупреждение:

char ** p = {"a", "b"};

потому что p не массив.

Это тоже не законно:

f({"a", "b"});

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

Можно создать массив на лету, используя составной литерал:

f((char *[]){"a", "b"});

Вы также можете использовать составной литерал для инициализации временного:

char ** p = (char *[]){"a", "b"};

В отличие от первого утверждения, это допустимо, потому что литерал является массивом типа char *[2] и будет распадаться на char ** который можно использовать для инициализации переменной этого типа.

См. Раздел 6.5.2.5 стандарта C для более подробной информации о составных литералах.

Ответ 2

Это объявление char ** p = {"a", "b"}; вызывает предупреждение, потому что C не знает, как интерпретировать содержание { } фигурные скобки:

  • Тип объявления char** предполагает наличие единого указателя на указатель на символ
  • Содержимое { } определяет два элемента.

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

char ** p = (char*[]){"a", "b"}

Эта конструкция называется "составной литерал". Его можно использовать в объявлении, но он также работает как выражение.

Примечание: если вы планируете передать подобный массив функции, вам нужно указать способ определения размера массива. Один из подходов состоит в том, чтобы передать NULL для "завершения" массива, например так:

void f(char **p) {
    int i = 0;
    while (*p) {
        printf("%d:%s\n", i++, *p++);
    }
}
int main(void) {
    f((char*[]){"a", "b", NULL});
    return 0;
}

Demo.

Ответ 3

C99 и позже добавили составные литералы для точной цели: создание массива и структур на лету
Пример:

struct foo {int a; char b[2];} structure;
structure = ((struct foo) {1, 'a', 0});
int *y = (int []) {1, 2, 3};
int *z = (int []) {1}; 

Помимо стандартного C99 и более поздних, GCC также предоставляет эту функцию в качестве расширения.
В вашем случае это будет работать

f((char *[]){"a","b"});

(char *[]){"a","b"} является составным литералом, который создает массив из 2 указателей на char на лету.