Need help understanding pointer to a type of n elements in C (int (*p)[10])?

I am studying pointers in C and have written the following code:

int main() {
    int a = 10;
    int *pa = &a;
    printf("Value of pa : %pn", pa);         // 0x7fffd215b5b8
    printf("Value of *pa : %dn", *pa);       // 10

    int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int (*parr)[10] = &arr;
    
    printf("Address of arr : %pn", &arr);    // 0x7fffd215b5d0
    printf("Address of parr : %pn", &parr);  // 0x7fffd215b5c0
    printf("Value of parr : %pn", parr);     // 0x7fffd215b5d0
    printf("Value of *parr : %pn", (*parr)); // 0x7fffd215b5d0 <---WHY?
    printf("Value of first element : %dn", **parr); //1

    // In order to print elements using pointer parr
    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
        printf("%dt", *((*parr)+i));  // 1 2 3 ... values stored in arr

    return 0;
}

For the pointer pa, the value that it holds is the address of the variable a. When we do *pa we are de-referencing the (value of p)/(address of a) i.e *(0x7fffd215b5b8) which is equal to 10.

For pointer parr, the value that it holds is the base address of an array of 10 elements (0x7fffd215b5d0). Shouldn’t *parr i.e. *(0x7fffd215b5d0) de-reference the value that it contains similar to what *pa did previously and output 1? Why does *parr display the value that it contains i.e. base address of arr?

And why do I need to de-reference the pointer twice to get the values for the array (I have managed to get the output by trial and error and don’t really understand why it’s supposed to work this way). The pointer *parr that I have initialized is a single pointer and not a double pointer right?

3

An array decays into a pointer to its first element.


First, the basics.

arr is an array.
It has type int [10].
The array is located at 0x7fffd215b5d0.

parr is a pointer to an array.
It has type int (*)[10].
The pointer is located at 0x7fffd215b5c0.
Its value is 0x7fffd215b5d0.

*parr is an array.
It has type int [10].
The array is located at 0x7fffd215b5d0.

Now on to the question.

Except in a couple of very specific situations (&a, sizeof a, typeof a), using an array doesn’t produce the array but a pointer to its first element. We say it decays into a pointer to its first element.

arr decays into a pointer to its first element.
It has type int *.
Its value is 0x7fffd215b5d0.

*parr decays into a pointer to its first element.
It has type int *.
Its value is 0x7fffd215b5d0.

The code in question is equivalent to the following:

printf("Value of *parr : %pn", &((*parr)[0]));

Using pointers to arrays complicates things. It’s more common to use pointers to the first element of arrays.

int *pele = arr;  // Same as `&(arr[0])`
printf("Value of first element : %dn", *pele);    // 1
printf("Value of first element : %dn", pele[0]);  // 1

More complete demo:

int arr[] = {1,2,3,4,5,6,7,8,9,10};
int (*parr)[10] = &arr;
int *pele = arr;  // Same as `&(arr[0])`

printf( "Address of arr:           %pn", (void*)&arr  );
printf( "Address of parr:          %pn", (void*)&parr );
printf( "Value of parr:            %pn", (void*)parr  );
printf( "Address of pele:          %pn", (void*)&pele );
printf( "Value of pele:            %pn", (void*)pele  );
printf( "n" );
printf( "Address of first element:n" );
printf( "via decay of arr:         %pn", (void*)arr   );
printf( "via decay of *parr:       %pn", (void*)*parr );
printf( "via pele:                 %pn", (void*)pele  );
printf( "n" );
printf( "Value of first element:n" );
printf( "*p, via decay of arr:     %dn", *arr         );
printf( "*p, via decay of *parr:   %dn", **parr       );
printf( "*p, via pele:             %dn", *pele        );
printf( "p[0], via decay of arr:   %dn", arr[0]       );
printf( "p[0], via decay of *parr: %dn", (*parr)[0]   );
printf( "p[0], via pele:           %dn", pele[0]      );

