Lvalue in C

cd .. || cd

December 23, 2025 · 6 mins · Robertus Chris

Table of Contents

A Brief Into

I am always confused when trying to understand what is considered an lvalue in C programming language, so let me try to explain that to make me have a better understanding of lvalue in C.

What is Lvalue?

From C committee draft definition :

An lvalue is an expression (with an object type other than void) that potentially designates an object; if an lvalue does not designate an object when it is evaluated, the behavior is undefined.

And from C committee draft the origin of lvalue term :

The name “lvalue” comes originally from the assignment expression E1 = E2, in which the left operand E1 is required to be a (modifiable) lvalue. It is perhaps better considered as representing an object “locator value”.

Here’s another explanation about lvalue from GNU C intro:

An expression that identifies a memory space that holds a value is called an lvalue, because it is a location that can hold a value.

With that in mind, lvalue is an expression that refers to the region of storage that can hold a value. Those storage can be memory or something else.

The region of storage that can hold a value is called object.

I think calling lvalue as locator value make it easier to understand than the usual terminology left value, because lvalue might not be on the left side of the statement.

Pointer Dereference is Lvalue

Here’s an example of lvalue can be on the left side or right side:

int num = 68;
int *ptr = #

/*
 * *ptr is an lvalue that's on the left and right of statement.
 * The *ptr on the right undergo conversion from _lvalue to rvalue_.
 */
*ptr = *ptr + 1;

The expression *ptr from example above is an lvalue because it refers to the memory space that can hold a value. Keep in mind that ptr is a variable that stored memory address as its value, that means ptr stored the address of another region of storage. And dereferencing ptr or *ptr means that we are using those region of storage stored in ptr.

Let’s say we have memory space in address 001 and 002, the num variable in previous example using the memory space at address 001 and the ptr variable using the memory space at address 002 which stored 001 inside it. Here’s a simple representation:

Variable is Lvalue

When we define a variable like this:

int num = 69;

num is an lvalue because num refers to the memory space that hold value 69.

Non-modifiable Lvalue

Apparently lvalue is not always modifiable, there’s some region of storage we can not modify.

From C committee draft about lvalue :

A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const-qualified type.

Basically we can not modify lvalue if it has:

Other than that, i assume we can modify the lvalue (?). I’m still not sure yet, at least by the time of writing this post. But the point is that, we can change the value in lvalue depending on lvalue modifiable or not. If the lvalue modifiable, we can change the value, if not, then we can’t change the value.

Anyway, let’s check the constraint one by one.

Array Type

When we declare array like this:

int array[2];

From what i understand, array is an lvalue because it refers to the memory space that can hold a value but, by the C standard, we can’t modify those lvalue with array type like this:

int array[2];
array = 69; // This is invalid!

I still have no idea why that’s the case, but if you are curious, you can read the development of the C language .

Incomplete Type

Incomplete type in here means that the type lacks size information, for example:

struct anu;

anu is incomplete type because there is not enough information for the compiler to determine how much storage to set aside for struct anu type.

Here’s another example of incomplete type:

int itu[];
union ini;

We cannot use the incomplete type like this:

struct anu nganu;

unless we complete the type.

But, we can create a pointer to incomplete type like this:

struct anu *nganu;

without completing the definition of struct anu. That is because a pointer just storing the memory address and we only need to know the pointer size, we don’t need to know the type size.

Constant Type

Well, as far as i understand, constant in C is read-only, which means that we can’t really change that.

Constant Type in Struct or Union

I’m still not sure about this one, but i kind of found an example. Here’s an example that will throw an error because of constant type in struct (using gcc):

struct something {
    const int size;
};

int main(void)
{
    struct something very_big;
    very_big.size = 69;

    return 0;
}

For some reason, the example above, which separate the declaration and assignment, trigger an error assignment of read-only member 'size', but if we use the initialization like this:

struct something {
    const int size;
};

int main(void)
{
    struct something very_big = {.size = 69};

    return 0;
}

The example above didn’t give any error. So maybe the non-modifiable is during the assignment (?). Well, i’m still not sure.

Bonus: brief intro to rvalue

This post is mainly exploring about lvalue because lvalue make me confused the most. Especially the dereference pointer statement, when i see some reference write about “*ptr is lvalue”, i’m like “but why can i put it on the right side like *ptr = *ptr + 1;?”. That is the primary reason i’m creating this post.

But other people usually also talking about rvalue. Here’s a footnotes from C committee draft :

What is sometimes called “rvalue” is in this International Standard described as the “value of an expression”

And that’s what i think about rvalue at the time of writing this post, a value of an expression. At the moment, i don’t really want to dive deep into what is considered an rvalue. One step at a time.

That’s it from me, see you next time!

References