9
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?

LogicStuff
  • 19,397
  • 6
  • 54
  • 74
kumar shivam
  • 141
  • 3
  • 3
    It's not such a bad question as it might seem at first glance (except for not using ``). I would tend to guess that the reason is the fact that there is more than one way to interpret `{1,2,3}` (i.e., as signed/unsigned short, int, long, etc), although a good compiler would be able to deduce it from the type of the declared variable. – barak manos Apr 24 '16 at 07:19
  • 3
    Ironically even the first example is invalid as of C++11. – user657267 Apr 24 '16 at 07:20
  • 1
    /* Do not conform to the grammar */ – BLUEPIXY Apr 24 '16 at 07:21
  • 5
    Well, maybe it would be better to choose between C++ and C. – jdarthenay Apr 24 '16 at 07:22
  • The answer is probably "just because (that's what the C-language standard says)". You could ask a similar question of why functions cannot return a local array but can return a local struct. The standard could have easily handle arrays just as it handles structs, but somebody "up there" had chosen not to, and now we all have to pay the price... – barak manos Apr 24 '16 at 07:23
  • 3
    I'd like to add that in C `int arr[] = {1, 2, 3};` is valid, maybe you want to add it in your question. – jdarthenay Apr 24 '16 at 07:25
  • The answer to your question is outlined here: http://stackoverflow.com/a/1336461/19410 – Alex Reynolds Apr 24 '16 at 07:32
  • I feel the casting you have done in the third case is dangerous. In fact things may be clear in `int *a=(unsigned int[]){4294967295}` but are not that clear in `your_type_one *a=(your_type_two[]){somevalue}` – sjsam Apr 24 '16 at 07:59
  • @barakmanos: What `{1,2,3}` is, is defined by the language standard, and independent of the variable type or a good/bad compiler. And 1,2,3 a.s.o. are ALWAYS `int`s. The question that has to be answered for each line is: Can the expression on the right be used to intialize the variable defined on the left and the answer depends on whether this is C or C++ – MikeMB Apr 24 '16 at 08:37
  • By the third code, you are using a [compound literal](https://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Compound-Literals.html) – Spikatrix Apr 24 '16 at 10:43
  • Strange how this Question got +6 when it's clearly that the OP needs to read a basic C book about pointers and Arrays. He doesn't know what a Pointer is, when he ask about **int *arr = {1,2,3}; – Michi Apr 24 '16 at 11:33
  • @MikeMB: Re " deprecated since in c++11", no, deprecated since C++98, the first C++ standard. Removed in C++11. – Cheers and hth. - Alf Apr 24 '16 at 13:16
  • @Cheersandhth.-Alf: Sorry, my mistake. – MikeMB Apr 24 '16 at 13:18

3 Answers3

6

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.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • 1
    Nice explenation :). – Michi Apr 24 '16 at 11:46
  • "*Note the temporary variable will be destroyed after the statement*" -- The lifetime is actually [the containing block](http://stackoverflow.com/a/14955360/134252). – Dolda2000 Apr 24 '16 at 12:13
  • @Dolda2000 No, local variable is not same as temporary variable. – songyuanyao Apr 24 '16 at 12:16
  • @songyuanyao: What does that have with anything to do? The lifecycle of a compound literal is nevertheless bound to its containing block. – Dolda2000 Apr 24 '16 at 12:18
  • @Dolda2000 Please see the warning message. http://rextester.com/GOGG38475 – songyuanyao Apr 24 '16 at 12:20
  • @Dolda2000 So `{1, 2, 3}` is a literal? What literal? What's its type? – songyuanyao Apr 24 '16 at 12:21
  • @songyuanyao: No, but `(int[3]){1, 2, 3}` is a compound literal, and the lifetime of compound literals are bound to their containing block. See http://stackoverflow.com/a/21882652/134252 – Dolda2000 Apr 24 '16 at 12:24
  • Actually, I assumed this question was only about C. There appears, at least, to be a difference in how this is handled in C and C++, so I suspect we're just talking about different things. – Dolda2000 Apr 24 '16 at 12:26
  • @Dolda2000 I've limited my answer to c++ at the first, so compound literals should be irrelevant. Of course you might say the question should be a c questioin, but it's another issue... – songyuanyao Apr 24 '16 at 12:32
  • `int *arr = {1, 2, 3};` is invalid in `C` too – Michi Apr 24 '16 at 12:38
  • `For this case, a temporary array will be constructed` Is there any differences between [THIS](http://ideone.com/IbHxtT) and =>> [THIS](http://ideone.com/OOCIBj) ? Why is called `temporary`? – Michi Apr 24 '16 at 12:41
  • @Michi According to Dolda2000, for c it seems to be fine. For c++ the 1st code is UB, and most c++ compiler will give a warning for it. – songyuanyao Apr 24 '16 at 12:45
  • @songyuanyao I would like to see if there are any `Advantages` or `Disadvantages` between my two examples :) – Michi Apr 24 '16 at 12:47
  • @Michi For c++, it's easy to select because the 1st code is UB. For c, hmm... I still prefer to 2nd code. You can use `arr` as pointer directly, array could decay to pointer. Anyway, I'm not familiar with c, so just suggestions. :) – songyuanyao Apr 24 '16 at 12:56
  • @songyuanyao If we stick to the `C` standard, this =>> `int *arr = (int[3]){ 1, 2, 3};` wasn't valid with `ISO C90` only later. This means that trying to compile it with `-pedantic` FLAG won't work. – Michi Apr 24 '16 at 13:05
  • 1
    @Michi Yes, I think it's from C99, compound literal. – songyuanyao Apr 24 '16 at 13:08
