Which Pointer Expressions in C Programming are referred to as L-Values?

Question: Which Pointer Expressions in C Programming are called as L-Values?

Answer: We already know that lvalue refers to some location or storage used by some data object, for example: variables, arrays, structures, pointers etc. Further, ‘l’ in lvalue stands for left side of assignment “=” operator meaning that left side of assignment must be an expression which results in some location in memory. Let’s take a few examples:

    int index = 0;        /* index, an integer, is initialized with 0 */
    char address[100];    /* address, an array of 100 characters *
    float avg_rain;       /* avg_rain has some garbage */
    float temperature;
    float *fp = &avg_rain;
    /* 
     * *fp, a pointer-to-float, is initialized with address of float 
     * 'avg_rain'
     */

All the above data objects are stored in memory at specified locations. Their addresses can be obtained by using unary operator called addressof operator ‘&’. Addressof operator returns address of first byte of storage allocated to the data object.

Now, see, if constants are allocated to specified locations, can we determine their addresses using addressof operator? We take some examples:

    500; 
    45.0;     /* all these are constants *
    'A';

All the above constants are stored in memory but where? We can’t know this? Actually, constants don’t have lvalues. And that’s why we can’t assign them values! Let’s try to find their addresses:

advertisement
advertisement
/*
 * determine_add_const.c -- Program shows if we can determine address of
 * constants
 */
#include <stdio.h>
 
int main(void)
{
    500;        /* integer constant */
    45.06;      /* float constant */
    'A';        /* character constant */
 
    printf("Address of integer '500' is %p\n", &(500));
    printf("Address of float '45.06' is %p\n", &(45.06));
    printf("Address of character \'A\' is %p\n", &('A'));
 
    return 0;
}

Output as below:

[root@localhost ch06_Pointers]# gcc determine_add_const.c
determine_add_const.c: In function ‘main’:
determine_add_const.c:9:43: error: lvalue required as unary ‘&’ operand
determine_add_const.c:10:43: error: lvalue required as unary ‘&’ operand
determine_add_const.c:11:45: error: lvalue required as unary ‘&’ operand

Note that addressof operator operates on operands having lvalues. Constants don’t have!

Sanfoundry Certification Contest of the Month is Live. 100+ Subjects. Participate Now!

O key! Now, we try to modify the above mentioned data objects, below:

    int index = 100;	    /* now index is assigned 100 */
    char address[100];	    /* address is an array of 100 characters */
    float avg_rain = 3.203; /* avg_rain is assigned 3.203 */
    *fp = &temperature;   /* *fp is assigned address of float temperature */

Because the left side of the assignment can be modified, lvalue is properly referred to as modifiable lvalue.

Until now, we are cleared with what an lvalue is! Let’s further talk about it with reference to pointer expressions. Addressof operator ‘&’ and indirection / dereferencing operator ‘*’ are frequently used with pointers. Let’s unravel some pointer expressions to understand how these two operators work, for example:

    int men = 100, women = 150;
    int *ip = &men;
    /* *ip = &men, assigns 'ip' address of location where men is stored */

In exp. “*ip = &men”, pointer to integer ‘ip’ points to location of men in the memory. To access the value of men using ‘ip’, we need to perform indirection on ‘ip’. Let’s see how,

advertisement
    printf("There are %d men working in a factory.\n", *ip);

Let’s interpret a simple exp. *ip. ‘ip’ holds the address of location of men. When we performed indirection on ‘ip’, it caused to go to location pointed to by ‘ip’, that is location of men, and access the value there, whether to read value from or to write value there. That’s why ‘*ip’ represents both as value and location and hence is an lvalue.

Take some little bit complex pointer exp., for example:

    int NH_India = 228;
    int *ip = &NH_India;
    int new;
 
    new = ++*ip;

In the exp. “new = ++*ip”, ++ and * are unary operators have associativity from right to left. So indirection is applied first and value at the location pointed to by ‘ip’ is taken, which is here 228. Then pre-increment operator to operate. But pre-increment operator increments its operand by 1, then makes a copy of incremented value and use that copy in the surrounding expression. See program below:

advertisement
/* ptrexp_as_lvalue1.c -- program shows pointer expressions */
#include <stdio.h>
 
int main(void)
{
    int NH_India = 228;
    int *ip = &NH_India;
    int new;
 
    printf("When \"new\" not initialized it is %d and NH_India is %d\n",
            new, NH_India);
 
    new = ++*ip;
 
    printf("After \"new = ++*ip\", new is %d and NH_India is %d\n",
           new, NH_India);
 
    return 0;
}

Output of the above program:

[root@localhost ch06_Pointers]# gcc ptrexp_as_lvalue1.c
[root@localhost ch06_Pointers]# ./a.out 
When "new" not initialized it is 32767 and NH_India is 228
After "new = ++*ip", new is 229 and NH_India is 229

