Is an array name a pointer?

Is an array’s name a pointer in C?
If not, what is the difference between an array’s name and a pointer variable?

8

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.

20

When an array is used as a value, its name represents the address of the first element.
When an array is not used as a value its name represents the whole array.

int arr[7];

/* arr used as value */
foo(arr);
int x = *(arr + 1); /* same as arr[1] */

/* arr not used as value */
size_t bytes = sizeof arr;
void *q = &arr; /* void pointers are compatible with pointers to any object */

0

If an expression of array type (such as the array name) appears in a larger expression and it isn’t the operand of either the & or sizeof operators, then the type of the array expression is converted from “N-element array of T” to “pointer to T”, and the value of the expression is the address of the first element in the array.

In short, the array name is not a pointer, but in most contexts it is treated as though it were a pointer.

Edit

Answering the question in the comment:

If I use sizeof, do i count the size of only the elements of the array? Then the array “head” also takes up space with the information about length and a pointer (and this means that it takes more space, than a normal pointer would)?

When you create an array, the only space that’s allocated is the space for the elements themselves; no storage is materialized for a separate pointer or any metadata. Given

char a[10];

what you get in memory is

   +---+
a: |   | a[0]
   +---+ 
   |   | a[1]
   +---+
   |   | a[2]
   +---+
    ...
   +---+
   |   | a[9]
   +---+

The expression a refers to the entire array, but there’s no object a separate from the array elements themselves. Thus, sizeof a gives you the size (in bytes) of the entire array. The expression &a gives you the address of the array, which is the same as the address of the first element. The difference between &a and &a[0] is the type of the result1char (*)[10] in the first case and char * in the second.

Where things get weird is when you want to access individual elements – the expression a[i] is defined as the result of *(a + i) – given an address value a, offset i elements (not bytes) from that address and dereference the result.

The problem is that a isn’t a pointer or an address – it’s the entire array object. Thus, the rule in C that whenever the compiler sees an expression of array type (such as a, which has type char [10]) and that expression isn’t the operand of the sizeof or unary & operators, the type of that expression is converted (“decays”) to a pointer type (char *), and the value of the expression is the address of the first element of the array. Therefore, the expression a has the same type and value as the expression &a[0] (and by extension, the expression *a has the same type and value as the expression a[0]).

C was derived from an earlier language called B, and in B a was a separate pointer object from the array elements a[0], a[1], etc. Ritchie wanted to keep B’s array semantics, but he didn’t want to mess with storing the separate pointer object. So he got rid of it. Instead, the compiler will convert array expressions to pointer expressions during translation as necessary.

Remember that I said arrays don’t store any metadata about their size. As soon as that array expression “decays” to a pointer, all you have is a pointer to a single element. That element may be the first of a sequence of elements, or it may be a single object. There’s no way to know based on the pointer itself.

When you pass an array expression to a function, all the function receives is a pointer to the first element – it has no idea how big the array is (this is why the gets function was such a menace and was eventually removed from the library). For the function to know how many elements the array has, you must either use a sentinel value (such as the 0 terminator in C strings) or you must pass the number of elements as a separate parameter.


  1. Which *may* affect how the address value is interpreted – depends on the machine.

12

An array declared like this

int a[10];

allocates memory for 10 ints. You can’t modify a but you can do pointer arithmetic with a.

A pointer like this allocates memory for just the pointer p:

int *p;

It doesn’t allocate any ints. You can modify it:

p = a;

and use array subscripts as you can with a:

p[2] = 5;
a[2] = 5;    // same
*(p+2) = 5;  // same effect
*(a+2) = 5;  // same effect

2

The array name by itself yields a memory location, so you can treat the array name like a pointer:

int a[7];

a[0] = 1976;
a[1] = 1984;

printf("memory location of a: %p", a);

printf("value at memory location %p is %d", a, *a);

And other nifty stuff you can do to pointer (e.g. adding/substracting an offset), you can also do to an array:

