Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does getting the address of an array variable mean?

Tags:

c

pointers

Today I read a C snippet which really confused me:

#include <stdio.h>  int main(void) {     int a[] = {0, 1, 2, 3};      printf("%d\n", *(*(&a + 1) - 1));     return 0; } 

In my opinion, &a + 1 makes no sense, but it runs without error.

What does it mean? And does the K&R C bible cover this?

UPDATE 0: After reading the answers, I realize that these two expressions mainly confuses me:

  1. &a + 1, which has been asked on Stack Overflow: about the expression “&anArray” in c

  2. *(&a + 1) -1, which is related to array decaying.

like image 672
whatacold Avatar asked Jul 05 '16 11:07

whatacold


People also ask

What is the address of array variable?

The individual elements in ar are aligned to single bytes. I.e., the address of ar[0] is the same as the address of ar itself, the address of ar[1] is one byte after ar (and not 4 bytes after ar), ..., the address of ar[4] is ar plus 5 bytes and thus not aligned to 4 bytes.

What does the address of a variable mean?

An address is a non-negative integer. Each time a program is run the variables may or may not be located in same memory locations. Each time you run the program above may or may not result in the same output.

What do you call the address of an array?

The memory address of the first element of an array is called first address, foundation address, or base address. Because the mathematical concept of a matrix can be represented as a two-dimensional grid, two-dimensional arrays are also sometimes called matrices.

What is meant by base address of an array?

Base address is the address of list element of the array. If we pass only name of the Array to a function, it basically means that The base address is passed.


1 Answers

First a little reminder (or something new if you didn't know this before): For any array or pointer p and index i the expression p[i] is exactly the same as *(p + i).

Now to hopefully help you understand what's going on...

The array a in your program is stored somewhere in memory, exactly where doesn't really matter. To get the location of where a is stored, i.e. get a pointer to a, you use the address-of operator & like &a. The important thing to learn here is that a pointer by itself doesn't mean anything special, the important thing is the base type of the pointer. The type of a is int[4], i.e. a is an array of four int elements. The type of the expression &a is a pointer to an array of four int, or int (*)[4]. The parentheses are important, because the type int *[4] is an array of four pointers to int, which is quite a different thing.

Now to get back to the initial point, that p[i] is the same as *(p + i). Instead of p we have &a, so our expression *(&a + 1) is the same as (&a)[1].

Now that explains what *(&a + 1) means and what it does. Now let us think for a while about the memory layout of the array a. In memory it looks something like

 +---+---+---+---+ | 0 | 1 | 2 | 3 | +---+---+---+---+ ^ | &a 

The expression (&a)[1] treats &a as it was an array of arrays, which it definitely isn't, and accessing the second element in this array, which will be out of bounds. This of course technically is undefined behavior. Let us run with it for a moment though, and consider how that would look like in memory:

 +---+---+---+---+---+---+---+---+ | 0 | 1 | 2 | 3 | . | . | . | . | +---+---+---+---+---+---+---+---+ ^               ^ |               | (&a)[0]         (&a)[1] 

Now remember that the type of a (which is the same as (&a)[0] and therefore means that (&a)[1] must also be this type) is array of four int. Since arrays naturally decays to pointers to its first element, the expression (&a)[1] is the same as &(&a)[1][0], and its type is pointer to int. So when we use (&a)[1] in an expression what the compiler gives us is a pointer to the first element in the second (non-existing) array of &a. And once again we come to the p[i] equals *(p + i) equation: (&a)[1] is a pointer to int, it's p in the *(p + i) expression, so the full expression is *((&a)[1] - 1), and looking at the memory layout above subtracting one int from the pointer given by (&a)[1] gives us the element before (&a)[1] which is the last element in (&a)[0], i.e. it gives us (&a)[0][3] which is the same as a[3].

So the expression *(*(&a + 1) - 1) is the same as a[3].

It's long-winded, and passes through dangerous territory (what with the out-of-bounds indexing), but due to the power of pointer arithmetic it all works out in the end. I don't recommend you ever write code like this though, it needs people to be really know how these transformations work to be able to decipher it.

like image 113
Some programmer dude Avatar answered Nov 10 '22 23:11

Some programmer dude