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 0x400c801.  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%pspecifier drops leading zeros.
- Same deal - technically, the values are 0x000000007fffcb4d4518and0x000000007fffcb4d4510.  Note that the specific address values will change from run to run.
- *stris 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.