char *ch = "delhi"; // valid
int *arr = {1, 2, 3}; // invalid
int *arr = (int[3]){1, 2, 3}; // valid
Why some of the above statements are valid while others are invalid?
char *ch = "delhi"; // valid
int *arr = {1, 2, 3}; // invalid
int *arr = (int[3]){1, 2, 3}; // valid
Why some of the above statements are valid while others are invalid?
Disclaimer too: I'm answering the question for C++, and I think the answer won't apply to C, in many aspects.
In short, pointer and array are different things.
In c++,
int *arr = {1, 2, 3};
is invalid because pointer couldn't be initialized via list initialization.
int *arr = (int[3]){1, 2, 3};
is valid because array could be initialized via list initialization, and aggregate initialization will be appied for array actually. For this case, a temporary array will be constructed and then decayed to int*
and assigned to arr
. Note that the temporary variable will be destroyed after the statement so arr
will be dangled after that.
char *ch = "delhi";
is not valid from c++11, const char* ch = "delhi"
is valid. "delhi" is a string literal with type const char[6]
, and then decayed to const char*
and assigned to ch
. Because string literal has static storage duration and ch
won't be dangled.
Note that it's not the special rule for int
pointer, it's same for char
pointer too. const char* ch1 = { 'd', 'e', 'l', 'h', 'i', '\0'};
will fail too.
char *ch = "delhi"; // valid
This uses a string literal. The compiler generates a static c-string and assigns its starting address to ch
. This syntax is special for strings, and has been introduced for the convenience of avoiding typing something like char ch[] = {'d', 'e', 'l', 'h', 'i', '\0'};
every time.
int *arr = {1, 2, 3}; // invalid
{1, 2, 3}
is an array initializer, or a std::initializer_list
starting from c++ 11. In the first case, the array initializer is not an 'array literal' but a special convenience syntax that is understood by the compiler and applies to arrays only (int*
and int[]
are slightly different types). In the c++ 11 case you simply don't have conversion from std::initializer_list<T>
to T*
.
int *arr = (int[3]){1, 2, 3}; // valid
In this case you are telling the compiler to generate a temporary int[3]
array, initialize it with the list, and assign its address to arr
. Valid, but you should get a warning that you are taking the address of a temporary object or something like that.
EDIT (errata corrige):
The first part of the answer seems to imply that a string literal and a char[]
are the same thing. This was unintended, hopefully the part "The compiler generates a static c-string..." and the remark on the second paragraph avoided this misunderstanding in most cases.
The statement "{1, 2, 3} is a std::initializer_list in c++ 11" is not correct. "{1, 2, 3}" is a braced-list-initializer, which is not a type per se. It is a syntactical element that can initialize certain types depending on the context.
Disclaimer: I'm answering the question for C++ - some of the reasoning might also apply to C, but I haven't checked the latter.
String literals (the right side of your first example) are (in c++) const char
arrays with static storage duration. This means the compiler puts them somewhere in a fixed position in memory that remains valid during the whole execution of your program. As a result, you can assign them to a pointer just like any other array (storing the address of the first element).
Your particular example won't work in c++11 and later, because ch
would actually have to be of type const char*
. In C-however - and to the best of my knowledge - string literals are arrays of type char
(non const), so you can also assign them to a normal char
ptr and c++ versions prior to c++11 allowed this assignment for campatibility reasons.
The second line is invalid C++ code for multiple reasons: Contrary to the first line, the right side is not an array, so array to pointer decay doesn't work here. Now you can initialize a pointer with and initializer list, but only if
nullptr
)however, the meaning would be totally different compared to the first example: you wouldn't initialize the pointer with the address of an element in the initializer list but just with a copy
Now in the third example (which will be rejected by a c++ compiler by the way, but is OK for const int*
and const int[]
) I believe you are creating a temporary array that is initialized by copying the contents of the initializer list (the integer literals) and then assigning it to a pointer. Imho this should produce a dangling pointer as soon as the end of the statment is reached, but I'm not 100% sure of it.