2
main()
 {
     const char **a = {"string1","string2"};
     printf("%c", *a); /* prints s */
     printf("%s", a);  /* prints string1 */
     printf("%s", a+1);/* prints ng1 */
 }

GCC v4.8.3 prints "%s" for the last printf, where as http://codepad.org/ prints "ng1".

I thought that the code will create an array of pointers to two strings and the base address assigned to a, which allows normal pointer arithmetic. but it seems that there is something wrong with the assumption.The first printf suggests that my assumption is wrong. can anyone explain why this behavior is observed? ( note that VS 2012 has thrown an error saying too many initalizers where as GCC has thrown a warning for incompatible pointer assignment). I am aware of the warning due to incompatible pointer assignment.

bare_metal
  • 1,134
  • 9
  • 20
  • 4
    `const char **a = {"string1","string2"};` is wrong. Your `main()` doesn't return `int` which is also wrong. Please read about pointers and arrays before assuming that waht you wrote should work. – Iharob Al Asimi Feb 08 '15 at 14:11
  • 3
    The question is not about the absolute correctness of my program, please note that this is a legal program compiled by GCC v4.8.3 with warning. the questions is about why the behavior is strange. – bare_metal Feb 08 '15 at 14:14
  • 1
    @bare_metal it's not a legal program. gcc's treatment of it doesn't change that fact – M.M Feb 09 '15 at 19:41

3 Answers3

2

const char **a is not an array of pointer to two strings. It declares a to be a pointer to pointer to const char.

const char **a = {"string1","string2"}; //No memory is allocated to store string literals 

will invoke undefined behavior and you may get either expected or unexpected results.
To declare a as an array of two pointers you need to change the declaration as

const char *a[] = {"string1","string2"};
haccks
  • 104,019
  • 25
  • 176
  • 264
  • I am aware of that, but will you please help me understand why the program behaves the way it is . if i create an array of pointers and then cast it back to a char** then things work as expected – bare_metal Feb 08 '15 at 14:16
  • how do we say that memory is not allocated, in char* a = "string1" where string is stored in .rodata? – bare_metal Feb 08 '15 at 14:22
  • @bare_metal; Do you think `char* a` and `char** a` are same? – haccks Feb 08 '15 at 14:25
  • i don't think so , but i do think that char* a = "string" and char **a = {"string1","string2"}; has to store them somewhere and prints confirm that it is stored. – bare_metal Feb 08 '15 at 14:27
  • @bare_metal; You need to read more on [pointers and arrays](http://www.c-faq.com/aryptr/index.html). – haccks Feb 08 '15 at 14:30
  • @hackks The original is ill-formed (not merely UB) , a brace-enclosed initializer list (with more than one entry) can only be used to initialize an aggregate. C11 6.7.9/11 – M.M Feb 09 '15 at 19:40
  • @MattMcNabb; 6.7.9 p2: *No initializer shall attempt to provide a value for an object not contained within the entity being initialized.* If a constraint is violated then its UB. – haccks Feb 10 '15 at 13:25
  • @haccks constraint violation is a stronger condition than UB. It is UB *and* a constraint violation, which requires a diagnostic. – M.M Feb 11 '15 at 01:10
2

The memory range in your program's stack looks like this: (notice that it is not allocated before assignment which is wrong)

char** a = {s, t, r, i, n ,g, 1, \0, s, t, r, i, n, g, 2, \0}

Therefore when you print the command:

printf("%c", *a);

You are dereferencing the first character of the string which is 's'.

On the other hand, when you print the command:

 printf("%s", a);

you are printing a string that starts at pointer a and finishes at '\0'. That's why you see the output 'string1'.

Lastly, when you type "a+1" you increase the pointer in one step (example here: How to increment a pointer address and pointer's value?). in this case because char** is a pointer, and every pointer is 4 byte, the "+1" jumps 4 chars forward. Therefore when you print the command:

printf("%s", a+1);

The printf starts at the pointer 'a' + 4 bytes and ends at '\0'. That's why the output is 'ng1'.

Hope it was clear enough.

Community
  • 1
  • 1
nerez
  • 437
  • 4
  • 18
  • Thanks for trying,but the explanation seems to be wrong – bare_metal Feb 08 '15 at 15:34
  • 1
    Please elaborate. What exactly do you think is wrong? I would be happy to edit my answer if needed. – nerez Feb 09 '15 at 06:05
  • const char **a = {"string1","string2"}; and const char** a = {s, t, r, i, n ,g, 1, \0, s, t, r, i, n, g, 2, \0} are not same ( ' ' are omitted ) , when i try printf("%s", a); i get a seg fault. – bare_metal Feb 09 '15 at 08:34
  • so why did you write that when you type printf("%s",a); //prints string1? – nerez Feb 09 '15 at 11:57
  • const char **a = {"string1","string2"}; prints string1 where as const char** a = {s, t, r, i, n ,g, 1, \0, s, t, r, i, n, g, 2, \0} crashes on printing. basically i mean to say both are not same. – bare_metal Feb 09 '15 at 12:47
  • it seems eventually i figured out the thing. see http://stackoverflow.com/questions/28412577/int-q-1-2-peculiar-initialization-list – bare_metal Feb 09 '15 at 14:54
  • 2
    Obviously I didn't mean that a = {s, t, r, i, n ,g, 1, \0, s, t, r, i, n, g, 2, \0}. I meant the memory range which starts in 'a' looks the following. Never mind. I'm glad you figured it out. – nerez Feb 09 '15 at 14:57
1

This is due to the following peculiar initialization performed by GCC. please see int q = {1,2}; peculiar initialization list. the statement const char **a = {"string1","string2"}; results in a being treated as if const char **a = "string1". this solves the mystery as *a would print 's', a would print string1.

Community
  • 1
  • 1
bare_metal
  • 1,134
  • 9
  • 20