-1

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.

김선달
  • 1,485
  • 9
  • 23
Franc
  • 319
  • 9
  • 28
  • 1
    There is no advantage. They are identical. The differences you are asserting don't exist. – user207421 Jun 09 '20 at 09:35
  • Unclear what you're asking. – Aykhan Hagverdili Jun 09 '20 at 09:36
  • Suppose you're using "real life" pointers ... to people or families. What's the difference between pointing to John or to the Smiths (suppose you point to John Smith when pointing to the family)? – pmg Jun 09 '20 at 09:37
  • Use neither, use the 3rd, sane form: `for(i=0; i<10; i++) { printf("%d\n", arr[i]);`. This will also perform better than the pointer ones, if the compiler can't optimize away the pointer de-referencing. – Lundin Jun 09 '20 at 09:38
  • @pmg I give up, what is the difference? And how does it relate to the question? – user207421 Jun 09 '20 at 09:39
  • 1
    @pmg In this case it's rather someone either pointing at John Smith or John Smith's house. If you know where to find either, you know where to find him. And if you already knew where he lives, you need no pointers at all, just walk up to his door and knock, without asking for directions :) – Lundin Jun 09 '20 at 09:40
  • 2
    In this case there is no difference, but you may see one when you try to pass an array to function and [retain `sizeof` information](https://stackoverflow.com/questions/1461432/what-is-array-to-pointer-decay). – Yksisarvinen Jun 09 '20 at 09:41
  • @Lundin No it isn't. The language semantics guarantee that the two things are the same. – user207421 Jun 09 '20 at 09:41
  • 1
    There is no advantage in doing one or the other. All depends on what are you going to do. – armagedescu Jun 09 '20 at 09:42
  • @Yksisarvinen in C language the only way to pass that array pointer to a function without losing the size is if the function already knows the size. C++ already have better ways of doing that – Aykhan Hagverdili Jun 09 '20 at 09:42
  • When pointing to the person and then keep advancing to next person, next person, ..., ... (`p++`) how do you know you get to the next family? When pointing to families you can go to next family (`f++`) or dereference family and go the people – pmg Jun 09 '20 at 09:43
  • @MarquisofLorne Ok so go ahead and run this if they are the same: `int arr[10]; int (*ptr)[10] = arr; for (i = 0; i < 10; i++) { printf("%d\n", *ptr); ptr ++; }` "Same but different", or...? – Lundin Jun 09 '20 at 09:46
  • @_Static_assert I don't know C, but `int (*ptr)[10];` should preserve the size? I mean, you are right, `10` will be a part of the function signature, but that's the difference between plain pointer and pointer-to-array declaration that OP is asking about. – Yksisarvinen Jun 09 '20 at 09:50
  • 1
    This recent question might clarify things: https://stackoverflow.com/questions/62209942/what-int-ptr4-really-means-and-how-is-it-different-than-ptr. Possible dupe even. – Lundin Jun 09 '20 at 09:50
  • Read [*Modern C*](https://modernc.gforge.inria.fr/) or a good [C++ programming](http://stroustrup.com/Programming/) book. Notice that **C and C++ are very different programming languages**, see [this reference](http://en.cppreference.com/w/). In C++, prefer using [standard containers](https://en.cppreference.com/w/cpp/container). – Basile Starynkevitch Jun 09 '20 at 09:53
  • 1. C or C++? Else I wouldn't vote to reopen the question. 2. Who said there would be an advantage if using the first example? I guess this is just an opinion and with that the question should be closed as opinion-based anyway. – RobertS supports Monica Cellio Jun 09 '20 at 10:39
  • @RobertSsupportsMonicaCellio I think this question would've been better received if it were tagged just C. Lots of C++ folks will scoff at digging into the C core, especially now that they've got the "better"/modern `std::array`. – Petr Skocik Jun 09 '20 at 10:39
  • @PSkocik Yes, or only C++ (dependent upon what OP is looking for). The reason why an OP tags both languages is in most cases just to cover more audience, but it makes it way harder (if not completely impossible) to fully address a question by an answer at the other side. – RobertS supports Monica Cellio Jun 09 '20 at 10:43
  • @RobertSsupportsMonicaCellio Ironically, it also increases the chance of infuriating one of the two crowds, resulting in downvotes and close votes, which then have the opposite effect :D – Petr Skocik Jun 09 '20 at 10:47
  • 1
    @PSkocik Yes, exactly. Which is good shown here. C++ vs. C folk. Seems to be a never ending conflict. :-) – RobertS supports Monica Cellio Jun 09 '20 at 10:48

2 Answers2

3

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.
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
1

There is no advantage. The types of the pointers to access the elements of array arr are just different.

In the first case, ptr is of type int (*)[10] - pointer to array of 10 int.

In the second case, ptr is of type int * - pointer to int.


ptr = arr; - arr decays to a pointer to the first element of arr (type int*).

ptr = &arr; - &arr is a pointer to an array of 10 elements to int (type int (*)[10]).


Side note:

If you would increment ptr in the first example, the pointer will point to a memory location past the array arr. Dereferencing that pointer would invoke undefined behavior at least in C:

When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P) + N (equivalently, N + (P)) and (P) - N (where N has the value n) point to, respectively, the i + n-th and i − n-th elements of the array object, provided they exist.

Moreover, if the expression P points to the last element of an array object, the expression (P) + 1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q) - 1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.

Source: C18, 6.5.6/8

For C++: Dereferencing one past the end pointer to array type

While incrementing ptr in the second example just let ptr point to the following char element of array arr.

Community
  • 1
  • 1