Is the array to pointer decay changed to a pointer object?

"But a itself is not pointing to another region of memory, it IS the region of memory itself.

"So when the compiler converts it to a pointer, does it save it (like p) somewhere in memory or it's an implicit conversion?"

It is an implicit conversion. The compiler does not implement the creation of a separate pointer object in memory (which you can f.e. assign in any manner with a different memory address) to hold the address of the first element.

The standard states (emphasize mine):

"Except when it is the operand of the sizeof 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. If the array object has register storage class, the behavior is undefined."

Source: ISO/IEC 9899:2018 (C18), 6.3.2.1/4

The array is converted to an expression of pointer type, it is not an lvalue.

The compiler just evaluates a to &a[0] (pointer to a[0]).


"I understand that array names are converted to pointers."

An array does not always convert to a pointer to its first element. Look at the first part of the quote above. F.e. when used as &a, a does not decay to a pointer to its first element. Rather it gains a pointer to the whole array int (*)[3].


C has objects and values.

A value is an abstract concept—it is some meaning, often mathematical. Numbers have values like 4, 19.5, or −3. Addresses have values that are locations in memory. Structures have values that are the values of their members considered as an aggregate.

Values can be used in expressions, such as 3 + 4*5. When values are used in expressions, they do not have any memory locations in the computing model that C uses. This includes values that are addresses, such as &x in &x + 3.

Objects are regions of memory whose contents can represent values. The declaration int *p = &x defines p to be an object. Memory is reserved for it, and it is assigned the value &x.

For an array declared with int a[10], a is an object; it is all the memory reserved for 10 int elements.

When a is used in an expression, other than as the operand of sizeof or unary &, the a used in the expression is automatically converted to the address of its first element, &a[0]. This is a value. No memory is reserved for it; it is not an object. It may be used in expressions as a value without any memory ever being reserved for it. Note that the actual a is not converted in any way; when we say a is converted to a pointer, we mean only that an address is produced for use in the expression.

All of the above describes semantics in the computing model C uses, which is that of some abstract computer. In practice, when a compiler works with expressions, it often uses processor registers to manipulate the values in those expressions. Processor registers are a form of memory (they are things in a device that retain values), but they are not the “main memory” we often mean when we speak of “memory” without qualification. However, a compiler may also not have the values in any memory at all because it calculates the expression in part or in full during compilation, so the expression that is actually computed when the program is executing might not include all the values that are nominally in the expression as it is written in C. And a compiler might also have the values in main memory because computing a complicated expression might overflow what is feasible in the processor registers, so that parts of the expression have to be temporarily stored in main memory (often on a hardware stack).