Are Arrays Pointers

Are arrays Pointers?

Let's get the important stuff out of the way first: arrays are not pointers. Array types and pointer types are completely different things and are treated differently by the compiler.

Where the confusion arises is from how C treats array expressions. N1570:

6.3.2.1 Lvalues, arrays, and function designators

...

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

Let's look at the following declarations:

int arr[10] = {0,1,2,3,4,5,6,7,8,9};
int *parr = arr;

arr is a 10-element array of int; it refers to a contiguous block of memory large enough to store 10 int values. The expression arr in the second declaration is of array type, but since it is not the operand of & or sizeof and it isn't a string literal, the type of the expression becomes "pointer to int", and the value is the address of the first element, or &arr[0].

parr is a pointer to int; it refers to a block of memory large enough to hold the address of a single int object. It is initialized to point to the first element in arr as explained above.

Here's a hypothetical memory map showing the relationship between the two (assuming 16-bit ints and 32-bit addresses):


Object Address 0x00 0x01 0x02 0x03
------ ------- ----------------------
arr 0x10008000 0x00 0x00 0x00 0x01
0x10008004 0x00 0x02 0x00 0x03
0x10008008 0x00 0x04 0x00 0x05
0x1000800c 0x00 0x06 0x00 0x07
0x10008010 0x00 0x08 0x00 0x09
parr 0x10008014 0x10 0x00 0x80 0x00

The types matter for things like sizeof and &; sizeof arr == 10 * sizeof (int), which in this case is 20, whereas sizeof parr == sizeof (int *), which in this case is 4. Similarly, the type of the expression &arr is int (*)[10], or a pointer to a 10-element array of int, whereas the type of &parr is int **, or pointer to pointer to int.

Note that the expressions arr and &arr will yield the same value (the address of the first element in arr), but the types of the expressions are different (int * and int (*)[10], respectively). This makes a difference when using pointer arithmetic. For example, given:

int arr[10] = {0,1,2,3,4,5,6,7,8,9};
int *p = arr;
int (*ap)[10] = &arr;

printf("before: arr = %p, p = %p, ap = %p\n", (void *) arr, (void *) p, (void *) ap);
p++;
ap++;
printf("after: arr = %p, p = %p, ap = %p\n", (void *) arr, (void *) p, (void *) ap);

the "before" line should print the same values for all three expressions (in our hypothetical map, 0x10008000). The "after" line should show three different values: 0x10008000, 0x10008002 (base plus sizeof (int)), and 0x10008014 (base plus sizeof (int [10])).

Now let's go back to the second paragraph above: array expressions are converted to pointer types in most circumstances. Let's look at the subscript expression arr[i]. Since the expression arr is not appearing as an operand of either sizeof or &, and since it is not a string literal being used to initialize another array, its type is converted from "10-element array of int" to "pointer to int", and the subscript operation is being applied to this pointer value. Indeed, when you look at the C language definition, you see the following language:

6.5.2.1 Array subscripting

...

2 A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).

In practical terms, this means you can apply the subscript operator to a pointer object as though it were an array. This is why code like

int foo(int *p, size_t size)
{
int sum = 0;
int i;
for (i = 0; i < size; i++)
{
sum += p[i];
}
return sum;
}

int main(void)
{
int arr[10] = {0,1,2,3,4,5,6,7,8,9};
int result = foo(arr, sizeof arr / sizeof arr[0]);
...
}

works the way it does. main is dealing with an array of int, whereas foo is dealing with a pointer to int, yet both are able to use the subscript operator as though they were both dealing with an array type.

It also means array subscripting is commutative: assuming a is an array expression and i is an integer expression, a[i] and i[a] are both valid expressions, and both will yield the same value.

In C, are arrays pointers or used as pointers?

Here's the exact language from the C standard (n1256):

6.3.2.1 Lvalues, arrays, and function designators

...

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

The important thing to remember here is that there is a difference between an object (in C terms, meaning something that takes up memory) and the expression used to refer to that object.

When you declare an array such as

int a[10];

the object designated by the expression a is an array (i.e., a contiguous block of memory large enough to hold 10 int values), and the type of the expression a is "10-element array of int", or int [10]. If the expression a appears in a context other than as the operand of the sizeof or & operators, then its type is implicitly converted to int *, and its value is the address of the first element.

In the case of the sizeof operator, if the operand is an expression of type T [N], then the result is the number of bytes in the array object, not in a pointer to that object: N * sizeof T.

In the case of the & operator, the value is the address of the array, which is the same as the address of the first element of the array, but the type of the expression is different: given the declaration T a[N];, the type of the expression &a is T (*)[N], or pointer to N-element array of T. The value is the same as a or &a[0] (the address of the array is the same as the address of the first element in the array), but the difference in types matters. For example, given the code

