csguy
csguy

Reputation: 1474

Array decay and modification

After reading a few answers as to why arrays in C/C++ are non-modifiable lvalues, I'm a bit confused still.

According to the accepted answer (summarized below): Why array type object is not modifiable?

C is written in such a way that the address of the first element would be computed when the array expression is evaluated.

This is why you can't do something like

int a[N], b[N];
a = b;

because both a and b evaluate to pointer values in that context; it's equivalent to writing 3 = 4. There's nothing in memory that actually stores the address of the first element in the array; the compiler simply computes it during the translation phase1.

This is when I got confused.

When we have a = b, it made sense to me that b should decay to a pointer value, pointing to the first element of b.

I think a decays to a pointer value as well, but I'm unsure.

My question is:

How would a = b be equivalent to something like 3 = 4?

Wouldn't it be more similar to address of a = address of b?

Upvotes: 1

Views: 140

Answers (1)

David C. Rankin
David C. Rankin

Reputation: 84551

Since to a measuable degree over your last couple of questions, it appears part of your confusion about the array/pointer conversion was due to difficulty understanding pointers themselves. Let's start with a quick summary.

Pointer Basics

A pointer is simply a normal variable that holds the address of something else as its value. In other words, a pointer points to the memory address where something else can be found. Where you normally think of a variable holding an immediate values, such as int a = 5;, a pointer would simply hold the address where 5 is stored in memory. To declare a pointer itself and assign an address as its value, you can use the unary '&' operator to obtain the address for an existing object of that type. For example int *b = &a; takes the address of a (where 5 is stored in memory) and assigns that address as the value of pointer b. (b now points to a)

To reference the value at the address held by a pointer you dereference the pointer by using the unary '*' character before the pointer name. E.g., b holds the address of a (e.g. b point to a), so to get the value at the address held by b, you simply dereference b, e.g. *b.

Pointer arithmetic works the same way regardless of the type of object because the type of the pointer controls the pointer arithmetic, e.g. with a char * pointer, pointer+1 points to the next byte (next char), for an int * pointer (normal 4-byte integer), pointer+1 will point to the next int at an offset 4-bytes after pointer. (so a pointer, is just a pointer.... where arithmetic is automatically handled by the type)

An Array is Converter to a Pointer Its First Element on Access

Moving on to the array-pointer conversion, Both C and C++ standards define how an array type is converted to a pointer on access subject to 4 exceptions. Note here the C++ standard will rely on the C standard for "Fundamental Types" and will add definitions to the C++ standard to extend the C standard where necessary. The basic behavior of the array/pointer conversion is provided by C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)

§ 6.3.2 Other operands
    6.3.2.1  Lvalues, arrays, and function designators
    Array pointer conversion
    (p3) Except when it is the operand of the sizeof operator, the _Alignof 
    operator, or the unary '&' operator, or is a string literal used to 
    initialize an array, an expression that has type "array of type" is 
    converted to an expression with type "pointer to type" that points to 
    the initial element of the array object and is not an lvalue.

The standard specifically defines that the array on access conversion to a pointer to the first element results in a pointer that is not an lvalue.. So if you have your array a and array b, it is the standard itself that prohibits your ability to change the address held by that pointer with:

a = b;

And, as explained in the examples provided in the comments, would result in the loss of your ability to access the elements of a if that were allowed.

The C++ standard incorporates the behavior defined in the C standard, but then extends the definitions surrouding the array/pointer conversion to account for the use of types and objects not present in C, but does not alter the behavior of 6.3.2.1(p3).

C++ Standard 7.3.2 Array-to-pointer conversion provides:

§ 7.3.2 Array-to-pointer conversion
  1  An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” 
     can be converted to a prvalue of type “pointer to T”. The temporary 
     materialization conversion ([conv.rval]) is applied. The result is a 
     pointer to the first element of the array.

The extension being the C++ standard specifically defining the resulting pointer as a prvalue - pure rvalue instead of the C definition of "not an lvalue", and adds the “array of unknown bound of T” language.

So in both C and C++, on access an array is converted to a pointer to its first element (subject to the 4 exceptions enumerated in §6.3.2.1(p3)) and the resulting pointer cannot be modified or assigned another address. The limitation on modification doesn't have anything to do with the way ordinary pointers behave, but instead is solely due to the way the C/C++ standards define the conversion and that the resulting pointer-to-type can't be modified. (so the short answer is because the standards say you can't)

Look things over and make sure it makes sense to you and has sunk-in. If you are still fuzzy, drop a comment. The important part is understanding pointers themselves. The limitation on what you can do with those pointers that result from the array/pointer conversion is dictated by the standards themselves.

Upvotes: 1

Related Questions