printf("value at memory location %p is %d", a + 1, *(a + 1));

Language-wise, if C didn’t expose the array as just some sort of “pointer”(pedantically it’s just a memory location. It cannot point to arbitrary location in memory, nor can be controlled by the programmer). We always need to code this:

printf("value at memory location %p is %d", &a[1], a[1]);

2

I think this example sheds some light on the issue:

#include <stdio.h>
int main()
{
        int a[3] = {9, 10, 11};
        int **b = &a;

        printf("a == &a: %dn", a == b);
        return 0;
}

It compiles fine (with 2 warnings) in gcc 4.9.2, and prints the following:

a == &a: 1

oops 🙂

So, the conclusion is no, the array is not a pointer, it is not stored in memory (not even read-only one) as a pointer, even though it looks like it is, since you can obtain its address with the & operator. But – oops – that operator does not work :-)), either way, you’ve been warned:

p.c: In function ‘main’:
pp.c:6:12: warning: initialization from incompatible pointer type
  int **b = &a;
            ^
p.c:8:28: warning: comparison of distinct pointer types lacks a cast
  printf("a == &a: %dn", a == b);

C++ refuses any such attempts with errors in compile-time.

Edit:

This is what I meant to demonstrate:

#include <stdio.h>
int main()
{
    int a[3] = {9, 10, 11};
    void *c = a;

    void *b = &a;
    void *d = &c;

    printf("a == &a: %dn", a == b);
    printf("c == &c: %dn", c == d);
    return 0;
}

Even though c and a “point” to the same memory, you can obtain address of the c pointer, but you cannot obtain the address of the a pointer.

2

The following example provides a concrete difference between an array name and a pointer. Let say that you want to represent a 1D line with some given maximum dimension, you could do it either with an array or a pointer:

typedef struct {
   int length;
   int line_as_array[1000];
   int* line_as_pointer;
} Line;

Now let’s look at the behavior of the following code:


void do_something_with_line(Line line) {
   line.line_as_pointer[0] = 0;
   line.line_as_array[0] = 0;
}

void main() {
   Line my_line;
   my_line.length = 20;
   my_line.line_as_pointer = (int*) calloc(my_line.length, sizeof(int));

   my_line.line_as_pointer[0] = 10;
   my_line.line_as_array[0] = 10;

   do_something_with_line(my_line);

   printf("%d %dn", my_line.line_as_pointer[0], my_line.line_as_array[0]);
};

This code will output:

0 10

That is because in the function call to do_something_with_line the object was copied so:

  1. The pointer line_as_pointer still contains the same address it was pointing to
  2. The array line_as_array was copied to a new address which does not outlive the scope of the function

So while arrays are not given by values when you directly input them to functions, when you encapsulate them in structs they are given by value (i.e. copied) which outlines here a major difference in behavior compared to the implementation using pointers.

NO. An array name is NOT a pointer. You cannot assign to or modify an array name, but you can for a pointer.

int arr[5];
int *ptr;

/* CAN assign or increment ptr */

ptr = arr;
ptr++;

/* CANNOT assign or increment arr */  

arr = ptr;
arr++;

/* These assignments are also illegal */

arr = anotherarray;
arr = 0;

From K&R Book:

There is one difference between an array name and a pointer that must
be kept in mind. A pointer is a variable, but an array name is not a
variable.

sizeof is the other big difference.

sizeof(arr); /* size of the entire array */
sizeof(ptr); /* size of the memory address */

Arrays do behave like or decay into a pointer in some situations (&arr[0]). You can see other answers for more examples of this. To reiterate a few of these cases:

void func(int *arr) { }
void func2(int arr[]) { }  /* same as func */

ptr = arr + 1;  /* pointer arithmetic */
func(arr);      /* passing to function */

Even though you cannot assign or modify the array name, of course can modify the contents of the array

arr[0] = 1;

Here is how I understood the differences (and the similarities) between arrays and pointers.

int v = 0;

    |       |
    |-------|