int a[10];
int *p = a;
int (*ap)[10] = &a;

printf("p = %p, ap = %p\n", (void *) p, (void *) ap);
p++;
ap++;
printf("p = %p, ap = %p\n", (void *) p, (void *) ap);

you'll see output on the order of

p = 0xbff11e58, ap = 0xbff11e58
p = 0xbff11e5c, ap = 0xbff11e80

IOW, advancing p adds sizeof int (4) to the original value, whereas advancing ap adds 10 * sizeof int (40).

More standard language:

6.5.2.1 Array subscripting


Constraints


1 One of the expressions shall have type ‘‘pointer to object type’’, the other expression shall have integer type, and the result has type ‘‘type’’.


Semantics


2 A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).

Thus, when you subscript an array expression, what happens under the hood is that the offset from the address of the first element in the array is computed and the result is dereferenced. The expression

a[i] = 10;

is equivalent to

*((a)+(i)) = 10;

which is equivalent to

*((i)+(a)) = 10;

which is equivalent to

 i[a] = 10;

Yes, array subscripting in C is commutative; for the love of God, never do this in production code.

Since array subscripting is defined in terms of pointer operations, you can apply the subscript operator to expressions of pointer type as well as array type:

int *p = malloc(sizeof *p * 10);
int i;
for (i = 0; i < 10; i++)
p[i] = some_initial_value();

Here's a handy table to remember some of these concepts:


Declaration: T a[N];

Expression Type Converts to Value
---------- ---- ------------ -----
a T [N] T * Address of the first element in a;
identical to writing &a[0]
&a T (*)[N] Address of the array; value is the same
as above, but the type is different
sizeof a size_t Number of bytes contained in the array
object (N * sizeof T)
*a T Value at a[0]
a[i] T Value at a[i]
&a[i] T * Address of a[i]

Declaration: T a[N][M];

Expression Type Converts to Value
---------- ---- ------------ -----
a T [N][M] T (*)[M] Address of the first subarray (&a[0])
&a T (*)[N][M] Address of the array (same value as
above, but different type)
sizeof a size_t Number of bytes contained in the
array object (N * M * sizeof T)
*a T [M] T * Value of a[0], which is the address
of the first element of the first subarray
(same as &a[0][0])
a[i] T [M] T * Value of a[i], which is the address
of the first element of the i'th subarray
&a[i] T (*)[M] Address of the i-th subarray; same value as
above, but different type
sizeof a[i] size_t Number of bytes contained in the i'th subarray
object (M * sizeof T)
*a[i] T Value of the first element of the i'th
subarray (a[i][0])
a[i][j] T Value at a[i][j]
&a[i][j] T * Address of a[i][j]

Declaration: T a[N][M][O];

Expression Type Converts to
---------- ---- -----------
a T [N][M][O] T (*)[M][O]
&a T (*)[N][M][O]
*a T [M][O] T (*)[O]
a[i] T [M][O] T (*)[O]
&a[i] T (*)[M][O]
*a[i] T [O] T *
a[i][j] T [O] T *
&a[i][j] T (*)[O]
*a[i][j] T
a[i][j][k] T

From here, the pattern for higher-dimensional arrays should be clear.

So, in summary: arrays are not pointers. In most contexts, array expressions are converted to pointer types.

Do arrays in Java store data or pointers

tl;dr

Manipulating an array of references to objects has no effect on the objects, and has no effect on the objects’ location in memory.

Objects

An array of objects is really an array of references (pointers) to objects. A pointer is an address to another location in memory.

We speak of the array as holding objects, but that is not technically accurate. Because Java does not expose pointers themselves to us as programmers, we are generally unaware of their presence. When we access an element in the array, we are actually retrieving a pointer, but Java immediately follows that pointer to locate the object elsewhere in memory.

This automatic look-up, following the pointer to the object, makes the array of pointers feel like an array of objects. The Java programmer thinks of her array as holding her objects when in reality the objects are a hop-skip-and-a-jump away.

Arrays in Java are implemented as contiguous blocks of memory. For an array of objects, the pointers to those objects are being stored in contiguous memory. But when we access the elements, we are jumping to another location in memory to access the actual object that we want.

Sample Image

Adding elements may be “cheap” in that if memory happens to be available next door in memory, it can be allocated to the array to make room for more elements. In practice this is unlikely. Chances are a new array must be built elsewhere in memory, with all the pointers being copied over to the new array and then discarding the original array.

Such a new-array-and-copy-over is “expensive”. When feasible, we want to avoid this operation. If you know the likely maximum size of your array, specify that size when declaring the array. The entire block of contiguous memory is claimed immediately, with empty content in the array until you later assign a pointer to the elements.

