Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a temporary char** argument illegal?

I have a function, f(char **p), and I wanted to call it in the simplest way I could.

I tried

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

and got:

scalar object requires one element in initializer

so I changed it into

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

and that went fine [would have been also fine with just char * p[]].

Why can't I create an array of pointers on the fly like the following?

f({"a", "b"});
like image 940
CIsForCookies Avatar asked Oct 31 '18 12:10

CIsForCookies


3 Answers

This gives a warning:

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

because p is not an array.

This is also not legal:

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

since curly braces by themselves are not allowed in an expression (but can be used as an initializer).

It is possible to create an array on the fly like that using a compound literal:

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

You could also use a compound literal to initialize a temporary:

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

Unlike the first statement, this is valid because the literal is an array of type char *[2] and will decay to a char ** which can be used to initialize a variable of this type.

See section 6.5.2.5 of the C standard for more details on compound literals.

like image 84
dbush Avatar answered Nov 04 '22 20:11

dbush


This declaration char ** p = {"a", "b"}; triggers a warning because C does not know how to interpret the content of { } braces:

  • Declaration type char** suggests it's a single pointer-to-pointer-to-char
  • Content of { } specifies two items.

You need to tell C that you are initializing a pointer to pointer with a pointer to the initial element of an anonymous array by specifying a type in front of the braces:

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

This construct is called a "compound literal". It could be used in a declaration, but it also works as an expression.

Note: if you are planning to pass an array like that to a function, you need to provide a way to determine the size of the array. One approach is to pass NULL to "terminate" the array, like this:

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.

like image 15
Sergey Kalinichenko Avatar answered Nov 04 '22 19:11

Sergey Kalinichenko


C99 and later added compound literals for the exact purpose: creating array and structs on the fly
Example:

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

Apart from std C99 and later, GCC provide this feature as an extension too.
In you case this will work

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

(char *[]){"a","b"} is a compound literal which creates an array of 2 pointers to char on the fly.

like image 5
haccks Avatar answered Nov 04 '22 21:11

haccks