Is Array Name a Constant Pointer in C++

Is an array name a pointer?

An array is an array and a pointer is a pointer, but in most cases array names are converted to pointers. A term often used is that they decay to pointers.

Here is an array:

int a[7];

a contains space for seven integers, and you can put a value in one of them with an assignment, like this:

a[3] = 9;

Here is a pointer:

int *p;

p doesn't contain any spaces for integers, but it can point to a space for an integer. We can, for example, set it to point to one of the places in the array a, such as the first one:

p = &a[0];

What can be confusing is that you can also write this:

p = a;

This does not copy the contents of the array a into the pointer p (whatever that would mean). Instead, the array name a is converted to a pointer to its first element. So that assignment does the same as the previous one.

Now you can use p in a similar way to an array:

p[3] = 17;

The reason that this works is that the array dereferencing operator in C, [ ], is defined in terms of pointers. x[y] means: start with the pointer x, step y elements forward after what the pointer points to, and then take whatever is there. Using pointer arithmetic syntax, x[y] can also be written as *(x+y).

For this to work with a normal array, such as our a, the name a in a[3] must first be converted to a pointer (to the first element in a). Then we step 3 elements forward, and take whatever is there. In other words: take the element at position 3 in the array. (Which is the fourth element in the array, since the first one is numbered 0.)

So, in summary, array names in a C program are (in most cases) converted to pointers. One exception is when we use the sizeof operator on an array. If a was converted to a pointer in this context, sizeof a would give the size of a pointer and not of the actual array, which would be rather useless, so in that case a means the array itself.

Is array name a constant pointer in C++?

The C++ standard defines what an array is and its behaviour. Take a look in the index. It's not a pointer, const or otherwise, and it's not anything else, it's an array.

To see a difference:

int a[10];
int *const b = a;

std::cout << sizeof(a); // prints "40" on my machine.
std::cout << sizeof(b); // prints "4" on my machine.

Clearly a and b are not the same type, since they have different sizes.

In most contexts, an array name "decays" to a pointer to its own first element. You can think of this as an automatic conversion. The result is an rvalue, meaning that it's "just" a pointer value, and can't be assigned to, similar to when a function name decays to a function pointer. Doesn't mean it's "const" as such, but it's not assignable.

So an array "is" a pointer much like a function "is" a function pointer, or a long "is" an int. That is to say, it isn't really, but you can use it as one in most contexts thanks to the conversion.

What is an array of constant pointers in C?

No, the const in char *const argv[] is not redundant.

First, const and "constant" are actually two different things in C, even though the const keyword is obviously derived from the word "constant". A constant expression is one that can be evaluated at compile time. const really means "read-only". For example:

const int r = rand();

is perfectly legal.

Yes, the address of an array -- like the address of any object -- is read-only. But that doesn't mean that the value of the array (which consists of the values of its elements) is read-only, any more than any other object is necessarily read-only.

Consider these three declarations:

char *arr1[10];
char *const arr2[10];
const char *arr3[10];

arr1 is a 10-element array of pointers to char. You can modify the char* elements and you can modify the objects that those elements point to.

arr2 is an array of const (read-only) pointers to char. That means that you can't modify the char* elements of the array (once they're initialized) -- but you can still modify the char objects or arrays that those elements point to.

And arr3 is an array of pointers to const char; you can modify the array elements, but you can't modify what they point to.

Now the fact that you used the name argv suggests that you're talking about the second parameter to main, which has some huge effects on this. The language specifies that main's second parameter is

char *argv[]

or, equivalently,

char **argv

There is no const. You can probably get away with adding one, but it's best to follow the form specified by the standard. (Update: I see from your comment that you're asking about the argv parameter of getopt(), which is defined as char * const argv[].)

And since it's a parameter defined as an array, another rule comes into play: a parameter defined as an array of some type is "adjusted" to a pointer to that type. (This rule applies only to parameters.) This isn't a run-time conversion. A function cannot have a parameter of array type.