100 |       |   Name 1 - v
    |   0   |   Name 2 - *100
    |       |
103 |       |
    |-------|
    |       |

Even though *100 is technically not a name, and is instead a unary operator
(i.e. *) along with its operand (i.e. 100), we can still think of it as a name
in order to understand how addresses/pointers work.

Now, &v means the starting address of the variable whose name is v (i.e. 100).
Also, the data type of 100 is int* (i.e. pointer-to-int).

1. printf("%dn", v)
   is equivalent to printf("%dn", 0)
   is equivalent to printf("%dn", *100)
2. v = 10
   is equivalent to *100 = 10
3. v + 1
   is equivalent to 0 + 1
   is equivalent to *100 + 1
4. scanf("%d", &v)
   is equivalent to scanf("%d", &(*100))
   is equivalent to scanf("%d", 100)
5. func(&v)
   is equivalent to func(&(*100))
   is equivalent to func(100)
and so on.

Since the data type of 10 is int, therefore it can be assigned to the variable
whose name is v because that variable's data type is also int.
Similarly, since the data type of 100 is int* (i.e. pointer-to-int), therefore
it can be assigned to a variable whose data type is also int*.

////////////////////////////////////////////////////////////////////////////////

int* ptr = &v; is equivalent to int* ptr = 100;

    |       |
    |-------|
100 |       |   Name 1 - v
    |   0   |   Name 2 - *100
    |       |
103 |       |
    |-------|
    |       |

    |       |
    |-------|
200 |       |   Name 1 - ptr
    |       |   Name 2 - *200
    |       |
    |  100  |
    |       |
    |       |
    |       |
207 |       |
    |-------|
    |       |

Now, for eg., when evaluating v + 1, v is equivalent to 0 because 0 is the value
which is stored in the variable whose name is v.
Similarly, when evaluating *ptr, ptr is equivalent to 100 because 100 is the
value which is stored in the variable whose name is ptr.

1. printf("%dn", v)
   is equivalent to printf("%dn", 0)
   is equivalent to printf("%dn", *100)
   is equivalent to printf("%dn", *ptr)
2. v = 10
   is equivalent to *100 = 10
   is equivalent to *ptr = 10
3. v + 1
   is equivlent to 0 + 1
   is equivalent to *100 + 1
   is equivalent to *ptr + 1
4. scanf("%d", &v)
   is equivalent to scanf("%d", &(*100))
   is equivalent to scanf("%d", 100)
   is equivalent to scanf("%d", &(*ptr))
   is equivalent to scanf("%d", ptr)
5. func(&v)
   is equivalent to func(&(*100))
   is equivalent to func(100)
   is equivalent to func(&(*ptr))
   is equivalent to func(ptr)
and so on.

So, we can also think that the variable whose name is v gets a third name, i.e.
*ptr.

    |       |
    |-------|
100 |       |   Name 1 - v
    |   0   |   Name 2 - *100
    |       |   Name 3 - *ptr
103 |       |
    |-------|
    |       |

Also, to get the address of this variable, we can use &v, 100 or ptr.

////////////////////////////////////////////////////////////////////////////////

int main(void)
{
    int v;
    ...
    func(&v);
    ...
}

void func(int* ptr)
{
    ...
    *ptr = 5;
    ...
}

Here, the function call func(&v) is equivalent to func(100).

So, when func() is executed, the name v of the variable which is local to main()
goes out of scope.

    |       |
    |-------|
100 |       |   Name 1 - xxxx
    |   0   |   Name 2 - *100
    |       |
103 |       |
    |-------|
    |       |

Now, a variable which is local to func() whose name is ptr and whose data type
is pointer-to-int is created.

    |       |
    |-------|
200 |       |   Name 1 - ptr
    |       |   Name 2 - *200
    |       |
    |  100  |
    |       |
    |       |
    |       |
207 |       |
    |-------|
    |       |

Now, the variable which is local to main() gets a new name.

    |       |
    |-------|