Inserting into the middle of an array is also expensive. Either a new array is built and elements copied over, or all the elements after the insertion point must be moved down into their neighboring position.

None of these operations to the array affect the objects. The objects are floating around in the ether of memory. The objects know nothing of the array. Operations on the array do not affect the objects nor their position in memory. The only relationship is that if the reference held in the array is the last reference still pointing to the object, then when that array element is cleared or deleted, the object becomes a candidate for garbage-collection.

Primitives

In Java, the eight primitive types (byte, short, int, long, float, double, boolean, and char) are not objects/classes and are not Object-Oriented Programming. One advantage is that they are fast and take little memory, compared to objects.

An array of primitives hold the values within the array itself. So these values are stored next to one another, contiguous in memory. No references/pointers. No jumping around in memory.

As for adding or inserting, the same behavior discussed above applies. Except that instead of pointers being shuffled around, the actual primitive values are being shuffled around.

Sample Image

Tips

In business apps, it is generally best to use objects.

That means using the wrapper classes instead of primitives. For example, Integer instead of int. The auto-boxing facility in Java makes this easier by automatically converting between primitive values and their object wrapper.

And preferring objects means using a Collection instead of arrays, usually a List, specifically a ArrayList. Or for immutable use, a List implementation returned from the new List.of method.

In contrast to business apps, in extreme situations where speed and memory usage are paramount, such as your game engine, then make the most of arrays and primitives.

In the future, the distinction between objects and primitives may blur if the work done in Project Valhalla comes to fruition.

Array and pointers in c++

I often hear that the name of an array is constant pointer to a block of memory

You've often been mislead - or you've simply misunderstood. An array is not a constant pointer to a block of memory. Array is an object that contains a sequence of sub-objects. All objects are a block of memory. A pointer is an object that contains an address of an object i.e. it points to the object.

So in the following quote, a is an array, p points to the first sub-object within a.

int a[10];

and

int * const p= a;

must be equal in a sense that p is pointer that points to the same block of memory as array a[] and also it may not be changed to point to another location in memory.

If that is your definition of equal, then that holds for non-array objects as well:

char c;
int * const p = &c;

Here p "points to the same memory as c" and may not be changed to point to another location in memory. Does that mean that char objects are "equal" to pointers? No. And arrays aren't either.

But isn't a (the name of the array), a constant pointer that points to the same element of the array?

No, the name of the array isn't a constant pointer. Just like name of the char isn't a constant pointer.

the name of an array holds the address of the first element in the array, right?

Let's be more general, this is not specific to arrays. The name of a variable "holds the address" of the object that the variable names. The address is not "held" in the memory at run time. It's "held" by the compiler at compile time. When you operate on a variable, the compiler makes sure that operations are done to the object at the correct address.

The address of the array is always the same address as where the first element (sub-object) of the array is. Therefore, the name indeed does - at least conceptually - hold the same address.

And if i use *(a+1), this is the same as a[1], right? [typo fixed]

Right. I'll elaborate: One is just another way of writing another in the case of pointers. Oh, but a isn't a pointer! Here is the catch: The array operand is implicitly converted to a pointer to first element. This implicit conversion is called decaying. This is special feature of array types - and it is the special feature which probably makes understanding the difference between pointers and arrays difficult the most.

So, even though the name of the array isn't a pointer, it can decay into a pointer. The name doesn't always decay into a pointer, just in certain contexts. It decays when you use operator[], and it decays when you use operator+. It decays when you pass the array to a function that accepts a pointer to the type of the sub-object. It doesn't decay when you use sizeof and it doesn't decay when you pass it to a function that accepts an array by reference.

Are arrays in C or C++ language actually Pointers

The amount of memory a pointer can point to is limited only by the amount of memory in the computer. It only points to where a particular item starts; it says nothing about how much follows. Actually, it does carry some information about the size of the first object it points to, but not about how many of those objects follow.

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.

Difference between pointer and array as template arguments

Isn't int[] simply a pointer too?

No, it simply isn't. Arrays are arrays and pointers are pointers. Arrays are not pointers and pointers are not arrays. int[] is an array.

There is a case where "array is pointer (and isn't an array)": In a function parameter delcaration, an array parameter will be adjusted to be a pointer to element of that array type. Such adjustment doesn't apply to other contexts. Template arguments are a separate context from function arguments and in that context the mentioned adjustment does not occur.

What's the difference between int[] and int* here as template arguments?

The difference is that in one case the type argument is an array, and in the other case the type argument is a pointer.



Related Topics



Leave a reply



Submit