Почему аргумент временного символа ** недопустим?

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

Я пытался

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

и получил:

scalar object requires one element in initializer

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

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

и все прошло нормально [тоже было бы нормально только с char * p[]].

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

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

Что ж, самый простой способ, который я мог себе представить, - это char *p; f(&p);. Это зависит от семантики функции, если ожидается массив строк или адрес указателя, который должен быть изменен в функции.

Gerhardh 01.11.2018 13:25
26
1
3 019
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

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

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 для получения более подробной информации о составных литералах.

К сожалению, любой составной литерал, созданный таким образом, будет иметь время жизни, которое заканчивается ближайшим охватывающим блоком, поэтому, учитывая int **p; ... if (someCondition) p=(int*[2]){&x,&y};, добавление фигурных скобок вокруг оператора, управляемого if, приведет к тому, что объект, созданный составным литералом, умрет до того, как код сможет его использовать.

supercat 31.10.2018 21:50

@supercat: Должен признаться, я надеялся, что подобные литералы будут вести себя как строковые константы - то есть они действительно живут вечно и находятся в памяти только для чтения. Ну что ж.

Joshua 31.10.2018 23:17

@Joshua: Или, по крайней мере, они могут быть объявлены static и иметь такое поведение, или продолжаться до тех пор, пока код либо не покинет текущий функция, либо повторно не выполнит литерал. Предположительно, причина того, что литералы не сохраняются на протяжении всей текущей функции, заключается в неоднозначности того, что должно произойти, если они будут повторно выполнены, но ограничение их текущим блоком никак не устраняет эту двусмысленность.

supercat 31.10.2018 23:24

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

supercat 01.11.2018 03:20

Это объявление 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;
}

Демо.

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.

GCC также предоставляет эту функцию как расширение - Это (f((char *[]){"a","b"});) не сработало бы на Clang?
CIsForCookies 31.10.2018 13:56

@CIsForCookies; Clang также предоставляет то же самое, что и расширение. В clang int []y = (int []) {1, 2, 3}; тоже будет работать, в то время как GCC выдаст ошибку.

haccks 31.10.2018 14:04

Другие вопросы по теме