首页 > 解决方案 > What is the advantage of a pointer to the base address of an array instead of pointer to the first element?

问题描述

Following is a code snippet showing a pointer to the base of an array and a pointer to the element;

1st case:

/* Pointer to an array of 10 integers */
int arr[10]
int (*ptr)[10];
ptr = & arr; //points to the base address of an array of 10 integers
for (i = 0; i < 10; i++) //prints the values
    printf("%d\n", *(*ptr + i)); 

2nd case:

/* Pointer to an array element*/
int arr[10];
int *ptr;
ptr = arr;  //Points to the first element of an array
for (i = 0; i < 10; i++) //prints the values
{
    printf("%d\n", *ptr);
    ptr ++;
}

I understood the difference in accessing the values in both cases. However I didnot understand what is the advantage of using the first case i.e. pointer to an array of 10 integers even if the second case looks simple.

标签: c++carrayspointers

解决方案


In your particular example

int arr[10]
int (*ptr)[10];
ptr = & arr; //points to the base address of an array of 10 integers
for (i = 0; i < 10; i++) //prints the values
    printf("%d\n", *(*ptr + i));  //NOTE: same as `ptr[0][i]`

there's no advantage or at least no advantage being utilized.

*(*ptr+i) with int (*ptr)[10]; will/should generate the same assembly as *(ptr+i) would with int ptr[10]; (Note: some/many might find ptr[0][i] and ptr[i] respectively as more readable renderings of these expressions)

Example:

int get_nth(int (*X)[10], int N) { return *(*X+N); }
int get_nth_(int *X, int N) { return *(X+N); }

x86_64 output (gcc -O3 or clang -O3):

get_nth:                                # @get_nth
        movsxd  rax, esi
        lea     rax, [rdi + 4*rax]
        ret
get_nth_:                               # @get_nth_
        movsxd  rax, esi
        lea     rax, [rdi + 4*rax]
        ret

https://gcc.godbolt.org/z/up7HXc

If int (*ptr)[10] were derived from a multidimensional array as in

int multidim[5][10]; //array 10 or array 5 of int
int (*ptr)[10]=&multidim[1];

you could use the first index to jump the pointer in increments of 10*sizeof(int) in addition to using the second one to jump in increments of sizeof(int) (as with a plain int*).

In a standalone example (i.e., where the 10-int block isn't part of a multidimensional array), probably the only "advantage" of int(*)[10] is that it retains the sizeof information (even across a function call boundary), and you could conceivably use this for explicit bounds-checking.

Bounds-checking example:

#include <stdio.h>

#define CHECKED_SUBSCRIPT(PtrArray, Index) (*(PtrArray))[BOUNDCHECK(Index,ARRCNT(*(PtrArray)))] /*{{{*/
    #include <assert.h>
    static inline size_t BOUNDCHECK(size_t Index, size_t Bound)
    {
        assert(Index <  Bound);
        return Index;
    }
        //sizeof(A) or void (=>error) if A isn't an array
        #define ARRSZ(A) (+ sizeof(A) + 0* /*{{{*/\
                     _Generic((__typeof(&(A)[0])*)0, __typeof(__typeof(A)*):(void)0,default:0) ) /*}}}*/
    #define ARRCNT(A) (ARRSZ(A)/sizeof((A)[0])) /*}}}*/
int main()
{
    int arr[10]={0,2,4,6,8,10,12,14,16,18};
    int (*ptr)[10] = &arr;
    for (int i = 0; i < 20; i++){
        printf("%d\n", CHECKED_SUBSCRIPT(ptr,i));
    }
}

Output:

0
2
4
6
8
10
12
14
16
18
a.out: boundschecking.c:7: BOUNDCHECK: Assertion `Index < Bound' failed.

推荐阅读