100 |       |   Name 1 - xxxx
    |   0   |   Name 2 - *100
    |       |   Name 3 - *ptr
103 |       |
    |-------|
    |       |

And, to get the address of the variable local to main(), we can use 100 or ptr.

After executing *ptr = 5, when func() returns, the variable which is local to
func() and whose name is ptr is destroyed, and the name v of the variable which
is local to main() is restored.
Also, since the variable which is local to func() and whose name is ptr is
destroyed, therefore the name *ptr of the variable which is local to main() is
also destroyed.

    |       |
    |-------|
100 |       |   Name 1 - v
    |   5   |   Name 2 - *100
    |       |
103 |       |
    |-------|
    |       |

////////////////////////////////////////////////////////////////////////////////

int arr[3] = {7, 8, 9};

arr is the name of the entire array.

    |       |
    |-------|
300 |       |   Name - *300
    |   7   |
    |       |
303 |       |
    |-------|
304 |       |   Name - *304
    |   8   |
    |       |
307 |       |
    |-------|
308 |       |   Name - *308
    |   9   |
    |       |
311 |       |
    |-------|
    |       |

When the expression v + 1.0 is evaluated, the data type of v is converted from
int to double only for the purpose of evaluating that expression.

Similarly, except for those 4 cases (&, sizeof, alignof, string literal used to
initialize an array), arr is converted to the address of the first element of
the array only for the purpose of evaluating the corresponding expression.

Also, for eg., if the array consists of elements of data type int, then the
data type of the resultant address is pointer-to-int. So, the resultant address
can be assigned to a variable of data type pointer-to-int.
For eg., int* ptr = arr;

So, in other words, except for those 4 cases, arr is converted to a pointer to
the first element of the array only for the purpose of evaluating the
corresponding expression.

When an integer is added to an address/pointer, the resultant address is
calculated according to the data type of the address/pointer.
For eg., 300 + 1 gives 304.

arr[i] is equivalent to *(arr + i).

Now, arr[1]
     is equivalent to *(arr + 1)
     is equivalent to *(300 + 1)
     is equivalent to *304

And, &(arr[1])
     is equivalent to &(*(arr + 1))
     is equivalent to &(*(300 + 1))
     is equivalent to &(*304)
     is equivalent to 304

Also, arr + 1
      is equivalent to 300 + 1
      is equivalent to 304

1. printf("%dn", arr[1])
   is equivalent to printf("%dn", 8)
   is equivalent to printf("%dn", *(arr + 1))
   is equivalent to printf("%dn", *304)
2. arr[1] = 10
   is equivalent to *(arr + 1) = 10
   is equivalent to *304 = 10
3. arr[1] + 1
   is equivalent to 8 + 1
   is equivalent to *(arr + 1) + 1
   is equivalent to *304 + 1
4. scanf("%d", &(arr[1]))
   is equivalent to scanf("%d", &(*(arr + 1)))
   is equivalent to scanf("%d", &(*304))
   is equivalent to scanf("%d", 304)
   is equivalent to scanf("%d", arr + 1)
5. func(&(arr[1]))
   is equivalent to func(&(*(arr + 1)))
   is equivalent to func(&(*304))
   is equivalent to func(304)
   is equivalent to func(arr + 1)
and so on.

So, we can also think that the elements of the array have other names.

    |       |
    |-------|
300 |       |   Name 1 - *300
    |   7   |   Name 2 - arr[0]
    |       |   Name 3 - *(arr + 0)
303 |       |
    |-------|
304 |       |   Name 1 - *304
    |   8   |   Name 2 - arr[1]
    |       |   Name 3 - *(arr + 1)
307 |       |
    |-------|
308 |       |   Name 1 - *308
    |   9   |   Name 2 - arr[2]
    |       |   Name 3 - *(arr + 2)
311 |       |
    |-------|
    |       |

Also, to get the address of, for eg., arr[1], we can use &(arr[1]), 304 or
arr + 1.

////////////////////////////////////////////////////////////////////////////////