3
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.

Emerald Weapon
  • 2,392
  • 18
  • 29
  • 1
    Initializing an char array with a initializer list of chars and assigning a string litteral to a char pointer are two completely different operations, and the verbose syntax wiht passing ich characterindividually isn't necessary in the first place. – MikeMB Apr 24 '16 at 09:06
  • 2
    Regarding the first section of your answer: `char *ch = "delhi"` and `char ch[] = {'d', 'e', 'l', 'h', 'i', '\0'}` are not quite the same, so I don't think it's "appropriate" to say that the former has been introduced in order to avoid the latter. In any case, this part of your answer does not address **the context** of the question at hand. – barak manos Apr 24 '16 at 09:16
  • @barakmanos. I agree the two things are not quite the same, but I didn't say they are. I simply stated the fact that in a world without string literals you would have to construct strings char-by-char. On your second point, I frankly think the only way to answer the question is to explain what the three cases are and what the syntax means in each. We can then discuss what 'the context' is and what is not, but I fell it would be an exercise in pointlessness. – Emerald Weapon Apr 24 '16 at 10:05
  • 3
    Oh, and even in c++11 `{1,2,3}` isn't a `std::initializer_list` – MikeMB Apr 24 '16 at 10:11
  • @EmeraldWeapon: Those two expressions serve completely different needs. One creates a local (you can't e.g. return a pointer to it from a function) modifiable array. The other creates a pointer to a (effectively) global read-only array that can e.g. be put in read-only memory ( important for embedded systems) whereas the local array can't. – MikeMB Apr 24 '16 at 10:18
  • int *arr is a Pointer not an Array – Michi Apr 24 '16 at 11:34
  • `avoiding typing something like char ch[] = {'d', 'e', 'l', 'h', 'i', '\0'};` Realy? and you got `+4` :| . `char *ch = "delhi";` means that `ch` points to a `Literal String` which is found in read only (hard coded) memory and `char ch[] = {'d', 'e', 'l', 'h', 'i', '\0'};` means that, that `Literal String` will be copied and latter can be modified. Strange things happens here every day – Michi Apr 24 '16 at 11:43
  • I am re-reading my answer and I really don't see the place where I claim that `char *ch = "delhi"; ` is _the_ _same_ as `ch[] = {'d', 'e', 'l', 'h', 'i', '\0'};`, I only see an explicit remark that the two things are **not** the same (second paragraph). Plese don't interpret, read. – Emerald Weapon Apr 24 '16 at 12:06
  • @MikeMB Can you clarify on the fact that `{1,2,3}` is not an `std::initializer_list` in c++ 11? For example, if I do `auto l = {1,2,3}` then `l`'s type is `std::initializer_list`. – Emerald Weapon Apr 24 '16 at 12:09
  • @EmeraldWeapon Here, where you said: `avoiding typing something like` This makes clear that for you `int *arr` and `int arr[]` are the same when comes about `Literal Strings` – Michi Apr 24 '16 at 12:13
  • @Michi. No, it doesn't. – Emerald Weapon Apr 24 '16 at 12:15
  • @EmeraldWeapon To avoid this `char ch[] = {'d', 'e', 'l', 'h', 'i', '\0'};` you use this `char ch[] = "delhi";` and not this ==>> `char *ch = "delhi"` Any way the last one should be `const char *ch = "delhi";` because of the place where the Pointer points. – Michi Apr 24 '16 at 12:19
  • @EmeraldWeapon: That would be a little long to discuss in comments, but I'd suggest to read this article about [list initialization](http://en.cppreference.com/w/cpp/language/list_initialization). In particular the notes: *A braced-init-list is not an expression and therefore has no type, e.g. `decltype({1,2})` is ill-formed. [...]* TL:DR: a `std::initializer_list` is created from a braced-init-list in many contexts, but `{1,2,3}` itself isn't one. – MikeMB Apr 24 '16 at 13:00
  • What you wrote is: *[...] and has been introduced for the convenience of avoiding typing something like char ch[] = {'d', 'e', 'l', 'h', 'i', '\0'}; [...]* No it wasn't. As michi and I explained before, it serves different purpose. – MikeMB Apr 24 '16 at 13:16
  • @MikeMB Thanks for the clarification on the list initialization. I'm sorry for that misleading statement in my answer, but I still think the rest of it makes it clear enough that I didn't mean to state that a string literal and `char[]` are the same thing. – Emerald Weapon Apr 24 '16 at 14:10
  • @EmeraldWeapon: Sorry, I never wanted to imply that you would. I'm just disagreeing with your reasoning as to why that syntax exists / why it is valid that you expressed in the sentence I quoted. – MikeMB Apr 24 '16 at 15:10
1

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

  1. It has only one element
  2. The the element is of the appropriate type (e.g. a pointer to int or 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.

MikeMB
  • 20,029
  • 9
  • 57
  • 102
  • `The second line is - as far as I know - valid C code,` Invalid in `C` too. – Michi Apr 24 '16 at 12:45
  • @Michi: Thanks, wasn't sure about that, but the compiler produced no error or warning. – MikeMB Apr 24 '16 at 13:19
  • **−1** “you can assign [string literals] to a pointer just like any other array”, no, not in C++. A string literal can decay to pointer to `const`, just like any array. But the first line in the OP's code is invalid (in C++). – Cheers and hth. - Alf Apr 24 '16 at 13:21
  • @Cheersandhth.-Alf: Isn't that exactly what I said? I can assign an array to a pointer thanks to the array to pointer decay. In my vocabulary, *"assing"* means applying the assignement operator. Or is this just about const char* vs char*? I tried to stay clear of that (by saying *pointer*) because their type is differently in c and c++ - but after re-reading my answer again, I see that this might be confusing as I said I'd anser the question for c++. – MikeMB Apr 24 '16 at 13:59
  • **0** Downvote removed since answer's been corrected. @MikeMB: It was just that it *read*, in context, as if the first example line were valid. – Cheers and hth. - Alf Apr 24 '16 at 14:12