The relationship between arrays and pointers in C can be confusing -- and there's a lot of misinformation out there. The most important thing to remember is that arrays are not pointers.

Section 6 of the comp.lang.c FAQ is an excellent explanation of the details.

Isn't Array name a constant pointer, which can't be used in pointer arithmetic, so why does this program work?

In the code

void f(char a[]){
a++;
printf("%c",*a);
}

a is not an array - it's a pointer. In the context of a function parameter declaration, T a[N] and T a[] are "adjusted" to T *a - all three declare a as a pointer to T, not an array of T.

When you called f with an array argument:

f(a);

the expression a "decayed" from type "10-element array of int" to "pointer to int", so what f actually receives is a pointer, not an array object. It's exactly equivalent to writing

f( &a[0] );

In the code

void check(){
char a[10];
a[0] = 'a';
a[1] = 'b';
a[2] = 'c';
// f(a);
a++;
printf("%c",*a);
}

a is an array expression, not a pointer, and an expression of array type cannot be the operand of ++.

C pointer to an array of string and array name disambiguation

No. Array is not a constant pointer in C. It is dead wrong.

Array decays into pointer to first element in most cases. That doesn't mean it is a pointer.

Constant thing is coming in your mind because array is non-modifiable lvalue. So you can't use it as one where it is being modified.

From §6.3.2.1p3

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. If the array object has register storage class, the behavior is undefined.

This explains what happened - balance in that statement is converted into pointer to the first element. The first element is double and a pointer to it is double* and that pointer contains address of balance[0].

This paragraph also points out why the &array is char* (*)[] because array used as operand to & address of operator is a case where array is not converted to pointer. That is why the first statement is legal. (Note that char *(*ptr)[4] is a pointer to an array of 4 char*-s. Here the address of array is assigned to ptr).

from §6.3.2.1p1

...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.

Whatever the source be, maybe it tried to show you this idea that arrays are not modifiable lvalues so statements like balance++ in C is illegal given that balance is an array name.

Without going awry or confusing the correct way to describe what array would be from standard §6.2.5p20

An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type. The element type shall be complete whenever the array type is specified. Array types are characterized by their element type and by the number of elements in the array. An array type is said to be derived from its element type, and if its element type is T , the array type is sometimes called ''array of T ''. The construction of an array type from an element type is called ''array type derivation''

Also note one thing, when you used char a[]={"aa","bb"}; - this is wrong in the sense that string literals are char arrays which decays into pointer - so it is an array of char*-s not array of char.

why is array name a pointer to the first element of the array?

An array name is not itself a pointer, but decays into a pointer to the first element of the array in most contexts. It's that way because the language defines it that way.

From C11 6.3.2.1 Lvalues, arrays, and function designators, paragraph 3:

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.

You can learn more about this topic (and lots about the subtle behaviour involved) from the Arrays and Pointers section of the comp.lang.c FAQ.

Editorial aside: The same kind of behaviour takes place in C++, though the language specifies it a bit differently. For reference, from a C++11 draft I have here, 4.2 Array-to-pointer conversion, paragraph 1:

An lvalue or rvalue of type "array of N T" or "array of unknown bound of T" can be converted to an rvalue of type "pointer to T". The result is a pointer to the first element of the array.

Array is treated as constant pointer,so we cannot change the pointer value but individually element of an array can be changed why?

Assume the following declarations:

char arr[6] = "hello";
char *ap = "hello";

Here's one possible memory map showing the outcome of those declarations (all addresses are made up out of thin air and don't represent any real-world architecture):

Item            Address        0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
"hello" 0x00008000 'h' 'e' 'l' 'l'
"hai" 0x00008004 'o' 0x00 'h' 'a'
0x00008008 'i' 0x00 0x?? 0x??
...
arr 0x82340000 'h' 'e' 'l' 'l'
0x82340004 'o' 0x00 0x?? 0x??
ap 0x82340008 0x00 0x00 0x80 0x00

