artaxerxe
artaxerxe

Reputation: 6421

Cannot understand why that behavior

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

Answers (4)

Steve Jessop
Steve Jessop

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

Kerrek SB
Kerrek SB

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 foo returns a primitive type by value)[whoops, that was a concession meant for C++].

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

md5
md5

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

Daniel S
Daniel S

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

Related Questions