That is not correct. While a 2-D array could be referred to as a char**, you can't really construct or pass a 2-D array in that manner. Plus, the vast majority of uses for char** that I've seen in over 20 years of C/C++ working experience have had absolutely nothing to do with any two-dimensional arrays.
Originally Posted by C-Nerd
The problem lies with
char **ptr = "hellow";
as is explained by the warning:
eh.c:5: warning: initialization from incompatible pointer type
Though some compilers would have worded that warning differently, citing "differing levels of indirection." The key lies in those "differing levels of indirection."
Have you read at least Part 1 of Skelly's Pointer Power in C and C++ yet? You really need to, because he covers levels of indirection very well. After all, he developed the material in those articles while teaching C and C++ for the previous ten years.
Here's a particularly pertinent part, though in reading it here you're missing out on a lot of information and clarification -- BTW, in this quote, p is a pointer:
If two pointers are at different levels of indirection, then they are not compatible. That means that you cannot assign one to the other, you cannot compare them, and if a function expects a pointer at a particular level of indirection then you cannot pass it a pointer at a different level of indirection.
Get it! Got it? Good!
Now apply that to your question.
"hellow" is treated as a char array, hence as a pointer to char. It is a char* and hence is at the first level of indirection. You declare ptr to be a char**, which is at the second level of indirection. ptr and the string are at different levels of indirection, which means that they are incompatible, which is precisely what the warning tells you. What part of "incompatible pointer type" do you not understand?
char **ptr = "hellow";
Now, what does the code do there? Who knows? You're doing something wrong, so the compiler doesn't really know what to do with it. It appears that your compiler went ahead and assigned the address of that string to ptr, even though ptr is supposed to contain the address of a pointer to that string. A different compiler could very well do something different. We have no way of predicting that. Like one forum member's signature says about depending on undefined behavior, it's like dancing barefoot on broken glass.
Now there's the line,
which generates the warnings:
Think about that one. With "%s", you're telling printf that you're going to give it a char*, a level-1 pointer. But ptr is a level-2 pointer, a char**. What is that called now? That's right, an "incompatible pointer type". And again you're doing something wrong that confuses the compiler, so in its confusion the compiler does whatever it was designed to do, which could be just about anything. Again, we cannot predict what code the compiler will generate in this case, nor what it will do, plus a different compiler can very well do something entirely different. You've shot yourself in the foot again.
Now, it appears from what you describe that your compiler just takes an address in ptr, which should be a level-two pointer containing the address of a level-one pointer, and uses it instead as if it were a level-one pointer. When it uses ptr in that manner in which it shouldn't be used, then you get the reported result:
But the only reason that it superficially appears to work is because the code is broken and not working right.
Originally Posted by 2001goran
What the hell are you trying to do there? If you intended to print out the pointer value that ptr contains, then you should have used "%p". But as it is, you don't know the address of the string so you don't have anything to compare that value to in order for it to make any sense.
Originally Posted by 2001goran
Did you instead intend to write printf("%s",ptr) ? In that case, you should have gotten garbage because the code is broken and not working right.
What appears to be happening with "%s" is that ptr, a level-two pointer, actually contains a level-one pointer value. When you use it incorrectly (ie, printf("%s",ptr) ), then it appears to work because it's using that level-one pointer value to satisfy printf's need for a level-one pointer value. But now that you try to use it correctly, that level-one pointer value is being misused as if it were a level-two pointer value. So then, you dereference ptr to go to the location of "hellow" which it thinks contains the address to the string, whereas in reality that location contains the string itself. That means that the characters in the string get misinterpreted as being a pointer value. Pointing to where? Well, take the ASCII values of the first four characters (0x68, 0x65, 0x6C, 0x6C) and that will give you the 32-bit address of 0x6C6C6568.
The line you gave us, printf("%s",**ptr), is just too frakked up for words. You want to get the first character of the string, but instead you get the character at the address, 0x6C6C6568. But instead of a char, you promised printf a char*, so it's not at all happy about that: "format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int’"
Here's a scenario in which you could use ptr as a level-two pointer:
Read it and understand what it's doing.
char *p1 = "hellow";
char **ptr = &p1;
Bottom Line: Unless you are going to be using some intermediate pointer, ptr needs to be declared as a level-one pointer, a char* .