4

I would like to know if I can assign a pointer to pointer to char directly to a string literal without first assigning the string literal to an lvalue pointer first. Usually if an argument takes a pointer to something, you can pass the address of the object using &, and a string literal seems to be (or at least decays to) a char pointer because you can assign them to char pointers. This is why I expected one of the following to work:

const char ** ppchar = &"StringLiteral"; // This doesn't compile

const char ** ppchar = (const char **)&"StringLiteral"; // This compiles, but...

std::cout << *ppchar;   // Throws an exception

I expected at least the second one of these to work, because "StringLiteral" seems to me to at least decay to a pointer, so in:

const char** ppchar = (const char**) &"StringLiteral";

The first pointer would point to the address of the string literal. If the string literal is assigned to an lvalue pointer first, the first pointer would be pointing to the second pointer, which is essentially holding its address. Seems the same to me.

Edit: Also, it seems I can do:

const char * _pptr = (const char*)&"StringLiteral";
std::cout << _pptr; // Prints "Hello"

So it doesn't seem to a rule against assigning directly to the string literal.

Edit: As is so obvious to many, I have my levels of indirection wrong. This is explained by Vlad from Moscow in his answer. In:

const char ** pptr = (const char**)&"StringLiteral";

The compiler expects pptr to point to something that stores the address of a char, whereas I've assigned it to the address of the "StringLiteral" itself, which means when I cout it dereferences the first pointer to find which address find the char. The problem is it's pointing directly to the address of the "StringLiteral", meaning it uses the value of the first letter of "StringLiteral" as the address to go to to find the char, basically it'll use "S" as the address to go to. Hate being so thick.

Daniel H
  • 7,223
  • 2
  • 26
  • 41
Zebrafish
  • 11,682
  • 3
  • 43
  • 119
  • Oh boy, see https://www.tutorialspoint.com/cprogramming/c_pointers.htm to learn about pointers. There is much much wrong here. `&"stringLiteral"` will never work. – Burstful Oct 18 '17 at 21:14
  • @Zebrafish You need to declare an intermediate variable of the type char * and initialize it be the string literal. And then you can declare another variable of the type char ** and assign it with the address of the intermediate variable. You may not apply the operator & to an rvalue. – Vlad from Moscow Oct 18 '17 at 21:15
  • This makes no sense. A pointer to a pointer cannot possibly be compatible with a pointer to a character array. The former requires one more level of indirection to reach the character than the latter does. Try to find a good, basic tutorial on pointers. – Tom Karzes Oct 18 '17 at 21:16
  • @Zebrafish What are you trying to achieve using a pointer of the type char ** instead of a pointer of the type char *? – Vlad from Moscow Oct 18 '17 at 21:18
  • 1
    I'm learning the Vulkan API, and it has a bit much like the (int argc, char** args), I tried to assign a string literal directly, I thought it was OK, I've obviously got things completely wrong – Zebrafish Oct 18 '17 at 21:20
  • 1
    @Vlad from Moscow No but that's just it, at first I thought it was an rvalue, but a string literal isn't an r value is it? It has a place allocated in memory for it. – Zebrafish Oct 18 '17 at 21:21
  • 3
    @Zebrafish String literals have types of constant character arrays. – Vlad from Moscow Oct 18 '17 at 21:22
  • @Vlad from Moscow And if you assign it to a char pointer it then becomes a char pointer, and then you can make a pointer that points to that char pointer. I just thought you could do it directly. – Zebrafish Oct 18 '17 at 21:24
  • @Zebrafish No you can not do it directly because you may not take an address of an rvalue. Otherwise you could write char **p = &( "String literal" + 0 ); – Vlad from Moscow Oct 18 '17 at 21:26
  • @Vlad from Moscow But you just said a string literal is of type char array, how's a char array an rvalue? – Zebrafish Oct 18 '17 at 21:27
  • @Zebrafish Used in the expression "String literal" + 0 the string literal is implicitly converted to pointer to its first element. So the expression ( "String literal" + 0 ) has the type char * but it is an rvalue. So you may not apply the operator & to the expression. – Vlad from Moscow Oct 18 '17 at 21:29
  • 1
    @Zebrafish Take into account that for example the expression &"Hello" has the type char ( * )[6]. It is not the same as the type of the expression "Hello" + 0 that has the type char *. – Vlad from Moscow Oct 18 '17 at 21:31
  • 2
    String literals are of the type `const char[N]` for some N. So what you do is casting a `char const (*)[N]` to a `char const**`. You can't alias an array by a pointer. I say this for what feels like the umpteenth time (on SO), arrays are not pointers. And pointers are not arrays. – StoryTeller - Unslander Monica Oct 18 '17 at 21:33
  • @Vlad from Moscow I just found out I can do const char * pptr = (const char*)&"StringLiteral"; std::cout << pptr; Works fine. Hmmm, I don't know what to think. – Zebrafish Oct 18 '17 at 21:42
  • Possible duplicate of [What is the type of string literals in C and C++?](https://stackoverflow.com/questions/2245664/what-is-the-type-of-string-literals-in-c-and-c) – underscore_d Oct 18 '17 at 21:44
  • 1
    @Zebrafish There is notfhing to think. You are using a pointer of the type const char * instead of the pointer of the type const char ** that is referred in your question. – Vlad from Moscow Oct 18 '17 at 21:44
  • @Vlad from Moscow Just saying that it goes against what you said, that I can't apply & operator to the string literal because it's an rvalue. – Zebrafish Oct 18 '17 at 21:47
  • @VladfromMoscow A string literal is an lvalue. All the other literals are rvalues, but a string literal is an lvalue. You can take its address, which is why the second option even compiles. It just doesn’t do what the OP expected. – Daniel H Oct 18 '17 at 21:52
  • @Zebrafish I did not say this. I said that you main not apply the operator to the expression "StringLiteral" + 0. – Vlad from Moscow Oct 18 '17 at 21:54
  • @DanielH I do not understand why you are saying this me. – Vlad from Moscow Oct 18 '17 at 21:55
  • @VladfromMoscow It sure looked like you were saying the string literal itself was an rvalue. Re-reading it I see what you mean, but that was not as clear in your comments as you intended. – Daniel H Oct 18 '17 at 21:59

2 Answers2

5

This expression

&"StringLiteral"

has the type const char( * )[14] in C++ and char ( * )[14] in C because the string literal has respectively the type const char[14] or char[14].

So you may not assign such a pointer to an object of the type const char ** or char ** because the pointers are incompatible.

And if you will try to use a casting as in this declaration

const char** ppchar = (const char**) &"StringLiteral";

nevertheless dereferencing this pointer like *ppchar does not make sense because instead of the object of the type char * as it would be expected you will get the value that corresponds to the value of the first character of the string literal instead of an address value.

You could for example at first convert the string literal to the type const char * (or char * in C) the following way

StringLiteral + 0

because in such expressions a string literal is converted to pointer to its first character. However you may not apply the unary operator & to this expression like

&( StringLiteral + 0 )

to get an object of the type const char ** because you may not apply the operator to an rvalue.

So what you could do is the following

const char *pchar = "StringLiteral";
const char **ppchar = &pchar;
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • I see your point. As someone said I think I got my levels of indirection wrong. As you said it points to the first char. Though, I wonder, when I do cout << ppchar; or cout << *ppchar; it should print the word or the first letter, but it throws an exception. – Zebrafish Oct 18 '17 at 21:57
  • @Zebrafish The compiler considers the value stored in the expression *ppchar as a value of an address. But instead of an address it gets the first character of the string literal and interprets it as address trying to access memory using the character as an address. – Vlad from Moscow Oct 18 '17 at 21:59
  • Ohhhhh, that makes sense. – Zebrafish Oct 18 '17 at 22:00
  • "you will get the value that corresponds to the value of the first character of the string literal" - in fact it's a strict aliasing violation so anything could happen – M.M Oct 22 '17 at 04:11
1

In addition to @Vlad from Moscow fine answer.

Can I assign a pointer to char pointer directly to a string literal?

Almost. In C, code can use a compound literal to form a const char * initialized with the string literal and then take the address of that pointer to assign to ppchar.

const char **ppchar = &((const char *){"StringLiteral"});
puts(*ppchar);
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 1
    That's almost like what I tried doing, which was const char ** ppchar = &((const char*)"StringLiteral"); But I think the cast makes it an rvalue, so I can't take the address of it I guess. – Zebrafish Oct 18 '17 at 22:18
  • Isn't `(const char *){"StringLiteral"}` a temporary, making `ppchar` the address of a temporary and thus unusable without UB? – Daniel H Oct 20 '17 at 15:35
  • `"StringLiteral"` is a string literal. In C, `(const char *){"StringLiteral"}` is a compound literal. In C++ it is not, because C++ doesn't have the concept of compound literals; there, it is a cast of `"StringLiteral"` to a temporary `const char *`. The question is tagged both, but uses C++ constructs. See [here](https://godbolt.org/g/h69wCg) for what Clang in C++ mode makes of the construct. – Daniel H Oct 22 '17 at 03:18
  • @DanielH Ah yes a _compound literal_ and not a _string literal_. – chux - Reinstate Monica Oct 22 '17 at 03:59
  • 1
    @Zebrafish in C there is no cast; this is the syntax for a compound literal, which has superficial similarities to a cast expression. But it is not a cast expression because something enclosed by `{ }` does not make an expression. The code is invalid in C++ for the same reason – M.M Oct 22 '17 at 04:15