28

If I declare and use a pointer like this:

int counter;
char *pCow = "pCow goes MOO";

for(counter = 0; counter < 14; counter++)
    printf("%c", pCow[counter]);

it displays the entire string and works and yeah and there is much rejoicing.

However, if I use an initializer like this:

int counter;
char *pCow = {'p','C','o','w',' ','g','o','e','s',' ','M','O','O','\0'};

for(counter = 0; counter < 14; counter++)
    printf("%c", pCow[counter]);

the program crashes and pCow refuses to moo for my hedonistic pleasure!

3 Warnings. 0 Errors
line 11 (near initialization for 'pCow') [enabled by default]   C/C++ Problem
line 11 excess elements in scalar initializer [enabled by default]  C/C++ Problem
line 11 initialization makes pointer from integer without a cast [enabled by default]   C/C++ Problem

Lovingly tested in Eclipse CDT.

Michael-O
  • 18,123
  • 6
  • 55
  • 121
Jawi Mmm
  • 243
  • 2
  • 8
  • 3
    If this is C (and not C++) then `char *pCow = (char[]){'p','C','o','w',' ','g','o','e','s',' ','M','O','O','\0'};` should work and have the same effect. – user253751 Oct 16 '15 at 06:57
  • Note that @immibis solution above ↑ requires C99 or above. Look up on _Compound Literals_ to know more about it. – Spikatrix Oct 16 '15 at 16:17

3 Answers3

40

In this case, pCow is set to the address of a c-string in static memory:

char *pCow = "pCow goes MOO";

In this case, pCow is set to the value 'p' (i.e., 112):

char *pCow = {'p','C','o','w',' ','g','o','e','s',' ','M','O','O','\0'};

Since the address 112 most likely points into restricted/invalid memory, that causes your program to blow up when you try to access pCow[counter].

The warning "excess elements in scalar initializer" is telling you that it's ignoring all of the stuff after the 'p' since the pointer only needs one value.

The warning "initialization makes pointer from integer without a cast" is telling you that you're using 'p' as a pointer, which probably isn't a good idea...

What you want to do is declare pCow as a character array rather than a character pointer if you want to use the initializer syntax:

char pCow[] = {'p','C','o','w',' ','g','o','e','s',' ','M','O','O','\0'};
DaoWen
  • 32,589
  • 6
  • 74
  • 101
  • Makes sense. Thanks! ur hot. :| (can't accept as answer yet tho. button won't click. Ill get back to it!) – Jawi Mmm Oct 16 '15 at 05:33
  • 2
    @JawiMmm - Yeah, [they do that on purpose](http://meta.stackexchange.com/questions/50697/time-limit-on-accepting-an-answer). In any case, I'm glad this helped you! On a separate note, [using a `char*` to point to a c-string literal isn't a good idea either](http://stackoverflow.com/questions/16767042/deprecated-conversion-from-string-constant-to-char-in-c?lq=1) (you should use a `const char*` instead). – DaoWen Oct 16 '15 at 05:42
  • Note the difference here: using `char[]` creates the array as a local variable, and copies static data to it, while `char *` just creates a pointer as local variable, which then may point to static data without copying anything (which is why you want that `const char *` to point to string literals). – hyde Oct 16 '15 at 06:21
  • 2
    In GCC you can write it like this:`char *pCow = (char []) {'p','C','o','w',' ','g','o','e','s',' ','M','O','O','\0'};`. This way you have a pointer which points to an array stored in static memory which is the same as using quotes. However this syntax allows you to use not just character arrays which can be handy. – aragaer Oct 16 '15 at 07:15
  • 2
    @aragaer that syntax is part of Standard C – M.M Oct 16 '15 at 07:49
  • Ah, well, I've been checking the https://gcc.gnu.org/onlinedocs/gcc/Compound-Literals.html and was fooled by the fact that it is listed in "C Extensions" section. – aragaer Oct 16 '15 at 08:37
11

"pCow goes MOO" is a string literal and have two different uses. Either you can use it as an initializer to an array:

char aCow[] = "pCow goes MOO";

In which case the contents of the string literal are copied into the array.

Or alternatively, you can use a string literal as a stand-alone constant array anywhere in your program. For example strcpy(cow, "pCow goes MOO");. So there's a distinctive difference between these two:

char aCow[] = "pCow goes MOO";
char* pCow  = "pCow goes MOO";

In the first case, the literal is copied into the array. In the second case, the literal remains a stand-alone constant in read-only memory, which we point to with a pointer. The former can be modified, the latter cannot.

As for the case of

char *pCow = {'p','C','o','w',' ','g','o','e','s',' ','M','O','O','\0'};

You are using a pointer, but you have no string literal. Instead you have an initializer list intended for an array. A good compiler would warn you for "excess initializer". The reason that the code compiles is a very odd rule in C which is allowing plain variables to be initialized with braces, for example int x = {1};. So the compiler uses this rule to initialize your pointer to point at the address 'p', which is of course nonsense, and then it discards the rest of the initializer list.

Lundin
  • 195,001
  • 40
  • 254
  • 396
0

Because char * points the data to the Text segment in the memory and char a[ size ] stores data in the stack. The data in the stack can be modified but the data in the text segment can't be modified.