int main(void)
{
    int arr[3];
    ...
    func(arr);
    ...
}

void func(int* arr)
{
    ...
    arr[1] = 6;
    ...
}

Here, the function call func(arr) is equivalent to func(300).

So, when func() is executed, the name arr of the entire array which is local to
main() goes out of scope, which means that Name 2 and Name 3 of every element of
the array also go out of scope.

    |       |
    |-------|
300 |       |   Name 1 - *300
    |   7   |   Name 2 - xxxx
    |       |   Name 3 - xxxx
303 |       |
    |-------|
304 |       |   Name 1 - *304
    |   8   |   Name 2 - xxxx
    |       |   Name 3 - xxxx
307 |       |
    |-------|
308 |       |   Name 1 - *308
    |   9   |   Name 2 - xxxx
    |       |   Name 3 - xxxx
311 |       |
    |-------|
    |       |

Now, a variable which is local to func() whose name is arr and whose data type
is pointer-to-int is created.

    |       |
    |-------|
400 |       |   Name 1 - arr
    |       |   Name 2 - *400
    |       |
    |  300  |
    |       |
    |       |
    |       |
407 |       |
    |-------|
    |       |

It should be noted that when the expression arr[1] = 6 is evaluated inside
func(), arr isn't the name of the array which is local to main(), and is instead
the name of the variable which is local to func().
So, there is no need to convert arr to a pointer, as arr is already a pointer.

Now, similar to main(), inside func() also
arr[1]
is equivalent to *(arr + 1)
is equivalent to *(300 + 1)
is equivalent to *304

And, &(arr[1])
     is equivalent to &(*(arr + 1))
     is equivalent to &(*(300 + 1))
     is equivalent to &(*304)
     is equivalent to 304

Also, arr + 1
      is equivalent to 300 + 1
      is equivalent to 304

So, the elements of the array which is local to main() get new names.

    |       |
    |-------|
300 |       |   Name 1 - *300
    |       |   Name 2 - xxxx
    |   7   |   Name 3 - xxxx
    |       |   Name 4 - arr[0]
303 |       |   Name 5 - *(arr + 0)
    |-------|
304 |       |   Name 1 - *304
    |       |   Name 2 - xxxx
    |   8   |   Name 3 - xxxx
    |       |   Name 4 - arr[1]
307 |       |   Name 5 - *(arr + 1)
    |-------|
308 |       |   Name 1 - *308
    |       |   Name 2 - xxxx
    |   9   |   Name 3 - xxxx
    |       |   Name 4 - arr[2]
311 |       |   Name 5 - *(arr + 2)
    |-------|
    |       |

And, to get the address of, for eg., arr[1], we can use &(arr[1]), 304 or
arr + 1.

After executing arr[1] = 6, when func() returns, the variable which is local to
func() and whose name is arr is destroyed, and the name arr of the entire array
which is local to main() is restored, which means that Name 2 and Name 3 of
every element of the array are also restored.
Also, since the variable which is local to func() and whose name is arr is
destroyed, therefore Name 4 and Name 5 of every element of the array are also
destroyed.

    |       |
    |-------|
300 |       |   Name 1 - *300
    |   7   |   Name 2 - arr[0]
    |       |   Name 3 - *(arr + 0)
303 |       |
    |-------|
304 |       |   Name 1 - *304
    |   6   |   Name 2 - arr[1]
    |       |   Name 3 - *(arr + 1)
307 |       |
    |-------|
308 |       |   Name 1 - *308
    |   9   |   Name 2 - arr[2]
    |       |   Name 3 - *(arr + 2)
311 |       |
    |-------|
    |       |

The array name behaves like a pointer and points to the first element of the array. Example:

int a[]={1,2,3};
printf("%pn",a);     //result is similar to 0x7fff6fe40bc0
printf("%pn",&a[0]); //result is similar to 0x7fff6fe40bc0

Both the print statements will give exactly same output for a machine. In my system it gave:

0x7fff6fe40bc0

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật