Reputation: 6421
I have this code:
char *name[] = { "a1", "b2", "c3", "d4" };
printf("%s\n", *name); //the critical line
Related to critical line
:
In this form, the output is simple: a1
.
If I replace the critical line
with:
printf("%s\n", ++*name);
then the output is 1
. I think until now everything is good.
Taking in account that name
is a pointer to the first string of characters, respectively "a1"
, I replace the critical line
with:
printf("%s\n", ++name);
in the hope that I'll get "b2"
result as output. But I get this error:
../src/test.c:32: error: lvalue required as increment operand
.
Question: I can't understand why ++*name
is legal - name
is a pointer to first string of characters - and ++name
isn't. In my opinion, the ++name
should move the name
to the next string of characters. Can anybody explain me where is the lack in my understing?
Upvotes: 2
Views: 124
Reputation: 279455
When you write ++name
, the array name
is converted to a pointer to the first element of the array. The result of this conversion is not an lvalue, and it can't be modified with ++
or otherwise. You could instead write name+1
, which would print the right thing. When name
is an array, there is no way to modify it to refer to anything other than that array[*].
Consider also:
char **p = name; // OK, `name' converts to pointer
++p; // OK, `p' is an lvalue
++(p+1); // not OK, `p+1' is not an lvalue
++((char**)p); // not OK, `(char**)p' is not an lvalue
++*name; // OK, `*name' is an lvalue
Roughly speaking, an "lvalue" is an expression that refers to an object, whereas a "not an lvalue" is an expression that has a value. The difference between an object and a value, is that an object is a place for storing values (well, one value at a time). Values can never be modified, objects sometimes can.
Whenever you have a subexpression which is an lvalue but whose current value is needed, the object is read/loaded/whatever you want to call it. In C++ this is called an "lvalue to rvalue conversion", I can't remember whether it's called anything in C other than "evaluating the subexpression".
[*] you can shadow it with another variable name
in an inner scope, that refers to something else. But that's still not modifying the outer name
, just temporarily hiding it.
Upvotes: 3
Reputation: 477700
First off, make sure you're totally happy and confident with the following fact: An arrays is not a pointer.
Second, what's in a name
? The array decays into a pointer to the first element. After decay, the expression name
has type char **
(pointer to first element of an array of char*
. However, the decayed expression is an rvalue. You cannot modify it! And naturally so, since it makes no sense to modify a pointer that's a pointer to a fixed array.
Therefore, you cannot directly increment the pointer which is the result of decaying name
, no more than you can say ++5
or ++foo()
(where [whoops, that was a concession meant for C++].foo
returns a primitive type by value)
What you can say is this:
char ** np = name;
++np;
printf("%s", *np);
This has the same effect as printing name[1]
, but now you also have a variable that holds a pointer to the second array element.
Upvotes: 1
Reputation: 23727
name
is an array, so, except when you use it as operand of the sizeof
or &
operators, it is evaluated as a pointer to the initial member of the array objet and is not an lvalue.
Accordingly, you can't modify name
directly, with an operator such as ++
(remember that the postfix increment operator need a modifiable lvalue as operand). Otherwise, you can use a temporary pointer (p
in the following example).
#include <stdio.h>
const char *name[] = { "a1", "b2", "c3", "d4" };
const char **p = name;
printf("%s\n", *p); /* Output: a1 */
*++p; /* or *p++ */
printf("%s\n", *p); /* Output: b2 */
Upvotes: 2
Reputation: 466
While name points to the address of the first element, name is not of type char *
, but char *[4]
. therefore, sizof(name) == sizeof(char *)*4
Incrementing a pointer always means to add the size of the data it points to. So, after incrementing, it points behind the whole array. Just like if you have
int i, a;
int *p = &i;
p++;
p will now point behind i. It will point to a, if the compiler decided to put a behind i.
Also note that your array only contains 4 pointers, not 4 strings. Like above, it is the compilers choice where those strings actually are. So, the end of the first string is not necessarily next to the beginning of the second string. Especially if you assign other values (string adresses) to name[1] later. Therefore, you may cast name to char **
, but should not cast to char *
. Incrementing the former will point to the second element (second char* pointer).
Upvotes: 1