The string literal "hello" is stored as a 6-element array of char with static storage duration at address 0x0008000. The string literal "hai" is stored as a 4-element array of char with static storage duration at address 0x00080006. The contents of a string literal may be stored in a read-only section of memory (depends on the platform). The behavior on attempting to modify the contents of a string literal is undefined; the code is considered erroneous, but the compiler isn't required to handle the situation in any particular way. You may get a segfault, or the change simply won't be applied, or the change will be applied as you expect. I'm assuming that multiple instances of a string literal in the source code will map to a single instance in memory (which is the usual case IME).

Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array. The resulting pointer is a non-modifiable lvalue.

The declaration

char arr[6] = "hello";

declares arr as a 6-element array of char with auto extent, and the contents of the string literal "hello" are copied into it. You can modify the values of arr[0] through arr[5] all you want, so you can write

a[0] = 'a';

but you cannot write something like

arr = "hai";

because while both of the expressions arr and "hai" are converted to pointer values as described above, arr isn't a pointer variable; there's no storage associated with arr apart from the array elements themselves, so there's no place to assign the result of "hai" (0x00008006).

The declaration

char *ap    = "hello";

declares ap as a pointer to char, and assigns the result of the expression "hello" to it. In this case, the string literal is not the operand of the sizeof or unary & operators, and it isn't being used to initialize the contents of an array, so it is converted to a pointer expression, and its value is the address of the first element of the array, or 0x0008000.

This is almost the mirror opposite of arr; you cannot update the contents of ap[0] through ap[5], but you can assign a new pointer value to ap, so

ap = "hai";

works. After doing so, your memory map would look like

Item            Address        0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
"hello" 0x00008000 'h' 'e' 'l' 'l'
"hai" 0x00008004 'o' 0x00 'h' 'a'
0x00008008 'i' 0x00 0x?? 0x??
...
arr 0x82340000 'a' 'e' 'l' 'l'
0x82340004 'o' 0x00 0x?? 0x??
ap 0x82340008 0x00 0x00 0x80 0x06

When are array names constants or pointers?

Arrays decay into pointers in functions. The array name is a non-modifable lvalue. What this means is that, you can do this:

int x=10,y=20;
int *p = &x; // <---- p Now points to x
p = &y; // <---- p Now points to y

But not this:

int arr[10], x=10;
arr = &x; // <----- Error - Array name is a non-modifiable lvalue.

Since arrays decay immediately into pointers, an array is never actually passed to a function. As a convenience, any parameter declarations which "look like" arrays, e.g.

f(a)
char a[];

are treated by the compiler as if they were pointers, since that is what the function will receive if an array is passed:

f(a)
char *a;

This conversion holds only within function formal parameter declarations, nowhere else. If this conversion bothers you, avoid it; many people have concluded that the confusion it causes outweighs the small advantage of having the declaration "look like" the call and/or the uses within the function.

References: K&R I Sec. 5.3 p. 95, Sec. A10.1 p. 205; K&R II Sec. 5.3 p. 100, Sec. A8.6.3 p. 218, Sec. A10.1 p. 226;

When is an array name or a function name 'converted' into a pointer ? (in C)

An expression of array type is implicitly converted to a pointer to the array object's first element unless it is:

  • The operand of the unary & operator;
  • The operand of sizeof; or
  • A string literal in an initializer used to initialize an array object.

An examples of the third case are:

char arr[6] = "hello";

"hello" is an array expression, of type char[6] (5 plus 1 for the '\0' terminator). It's not converted to an address; the full 6-byte value of of "hello" is copied into the array object arr.

On the other hand, in this:

char *ptr = "hello";

