Espresso
Espresso

Reputation: 4752

How is it possible for the same pointer to have different types?

You can see what I mean when you take "str" or &"str"[0] and &"str" and get identical pointers but different types.

Note: I'm on a system where string literals are not distinct.

printf("%p\n", "str");
printf("%p\n", &"str"[0]);
printf("%p\n", &"str");

// hypothetical output:
// 0xADD12E550  (ptr to char)
// 0xADD12E550  (ptr to char)
// 0xADD12E550  (ptr to arr of char)

So in theory, when I dereference &"str", I should be able to get the first character of the string since I am dereferencing the same pointer. Peculiarly, that's not the case, I have to dereference twice. Once to get the array, then again to get the first character constant:

// &"str" = 0xADD12E550
// *(0xADD12E550) = 0xADD12E550
// *(0xADD12E550) = 's'

// **(&"str") == 's'

Maybe then this question can also answer how this is possible:

#include <stdio.h>

void dafunk() {
  printf("dadftendirekt\n");
}

main() {
  void (*dafunkptr)() = dafunk;
  (***************dafunkptr)();

  return 0;
}

Upvotes: 1

Views: 346

Answers (2)

Keith Thompson
Keith Thompson

Reputation: 263217

The expression "str" is of type char[4]. (In C++, it would be const char[4].)

Any expression of array type is, in most contexts, implicitly converted to a pointer to the first element of the array object. This conversion is commonly referred to as "decaying". The exceptions to this are:

  • When it's the operand of the unary sizeof operator (sizeof "str" yields the size of the array, not the size of a pointer).
  • When it's the operand of the unary & operator (&"str" yields a result of type char(*)[4], not char*).
  • When it's a string literal in an initializer used to initialize an array object (not applicable here).

In these three cases, an array expression keeps its array type.

A string literal refers to an implicitly created array object with static storage duration, big enough to hold the characters of the literal plus the terminating '\0' null character. In this case, "str" refers to an anonymous static object of type char[4].

So:

printf("%p\n", "str");

"str" is implicitly converted to a char* value, pointing to the 's' of "str".

printf("%p\n", &"str"[0]);

The "str" in "str"[0] decays to char*, as above. "str"[0" yields a char* value, pointing to the 's'. So "str" and "str"[0] are of the same type and value.

printf("%p\n", &"str");

Here, since "str" is the operand of &, the decay doesn't occur, so &"str" yields the address of the anonymous char[4] object, not the address of its first character. This expression is of type char(*)[4], or "pointer to array of 4 char".

The expressions &"str"[0] and &"str" both yield pointer values, both of which point to the same location in memory, but they're of different types.

In all three cases, the result of evaluating the expression is passed as an argument to printf. printf with a "%p" format requires an argument of type void*. In the first two cases, you're passing a char*, and the language's requirements for char* and void* imply that it will work as expected. In the third case, there are no such rules for char* vs. char(*)[4], so the behavior of

printf("%p\n", &"str");

is undefined.

As it happens, in most implementations all pointer types have the same size and representation, so you can get away with passing a pointer of any arbitrary type to printf with "%p".

In all three cases, you could (and probably should) explicitly cast the expression to void*, avoiding the undefined behavior:

printf("%p\n", (void*)"str");
printf("%p\n", (void*)&"str"[0]);
printf("%p\n", (void*)&"str");

The second part of your question deals with a distinct issue; it's about pointers to functions. The rules for expressions of pointer type are similar to those of array type: an expression of function type (such as a function name) is implicitly converted to a pointer to the function, except when it's the operand of sizeof (which is illegal) or & (which yields the address of the function). That's why applying * or & to a function acts like a no-op. In *func, func first decays to a pointer to the function, * dereferences the pointer, and the result again decays to a pointer to the function. In &func, the & inhibits the decay, but it yields the same pointer to the function that func by itself would yield.

Upvotes: 1

ouah
ouah

Reputation: 145829

The object "str" is of type array 4 of char.

The value of "str" is of type pointer to char.

The value of &"str"[0] is of type pointer to char.

The value of &"str" is of type pointer to an array 4 of char.

EDIT:

So now to access for example the first character of the string, from "str" you have to dereference the expression once:

char s = *"str";  // or s = "str"[0] 

or from the expression &"str" you have to dereference the expression twice as its type is a pointer to an array:

char s = **&"str";   // or s = *(&"str")[0]

Upvotes: 3

Related Questions