5

C lets me use char pointers and arrays interchangeably often enough that I often think of them as completely interchangeable. But the following code demonstrates this is not true. Can anyone please explain why the initialization of const char d[] with the ternary operator, in the code below, is illegal?

/* main.c */
#include <stdio.h>

int main()
{
  const char* a = "lorem";
  const char b[] = "ipsum";
  int* p;
  const char* c = ( *p ? "dolor" : "sit" );
  const char d[] = ( *p ? "amet" : "consectetur" ); // Why am I an error?
  return 0;
}

Compilation:

> gcc -g main.c 
main.c: In function \u2018main\u2019:
main.c:10:20: error: invalid initializer
   const char d[] = ( *p ? "amet" : "consectetur" ); // Why am I an error?

Related question: in case my terminology has been imprecise here: what is the correct term to describe const char d[]? Is it an array? A variable-length array? Something else? It is not considered a pointer - true?

Edit: I believe this question is not answered by Array initialization with a ternary operator?

RE: the referenced question, I believe the premise is slightly different. E.g. the accepted answer explains that { 1, 2 }; (or { 'a', 'b' );) are not valid C expressions, which I know already and accept. However "amet"; and "consectetur"; are valid C expressions.

StoneThrow
  • 5,314
  • 4
  • 44
  • 86
  • Arrays and pointers are almost completely unrelated. However, arrays can be implicitly converted to pointers. That's all. – melpomene Jan 29 '19 at 18:45
  • 1
    Possible duplicate of [Array initialization with a ternary operator?](https://stackoverflow.com/questions/15877560/array-initialization-with-a-ternary-operator) – Daniel Jan 29 '19 at 18:48
  • @melpomene - right: an array is a fixed block of memory that potentially holds user-initialized content, and a pointer is effectively an integer whose value happens to be a memory location. Is that a fully true, or are there subtleties I've missed? – StoneThrow Jan 29 '19 at 18:48
  • @StoneThrow C doesn't guarantee a pointer is effectively an integer. There have been "weird" implementations of C where pointers acted more like structs under the hood. But yeah, that's pretty much it. Keep in mind that each pointer has a type, which affects e.g. pointer arithmetic. – melpomene Jan 29 '19 at 18:53
  • @Daniel - I saw that one too, but the accepted answer doesn't completely fit. E.g. I understand and agree that `{ 1, 2 };` or `{ 'a', 'b' };` are not valid C expressions, but `"amet";` and `"consectetur";` are. – StoneThrow Jan 29 '19 at 18:55
  • 1
    `p` is uninitialized, so you don't know what it's pointing at. However, that's mostly tangential to getting the code to compile. – Jonathan Leffler Jan 29 '19 at 19:04

4 Answers4

5

6.7.9 Initialization

...
14 An array of character type may be initialized by a character string literal or UTF−8 string literal, optionally enclosed in braces. Successive bytes of the string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array.

C 2011 Online Draft

( *p ? "amet" : "consectetur" ) is not a string literal, nor does it evaluate to a string literal. It evaluates to an expression of type char *, which on its own is not a valid array initializer, and that evaluation does not occur until runtime.

Not to mention, p is uninitialized, so the expression is undefined to begin with.

Community
  • 1
  • 1
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • If the conditional initialization of array `d` worked, what would `sizeof(d)` return? Would that depend on what `p` was initialized to? You can't initialize VLAs at all, of course. – Jonathan Leffler Jan 29 '19 at 19:27
  • @JohnBode - So if I understand correctly, and wrt pre-`C11`: `char arrays` can only be initialized by string literals like `"abc"` or `{ 'a', 'b', 'c, '\0' }` (the former is implicitly the latter if I'm not mistaken), and a ternary operation with two string literals on either side of the `:` **does not** evaluate to a string literal, but to a `char*` - is that accurate? – StoneThrow Jan 29 '19 at 20:15
  • 1
    @StoneThrow: Correct. In the context of the `?:` operator, the string literals are implicitly converted to expressions of type `char *`, and they evaluate to the address of the first character of each string. Remember that `?:` is evaluated at run time, so the result cannot be the initializer of an array. – John Bode Jan 29 '19 at 20:27
2

String literals are slightly magical. Ordinarily (when used in expressions), they represent arrays. Arrays can "decay" (implicitly convert) to pointers, which is why e.g.

const char *p = "foo";

is valid. "foo" is a normal expression here. You could also write

const char *p;
p = "foo";  // assignment, not initialization

However, if a string literal is used to initialize an array, it behaves like an initializer list of characters:

char s[] = "foo";
// equivalent to:
char s[] = { 'f', 'o', 'o', '\0' };

In your example of

const char d[] = ( *p ? "amet" : "consectetur" );

the initializer is not a sole string literal; it is an expression. Hence both string literals decay to pointers, and then you get an error because you cannot initialize an array from a pointer. (In fact, you cannot initialize an array from an expression at all.)

melpomene
  • 84,125
  • 8
  • 85
  • 148
  • Ok, I think I'm getting the picture: you cannot initialize a _`char` array_ with a pointer - I can get with that. When a string literal - _and only a string literal_ - is used to initialize an array, it is implicitly an array of chars. E.g. `char c[] = "abc";` is implicitly `char c[] = { 'a', 'b', 'c', '\0' };`. But when string literals are on either side of the `:` in a ternary operator, they're no longer implicitly `char` arrays, but decay to their respective pointers, and as noted before, an array cannot be initialized with a pointer - is that an accurate summary? – StoneThrow Jan 29 '19 at 20:25
  • 1
    @StoneThrow Mostly yes. It seems to imply `{ 'a', 'b', 'c', '\0' }` is an array of chars, which it is not (it is special initializer syntax). String literals are arrays of chars everywhere else. – melpomene Jan 30 '19 at 06:12
0

You can only initialize an array with an initializer-list not with an expression. In your code

const char* c = ( *p ? "dolor" : "sit" );  

Here c is a pointer variable and string constants represents address only. Thats why we can use ternary operator and we can assign the address of string constant to pointer c and we can initialize the array. But

const char d[] = ( *p ? "amet" : "consectetur" );

Here d is an array but d represents the address of the array and string constants also represents address only. Therefore we cant assign address to address means string constant to array d. Thats why we will get error.

Please visit the link for better understanding:

https://www.geeksforgeeks.org/whats-difference-between-char-s-and-char-s-in-c/

Array initialization with a ternary operator?

Bishal Dubey
  • 150
  • 7
  • I saw that one too, but the accepted answer doesn't completely match my premise. E.g. I understand and agree that `{ 1, 2 }`; is not a valid C expression, but "amet"; and `"consectetur";` are. – StoneThrow Jan 29 '19 at 18:56
  • 1
    @StoneThrow *but "amet"; and "consectetur"; are.* And that's why you can't use them to initialize an array. An initializer can only be an assignable expression or an initializer list. But you can't assign anything to an array. – Andrew Henle Jan 29 '19 at 19:06
0

You are in error before you attempt to define d. In attempting to define c you ask the question *p ? But p is not initialised. The behaviour is I believe not defined.

Peter
  • 393
  • 3
  • 15