Notice, integer ‘NH_India’ is incremented by 1. But if exp. ++*ip represents an lvalue? Can we write

    new = ++*ip;
    as
    ++*ip = new;	/* if allowed? */

Of course, not! Why? Because, we have already seen above that ‘++*ip’ gives an integer value and not a location.

/* ptrexp_as_lvalue2.c -- program shows pointer expressions as lvalues */
#include <stdio.h>
 
int main(void)
{
    int NH_India = 228;
    int *ip = &NH_India;
    int new = 100;
 
    printf("When \"new\" initialized it is %d and NH_India is %d\n",
           new, NH_India);
 
    ++*ip = new;    /* if allowed? */
 
    printf("After \"++*ip = new\", ++*ip is %d and NH_India is %d\n",
            new, NH_India);
    return 0;
}

Output is below:

[root@localhost ch06_Pointers]# gcc ptrexp_as_lvalue2.c
ptrexp_as_lvalue2.c: In function ‘main’:
ptrexp_as_lvalue2.c:11:15: error: lvalue required as left operand of 
assignment

Note the error: lvalue required as left operand of assignment. What if we write the expression as:

    ++*ip;
    as
    *ip++;

Again, there are two operators * and ++. But this time ++ is used as post increment operator and post increment operator has higher precedence than ‘*’ operator. Let’s continue to emphasize on their working. Remember that pre and post increment operators create copies of values of their operands and that copy is used in the surrounding exp.. Also remember that in case of pre-increment, copy is created after operand is modified while in post-increment copy is created before operand is incremented.

So, in exp. “*ip++”, ‘++’ operates first, copy of value of ‘ip’, which is address of integer NH_India, is created, then pointer ‘ip’ is incremented to point to next integer and then indirection is performed on copy and value at the location pointer ‘ip’ was pointing to before increment is assigned to new.

    new = *ip++;
/*
 * ptrexp_as_lvalue3.c -- program shows where pointer points to when 
 * incremented
 */
#include <stdio.h>
 
int main(void)
{
    int NH_India = 228;
    int *ip = &NH_India;
    int new = 100;
 
    printf("When \"new\" initialized it is %d and value of *ip is %p\n",
           new, ip);
 
    new = *ip++;
 
    printf("After \"new = *ip++\", new is %d and \"new value\" of *ip is "
           ."%p\n", new, ip);
 
    .return 0;
}

Look closely at the output below:

[root@localhost ch06_Pointers]# gcc ptrexp_as_lvalue3.c
[root@localhost ch06_Pointers]# ./a.out 
When "new" initialized it is 100 and value of *ip is 0x7fffe9828340
After "new = *ip++", new is 228 and "new value" of *ip is 0x7fffe9828344

Notice pointer *ip’s earlier and current values. Although, *ip is incremented by 1 it’s value changed from 0x7fffe9828340 to 0x7fffe9828344. Actually, when we say, a pointer is incremented by 1, we mean it’s moved forward by one storage unit of the type it’s pointing to.

Now, we see what happens when we set the exp. “*ip++” on the left side of assignment.

    *ip++ = new;

Again, ‘*ip++’ doesn’t represent a location but a value. And a value can’t be assigned any other value. Therefore, this statement results in lvalue error.

There are numerous pointer expressions which are lvalues and used frequently in programs. However, on the other side, not all pointer expressions are lvalues.

Examples of pointer expressions having lvalues:

    *ip;
    *++ip;
    *ip++;

Examples of pointer expressions with no valid lvalues:

    ++*++ip;
    ip++; and ip++;
    (*ip)++;
    ++*ip++;

Note, you can apply above given reasoning to understand pointer expressions, in examples above, which are lvalues and which aren’t.

Sanfoundry Global Education & Learning Series – 1000 C Tutorials.

If you wish to look at all C Tutorials, go to C Tutorials.

advertisement
advertisement
Subscribe to our Newsletters (Subject-wise). Participate in the Sanfoundry Certification contest to get free Certificate of Merit. Join our social networks below and stay updated with latest contests, videos, internships and jobs!

Youtube | Telegram | LinkedIn | Instagram | Facebook | Twitter | Pinterest
Manish Bhojasia - Founder & CTO at Sanfoundry
Manish Bhojasia, a technology veteran with 20+ years @ Cisco & Wipro, is Founder and CTO at Sanfoundry. He lives in Bangalore, and focuses on development of Linux Kernel, SAN Technologies, Advanced C, Data Structures & Alogrithms. Stay connected with him at LinkedIn.

Subscribe to his free Masterclasses at Youtube & discussions at Telegram SanfoundryClasses.