1

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;
}
Espresso
  • 4,722
  • 1
  • 24
  • 33
  • 4
    I honestly think the best answer to this question is: "If you do silly things, you get silly results. If you only do sensible things, you will only get sensible results." – David Schwartz Apr 08 '12 at 23:45
  • There are no pointers. Printf() only sees a "pointer expression" (which also has a type attached to it) – wildplasser Apr 08 '12 at 23:45
  • The second part of your question is a duplicate [of this question](http://stackoverflow.com/questions/2795575/how-does-dereferencing-of-a-function-pointer-happen). – Sergey Kalinichenko Apr 08 '12 at 23:46
  • @wildplasser: When you say "There are no pointer", do you mean that there are no pointer *objects*? The word "pointer" by itself is ambiguous; we have pointer types, pointer objects, pointer expressions, and pointer values. The C standard uses the unqualified word "pointer" to refer to pointer values; see, for example, the description of the `malloc` function, which returns "either a null pointer or a pointer to the allocated space". – Keith Thompson Apr 09 '12 at 01:11
  • Of course there are pointer thingies. I intended to say that most people are thinking in terms of objects, while function arguments should be viewed as expressions (which of course inherit all their properties from the objects and constants that are part of them). The main syntactic element of C is an expression. Even an lvalue is an expression (not a *thing*). (I know you know, but most people think in terms of variables and assignments, not in terms of expressions and terms, etc) – wildplasser Apr 09 '12 at 01:22
  • @wildplasser: Okay, so what do we gain by observing this nuance? – GManNickG Apr 09 '12 at 04:44
  • **All your `printf` calls are undefined behavior** because `%p` only accepts ptr-to-void. The casts to `(void *)` are required unless the argument is a ptr-to-void already. – Jens Dec 05 '13 at 11:14

2 Answers2

3

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]
ouah
  • 142,963
  • 15
  • 272
  • 331
  • @Espresso in that case I cannot clearly see what is your question – ouah Apr 09 '12 at 00:05
  • Please see my comment on Jonathan's answer. Sorry, I'm on a mobile device. – Espresso Apr 09 '12 at 00:09
  • You make me LULz. When I dereference say, `0x123456789` which is a pointer to some integer. Shouldn't I always get the value that's it's pointing to? But that's not the case in my example. How did the pointer change types under my feet? How was I able to dereference it twice and get different values? – Espresso Apr 09 '12 at 00:25
  • You seem to forget `0x123456789` is an integer and not a pointer. You can make it a pointer to `char` with a cast. But then C requires you to dereference one time to get the char value: `*(char *) 0x123456789`. You can also make it (i.e., tell C it is) a pointer to an array 100 of `char`. And then C will require you to dereference twice to access the first `char` value: `**(char (*)[100]) 0x123456789`. Please forget/de-learn assembly language, this is C and you are following C rules here. – ouah Apr 09 '12 at 00:47
1

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.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • Memory addresses have types right? Depending on what the pointer (memory address) is pointing to it can be `int *`, `char *`, … Is that right? – Espresso Apr 09 '12 at 01:42
  • @Espresso: Yes, any C pointer (object|expression|value) has some particular type. Two pointers of different type can nevertheless point to the same memory location. You can detect that by converting them to a common type like `void*` and comparing them with `==`. – Keith Thompson Apr 09 '12 at 02:03
  • 1
    @Espresso: There's no implicit cast involved. A string literal is of type `array of char`, in this case, `char[4]`. Applying the address operator simply yields the address of that array. What seems odd about it is that the array expression *isn't* implicitly converted to a pointer, because it's the operand of the `&`. – Keith Thompson Apr 09 '12 at 04:16
  • OK. Your answer didn't directly answer my question. But I found out. For some reason, when I use the address operator on a string literal, the C compiler implicitly casted the pointer that the string literal decayed into to a pointer to an array of characters. That's my evaluation, correct me if I'm wrong. I really don't know what's going on under the hood. I don't know assembly like the other guy implied. If my evaluation turns out to be true, why C would cast instead of giving me a different pointer is another question. – Espresso Apr 09 '12 at 04:18
  • @Espresso: You understand that arrays and pointers are two different things, yes? – GManNickG Apr 09 '12 at 04:22
  • @Espresso: A C pointer value has a *type* and a *value*. The value is the memory address to which it points. You have two different C pointer values with different types that point to the same memory location. Suggested reading: sections 4 and 6 of the [comp.lang.c FAQ](http://c-faq.com). – Keith Thompson Apr 09 '12 at 04:45
  • If it wasn't being casted, how did the same memory address change into different types? If you had `&"str"`, you get the same memory address, `0xADD12E550`, but now it can only be applied on a variable that is pointer to an array of `char[4]` type. WRONG: `char *charptr = &"str"`. RIGHT: `char *charptr = (char *)(&"str");`. In the latter example I had to undo the implicit casting by explicitly casting it back to a pointer to `char`. Do you see my logic, where my evaluation is coming from? – Espresso Apr 09 '12 at 04:55
  • `&"str"` yields a pointer right? And `"str"` decays into a pointer, a different one. Fair enough, but how do you prove that they're two different pointers? Obviously you can't do `&(&"str")` or `&(&"str"[0])`. – Espresso Apr 09 '12 at 05:24
  • I can't do `char *ptr = "str"`, since the address operator would yield yet another different address when I apply it on `ptr` since its a different variable. – Espresso Apr 09 '12 at 05:30
  • Oh, I just had an epiphany. That's why string literals don't decay into pointers either when you use the address operators on them. Since `&(&"str"[0])` are illegal. – Espresso Apr 09 '12 at 05:38
  • @Espresso: Why do you have the idea that a memory address can be associated with only a single type? The first element of an array will have the a same address as the array itself, but they have different types. the first member of a struct will have the same address as the struct object, but again they are different types. An address has no type. An object has type and an address. An object that contains 'sub-objects' (array, struct, and union types) may share an address with one or more of those sub-objects. – Michael Burr Apr 09 '12 at 06:07
  • You're right. They are two different pointers. `"str"` and `&"str"` are the pointers themselves. (I was confused by memory addresses and pointers, as you've said the term pointer is ambiguous.) But each have different types but the same values, `0xADD12E550`. `"str"` decayed into a pointer while C had to cope with the illegality above by giving a different pointer. A pointer that points to an array, but with a value that remains unchanged. – Espresso Apr 09 '12 at 06:31
  • @Espresso: *"... C had to cope with the illegality above ..."* -- I don't know what that means. – Keith Thompson Apr 09 '12 at 07:02
  • Oh, what I meant was since `&(&"str"[0])` is invalid, C had to give me another pointer. – Espresso Apr 09 '12 at 22:46
  • @Espresso: That doesn't make sense. Since it's invalid, the compiler would have rejected it if you'd written it. You didn't. C doesn't "give you another pointer" because something is invalid. – Keith Thompson Apr 09 '12 at 23:09
  • When you do `&"str"`, `"str"` doesn't decay into a pointer because that would be like writing `&(&"str"[0])`. Hence, `&"str"[0]` and `&"str"` are two different pointers. – Espresso Apr 09 '12 at 23:42
  • @Espresso: Ok, I see what you mean. You're talking about the (presumed) rationale for `&` being one of the three exceptions to the rule. Personally, I find it easier just to remember what the rule says. Both approaches are valid as long as they lead to the same conclusion. (But assuming that C defines things in certain ways *because they make sense* can easily lead you astray.) – Keith Thompson Apr 10 '12 at 01:01