In response to comment, a little about dynamically allocating 2d arrays.

A common convention to allocate an array dynamically is to use

Type *p = malloc( n * sizeof *p );

It’s no exception with 2d arrays.

// Non-dynamic.
int a[rows][cols];                                                a[row][col]

// Same, but saves decayed pointer.
int a[rows][cols];  int (*p)[cols] = a;                           p[row][col]

// Dynamic.
                    int (*p)[cols] = malloc( rows * sizeof *p );  p[row][col]

p is a int (*)[cols], so
*p is a int [cols], so
sizeof *p is the size of a int [cols], so
sizeof *p is equal to cols * sizeof int, so
rows * sizeof *p is equivalent to rows * cols * sizeof int.

6

printf("Value of *parr : %pn", (*parr)); // 0x7fffd215b5d0 <---WHY?

parr is a pointer to an array, so *parr is the array itself. When an array appears in an expression, the actual value used by the compiler is the address of the first element, this is &(*parr)[0], so you get the first element address.

Beware that &arr and arr are pointers pointing to the same place, but they are different typed pointers (first is the address of an array of 10 ints and the second is a pointer to int)

In C, an array does not have a value that can be printed. Mathematically, the value of an array of n elements would be an ordered n-tuple of values, but C does not provide any way to work with ordered tuples. The elements of an array must be accessed individually, using subscripting or pointers.

To assist programmers with this, when an array appears in an expression, it is automatically converted to a pointer to its first element, unless the array is the operand of sizeof, is the operand of unary &, or is a string literal used to initialize an array.

In printf("Value of *parr : %pn", (*parr));, parr is a pointer to an array, so *parr is an array. Since *parr is an array, and is not in one of the exceptions listed above, it is automatically converted to the address of the first element of the array, and that address is what is printed.

In printf("Value of first element : %dn", **parr);, *parr is converted to a pointer to the first element of the array, and then **parr dereferences that pointer to provide the first element of the array.

An array is already pointing toward the values it contains.

arr[0] == *arr;

If *parr = &arr, it points towards a pointer that points towards others values.

So double pointing to point on the first value.
a contains only the value. arr contains pointers to every values.

What you correctly understand is that address is representing a section in memory where something is stored. A pointer to an address is representing the address and the value can be inferred via dereferencing it.

So, your a variable is located at the address of &a and has a value of 10. Therefore, pa has the value of the address of a and it can be dereferenced via *a, which will result in 10.

arr is an array and an array is a section in memory, whose items are sections in the memory associated to this array.

arr[0], for example is the first element, its value can also be inferred via *a. arr[1] is the second element, its value can also be inferred via *(a + 1)).

So each element of the array can be inferred as a pointer with the formula of *(a + offset) and it is equivalent to a[offset].

So, when you create a pointer to this array, which you call parr, *parr is pointing to the array. So, the value of this is the array and in order to infer a value of the array, *parr is not enough, because it’s not specific-enough to know which element you mean. **parr is dereferencing the array pointer and dereferencing the first element, as it is equivalent to *(*(parr + 0)) and of course, you can change the offset from 0 to the offset that you want to infer.

In short, a pointer to an array points to the array, not to its first element and since arr is not equivalent to arr[0] or *arr, *parr does refer to the array and not to its element.

I’ll suggest you during your starting with pointers to make the notation a bit simpler:

int arr[] = {1,2,3,4,5,6,7,8,9,10};   // Array of numbers
int* parr[10] = &arr;                 // Array of pointers

Now, the assign is a bit in correct because on arrays that &arr means the address that the array starts.

So for a single element it should be:

int* parr = &arr;                 // pointer of first integer

Now to get the pointer of each element you have to move sizeof(int) to next element

so:
*parr -> 1

parr = parr + sizeof(int)

*parr -> 2

as you do on the for loop

In your case you are printing the value of the pointer, just remove the ()

3

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