8

Can someone explain why this simple ternary operation won't even compile, in C?

void main(int argc, char *argv[]){
    int a = atoi(argv[1]);
    char foo[] = (a == 1) ? "bar1" : "bar2";
}

It seems to be a problem with strings in particular.

Kevin Reid
  • 37,492
  • 13
  • 80
  • 108
Pier-Alexandre Bouchard
  • 5,135
  • 5
  • 37
  • 72
  • Wait a minute, in C? Why is your main return void if you're working in C? It should be an int. – TheZ Oct 02 '12 at 16:19
  • 2
    returning void in allowed but not recommended. There's a void main.. – Pier-Alexandre Bouchard Oct 02 '12 at 16:22
  • Microsoft's compiler will happily accept it, others will not, but it could not be the first time MS did things their were. See http://stackoverflow.com/questions/4207134/what-is-the-proper-declaration-of-main for a very nice discussion about this. – WhozCraig Oct 02 '12 at 16:24

2 Answers2

16

A string literal "bar", when used in an expression (in this case, inside the ternary operator), is a pointer to pre-allocated memory. You cannot initialize an array using a pointer to data, only using a literal ("..." or {...}).

Instead, you could assign it to a char *:

const char *foo = (a == 1) ? "bar1" : "bar2";

This will not make a copy of the literal but point to it, so you should not modify the elements of foo. If you need a copy, you can use memcpy, provided that you know how big to declare the array:

char foo[5];
memcpy(foo, (a == 1) ? "bar1" : "bar2", sizeof foo);

If you particularly want to be able to assign the contents, then there is a trick to doing that; it is possible to implicitly copy the contents of a struct using assignment (as well as returning it from a function, and so on), no matter what the struct contains, and you could put a character array in a struct:

typedef struct { 
  char contents[5];
} mystring;

mystring foo = (a == 1) ? (mystring){"bar1"} : (mystring){"bar2"};
// You can also use assignments.
foo = (mystring){"baz"};

Just like the second option, when you do this you have to pick a fixed size for the array in the struct declaration. If you have strings of unbounded length, then you must use pointers.

Kevin Reid
  • 37,492
  • 13
  • 80
  • 108
  • Oh, I learn something today! I didn't know it was in a pre-allocated memory! – Pier-Alexandre Bouchard Oct 02 '12 at 16:24
  • You can initialize `char foo[]` with either `"bar1"` or `"bar2"` directly. But you can't do that via an expression. The standard says: "An array of character type may be initialized by a character string literal, optionally enclosed in braces." The `?:` expression is not a string literal, even though in most other contexts the types and the values of `"bar1"` and `1?"bar1":"bar2"` are going to be exactly the same. – Alexey Frunze Oct 02 '12 at 16:31
2

Your ternary operation is fine, but you can't initialize the array with the resultant pre-allocated memory where "bar1" and "bar2" reside.

int a = atoi(argv[1]);
char foo[5];
memcpy(foo,((a == 1) ? "bar1" : "bar2"), 5); 

Would be a possible way to keep doing what you're doing.

Mike
  • 47,263
  • 29
  • 113
  • 177