ptr = "apple"; // shouldn't it be *ptr = "apple"
Starting from the beginning...
The string literal "apple"
is stored in a 6-element array of char
, like so:
+---+---+---+---+---+---+
|'a'|'p'|'p'|'l'|'e'| 0 |
+---+---+---+---+---+---+
The trailing 0
marks the end of the string (it's called the string terminator).
When an expression of type "N-element array of T
" appears in an expression, it will be converted ("decay") to an expression of type "pointer to T
" and the value of the expression will be the address of the first element of the array, unless the array expression is the operand of the sizeof
or unary &
operators, or is used to initialize a character array in a declaration.
Thus, in the statement
ptr = "apple";
the expression "apple"
is converted ("decays") from an expression of type "6-element array of char
" to "pointer to char
". The type of the expression ptr
is char *
, or "pointer to char
"; thus, in the assignment above, ptr
will receive the address of the first element of "apple"
.
It should not be written as
*ptr = "apple";
since the expression *ptr
evaluates to the value of the thing ptr
points to, which at this point is a) indeterminate, and b) the wrong type for the assignment. The type of the expression *ptr
is char
, which is not compatible with char *
.
I've written a utility that prints a map of items in memory; given the code
char *ptr = "apple";
char arr[] = "apple";
the map looks something like this:
Item Address 00 01 02 03
---- ------- -- -- -- --
apple 0x400c80 61 70 70 6c appl
0x400c84 65 00 70 74 e.pt
ptr 0x7fffcb4d4518 80 0c 40 00 ..@.
0x7fffcb4d451c 00 00 00 00 ....
arr 0x7fffcb4d4510 61 70 70 6c appl
0x7fffcb4d4514 65 00 00 00 e...
The string literal "apple"
lives at address 0x400c80
1. The variables ptr
and arr
live at addresses 0x7fffcb4d4518
and 0x7fffcb4d4510
, respectively2.
The variable ptr
contains the value 0x400c80
, which is the address of the first element of the "apple"
string literal (x86 stores multi-byte values in "little-endian" order, so the least-significant byte comes first, meaning you have to read right-to-left).
Remember the "except" clause above? In the second declaration, the string literal "apple"
is being used to initialize an array of char
in a declaration; instead of being converted to a pointer value, the contents of the string literal are copied to the array, which you can see in the memory dump.
printf("%s", ptr) // Why should I send the address instead of the value
Because that's what the %s
conversion specifier expects - it takes a pointer to the first character of a 0-terminated string, and will print out the sequence of characters starting at that location until it sees the terminator.
3 ... I can't understand what is supposed to imply
You cannot change the value of an array object. Let's look at what str
would look like in memory:
+---+
str: |'Q'| str[0]
+---+
|'u'| str[1]
+---+
|'e'| str[2]
+---+
|'s'| str[3]
+---+
|'t'| str[4]
+---+
| 0 | str[5]
+---+
You can write to each str[i]
3 (changing its value), but you cannot write to str
because there's nothing to write to. There's no str
object separate from the array elements. Even though the expression str
will "decay" to a pointer value, no storage is set aside anywhere for that pointer - the conversion is done at compile time.
Similarly, attempting to modify the contents of a string literal invokes undefined behavior4; you may get a segfault, or your code may work as expected, or you may wind up launching nukes at Liechtenstein. So you can't write to *p
or p[i]
; however, you can write a new value to p
, pointing it to a different location.
- Techically, it's
0x0000000000400c80
; the %p
specifier drops leading zeros.
- Same deal - technically, the values are
0x000000007fffcb4d4518
and 0x000000007fffcb4d4510
. Note that the specific address values will change from run to run.
*str
is equivalent to str[0]
- The C language definition identifies certain operations which are erroneous, but doesn't place any requirements on the compiler to handle that code in any particular way. Different platforms store string literals in different ways; some put them in read-only memory, so attempting to modify them results in a segfault, while other platforms store them in a writable segment so that the operation succeeds. Some may store them in such a way that you don't get a segfault, but the string isn't changed.
*ptr
is of type char. You cannot assign a string literal to a character. – Rumba