the array expression "hello" "decays" to a pointer to the 'h', and that pointer value is used to initialize the pointer object ptr. (It should really be const char *ptr, but that's a side issue.)

An expression of function type (such as a function name) is implicitly converted to a pointer to the function unless it is:

  • The operand of the unary & operator; or
  • The operand of sizeof (sizeof function_name is illegal, not the size of a pointer).

That's it.

In both cases, no pointer object is created. The expression is converted to ("decays" to) a pointer value, also known as an address.

(The "conversion" in both these cases isn't an ordinary type conversion like the one specified by a cast operator. It doesn't take the value of an operand and use it to compute the value of the result, as would happen for an int-to-float conversion. Rather an expression of array or function type is "converted" at compile time to an expression of pointer type. In my opinion the word "adjusted" would have been clearer than "converted".)

Note that both the array indexing operator [] and the function call "operator" () require a pointer. In an ordinary function call like func(42), the function name func "decays" to a pointer-to-function, which is then used in the call. (This conversion needn't actually be performed in the generated code, as long as the function call does the right thing.)

The rule for functions has some odd consequences. The expression func is, in most contexts, converted to a pointer to the function func. In &func, func is not converted to a pointer, but & yields the function's address, i.e., a pointer value. In *func, func is implicitly converted to a pointer, then * dereferences it to yield the function itself, which is then (in most contexts) converted to a pointer. In ****func, this happens repeatedly.

(A draft of the C11 standard says that there's another exception for arrays, namely when the array is the operand of the new _Alignof operator. This is an error in the draft, corrected in the final published C11 standard; _Alignof can only be applied to a parenthesized type name, not to an expression.)

The address of an array and the address of its first member:

int arr[10];
&arr; /* address of entire array */
&arr[0]; /* address of first element */

are the same memory address, but they're of different types. The former is the address of the entire array object, and is of type int(*)[10] (pointer to array of 10 ints); the latter is of type int*. The two types are not compatible (you can't legally assign an int* value to an int(*)[10] object, for example), and pointer arithmetic behaves differently on them.

There's a separate rule that says that a declared function parameter of array or function type is adjusted at compile time (not converted) to a pointer parameter. For example:

void func(int arr[]);

is exactly equivalent to

void func(int *arr);

These rules (conversion of array expressions and adjustment of array parameters) combine to create a great deal of confusion regarding the relationship between arrays and pointers in C.

Section 6 of the comp.lang.c FAQ does an excellent job of explaining the details.

The definitive source for this is the ISO C standard. N1570 (1.6 MB PDF) is the latest draft of the 2011 standard; these conversions are specified in section 6.3.2.1, paragraphs 3 (arrays) and 4 (functions). That draft has the erroneous reference to _Alignof, which doesn't actually apply.

Incidentally, the printf calls in your example are strictly incorrect:

int fruits[10];
printf("Address IN constant pointer is %p\n",fruits);
printf("Address OF constant pointer is %p\n",&fruits);

The %p format requires an argument of type void*. If pointers of type int* and int(*)[10] have the same representation as void* and are passed as arguments in the same way, as is the case for most implementations, it's likely to work, but it's not guaranteed. You should explicitly convert the pointers to void*:

int fruits[10];
printf("Address IN constant pointer is %p\n", (void*)fruits);
printf("Address OF constant pointer is %p\n", (void*)&fruits);

So why is it done this way? The problem is that arrays are in a sense second-class citizens in C. You can't pass an array by value as an argument in a function call, and you can't return it as a function result. For arrays to be useful, you need to be able to operate on arrays of different lengths. Separate strlen functions for char[1], for char[2], for char[3], and so forth (all of which are distinct types) would be impossibly unwieldy. So instead arrays are accessed and manipulated via pointers to their elements, with pointer arithmetic providing a way to traverse those elements.

If an array expression didn't decay to a pointer (in most contexts), then there wouldn't be much you could do with the result. And C was derived from earlier languages (BCPL and B) that didn't necessarily even distinguish between arrays and pointers.

Other languages are able to deal with arrays as first-class types but doing so requires extra features that wouldn't be "in the spirit of C", which continues to be a relatively low-level language.

I'm less sure about the rationale for treating functions this way. It's true that there are no values of function type, but the language could have required a function (rather than a pointer-to-function) as the prefix in a function call, requiring an explicit * operator for an indirect call: (*funcptr)(arg). Being able to omit the * is a convenience, but not a tremendous one. It's probably a combination of historical inertia and consistency with the treatment of arrays.



Related Topics



Leave a reply



Submit