To copy the address of strA [0] into our variable pA via the assignment declaration, shouldn't we write pA = & strA?
Arrays are weird and don't behave like other types.
The expression strA
"decays" from type "N-element array of char
" to "pointer to char
", and the value of the expression is the address of the first element. This "decay" doesn't happen when the array expression is the operand of the sizeof
or unary &
operators, so the expression &strA
has type "pointer to N-element array of char
", or char (*)[N]
, which is not the same as char *
. The address value is the same (the address of an array is the same as the address of its first element), but the types are different.
Assuming the declarations
char str[N]; // for some size N
char *p;
when you write
p = str; // char * = char *
the expression str
has type "N-element array of char
"; however, since str
is not the operand of the sizeof
or unary &
operators, it "decays" to type char *
and evaluates to the address of the first element. It’s equivalent to writing
p = &str[0]; // char * = char *
There is a reason for this - C was derived from an earlier language named B, and in B array types had a dedicated pointer to the first element - when you declared an array in B like
auto a[N];
what you got in memory was
+---+
a: | | -------+
+---+ |
... |
+---+ |
| | a[0] <-+
+---+
| | a[1]
+---+
...
and the array subscript operation a[i]
was defined as *(a + i)
- given the starting address a
, offset i
elements from that address and dereference the result.
When he was designing C, Ritchie wanted to keep B's array behavior, but he didn't want to keep the explicit pointer that behavior required. When you declare an array in C like
int a[N];
what you get in memory is
+---+
a: | | a[0]
+---+
| | a[1]
+---+
...
Instead of setting aside space for an explicit pointer to the first element, the compiler replaces any occurrences of the expression a
with a pointer to a[0]
. So a[i]
is still defined as *(a + i)
. Unfortunately, this means that arrays lose their "array-ness" under most circumstances and most of the time what you're dealing with is a pointer.
Question 2: the expression c = * ++ p; increases the value of p or the address of p?
This gets a little complicated. To answer as asked, it increases the value of p
- it sets p
to point to the next object in a sequence. This is probably better explained with a concrete example.
Assume the following declarations:
char str[] = "foo";
char *p = str;
then the following are true:
+---+
str: |'f'| str[0] <--- p
+---+
|'o'| str[1] <--- p + 1
+---+
|'o'| str[2] <--- p + 2
+---+
| 0 | str[3] <--- p + 3
+---+
p == &str[0] // char * == char *
*p == str[0] == 'f' // char == char == char
p + 1 == &str[1]
*(p + 1) == str[1] == 'o'
p + 2 == &str[2]
*(p + 2) == str[2] == 'o'
The result of the expression p + 1
is a pointer to str[1]
, the result of the expression p + 2
is a pointer to str[2]
, etc.
c = *++p;
is roughly equivalent to writing
tmp = p + 1;
c = *tmp;
p = p + 1;
with the caveat that the updates to c
and p
can happen in any order - they may even be evaluated simultaneously (either interleaved or in parallel).
In this case, it means we assign the value of *(p + 1)
('o'
) to c
, and update p
to point to str[1]
, leaving us with this:
+---+
str: |'f'| str[0]
+---+
|'o'| str[1] <--- p
+---+
|'o'| str[2] <--- p + 1
+---+
| 0 | str[3] <--- p + 2
+---+
p == &str[1]
*p == str[1] == 'o'
p + 1 == &str[2]
*(p + 1) == str[2] == 'o'
p + 2 == &str[3]
*(p + 2) == str[3] == 0