4

Why I can't define an array

char **pp={ "123", "456", "789" };

But I can define it as a char*[] ,and send it to a function that will accept it as a char **

char *pp[]={ "123", "456", "789" };
fun(pp);

void fun(char **pointerToPointer)
{
    //++(**pointerToPointer);//error
    printf("%s", *pointerToPointer); 
}
//output::"123"

And why I can't increment

++(**pointerToPointer);
Spikatrix
  • 20,225
  • 7
  • 37
  • 83
Zag Gol
  • 1,038
  • 1
  • 18
  • 43
  • I can define `char **pp={ "123", "456", "789" };` .. What compiler are we talking about? – Eugene Sh. May 29 '15 at 14:32
  • What is the purpose of using `++(**pointerToPointer)` ? – Subinoy May 29 '15 at 14:33
  • 1
    @EugeneSh. , With warnings though... – Spikatrix May 29 '15 at 14:35
  • 1
    "_And why I can't increment `++(**pointerToPointer);`_" Because the strings you have are string literals and they are immutable. – Spikatrix May 29 '15 at 14:36
  • I am working with VS 2013. the purpose of using ++(**pointerToPointer); is that printf("%s", *pointerToPointer); will print "223" – Zag Gol May 29 '15 at 14:41
  • @ZagGol , If you want to modify the string, use `char pp[][100]={ "123", "456", "789" };`. Replace the 100 with the maximum length of a string-1. Also change `void fun(char **pointerToPointer)` to `void fun(char (*pointerToPointer)[100])` – Spikatrix May 29 '15 at 14:42
  • The next question should be why this declaration is good: `char *p = "abc";` – Eugene Sh. May 29 '15 at 14:53
  • @CoolGuy I am not getting the reason behind the increment. Please tell me more about it – Subinoy May 29 '15 at 14:59
  • Hm. It's actually interesting why `char *p = "ab";` is not the same as `char *p = {'a', 'b', '\0'};` – Eugene Sh. May 29 '15 at 15:05
  • @Subinoy , Increment if you want to change the first character of the string. For example, `"123"` will give `"223"` when the first character is incremented. – Spikatrix May 29 '15 at 15:10
  • @CoolGuy are you getting the `"223"` output. Mine is making runtime error – Subinoy May 29 '15 at 15:14
  • 1
    @Subinoy , Because of [this](http://stackoverflow.com/questions/30532077/c-char-array-and-char-pointer/30532586?noredirect=1#comment49138180_30532077). Fix it by [this](http://stackoverflow.com/questions/30532077/c-char-array-and-char-pointer/30532586?noredirect=1#comment49138449_30532077) – Spikatrix May 30 '15 at 08:23
  • @CoolGuy you mean "replace the 100 with the maximum length of a string +1" – M.M May 30 '15 at 11:36
  • @MattMcNabb , Yep. Thanks for pointing it out! – Spikatrix May 30 '15 at 11:37

2 Answers2

2

To answer the first question, the principles might be clearer if we use a single depth of pointer. This code is illegal for the same reason:

int *ptr = { 1, 2, 3 };

In C, a braced initializer list is not an object (especially not an array). It can only be taken as a list of items from which to read initializers when an object is being initialized.

ptr is one object, so at most one initializer could be taken, and the expected form of that initializer is a pointer (which 1 is not).

In fact this code is explicitly illegal under C11 6.7.9/11:

The initializer for a scalar shall be a single expression, optionally enclosed in braces

However, there is a gcc bug/feature where it permits excess initializers for a scalar and ignores them. Further, some compilers may "be helpful" and "only" issue a warning, and initialize ptr to point to address 1, wherever that might be.

"scalar" means an object that's not a struct or an array.


Since C99 you can write:

int *ptr = (int []){1, 2, 3};

which creates an array (using the same storage duration as ptr) and points ptr at its first element.

This array is mutable; for a non-mutable one use int const *ptr = (int const[]){1, 2, 3}; instead.


Replacing int by char *, we see that you could write:

char **p = (char *[]){ "123", "456", "789" };

in which case the pointers in the array are mutable, but the things they point to (i.e. the string literals) still aren't.

Note that you should always use char const * when dealing with string literals, because they are not mutable. The fact that string literals have type char [N] is a historical hangover from before const was added to C. So:

char const **pp = (char const *[]){ "123", "456", "789" };

or with non-mutable pointers to strings:

char const *const *pp = (char const *const []){ "123", "456", "789" };
M.M
  • 138,810
  • 21
  • 208
  • 365
1

Here char *pp[]={ "123", "456", "789" }; is correct because:

  • Here it is an array of pointers which can be used to point to an array of data items with each element of the pointer array pointing to an element of the data array.
  • As we assign each array to a char pointer, it works fine.

But in case of char **pp={ "123", "456", "789" };

  • Here the memory initialization is not happening.
  • Instead of this first string is assigned to the pointer and then compiler generates warning saying other many values are discarded.

As CoolGuy said ++(**pointerToPointer) it is error because it accessing 1 of "123" string, and trying to change the value of 1 to 2, and as these are string literals assigning can't be done. You can only see the increment value of print: printf("%c\n", **pointerToPointer+1); which will give output : 2

You can solve it by the solution provided by CoolGuy in the comment-

  1. By changing char *pp[]={ "123", "456", "789" }; to char pp[][100]={ "123", "456", "789" };
  2. By changing void fun(char **pointerToPointer) to void fun(char (*pointerToPointer)[100])

It is correct because here the value of **pointerToPointer is getting changed.

These are the reasons to me. And for the warning:

warning: excess elements in scalar initializer [enabled by default]

you can see the link in here which is the best explanation of it to me.

Community
  • 1
  • 1
Subinoy
  • 478
  • 7
  • 22