52

I know there is several questions about that which gives good (and working) solutions, but none IMHO which says clearly what is the best way to achieve this. So, suppose we have some 2D array :

int tab1[100][280];

We want to make a pointer that points to this 2D array. To achieve this, we can do :

int (*pointer)[280]; // pointer creation
pointer = tab1; //assignation
pointer[5][12] = 517; // use
int myint = pointer[5][12]; // use

or, alternatively :

int (*pointer)[100][280]; // pointer creation
pointer = &tab1; //assignation
(*pointer)[5][12] = 517; // use
int myint = (*pointer)[5][12]; // use 

OK, both seems to work well. Now I would like to know :

  • what is the best way, the 1st or the 2nd ?
  • are both equals for the compiler ? (speed, perf...)
  • is one of these solutions eating more memory than the other ?
  • what is the more frequently used by developers ?
3
  • 2
    Remember that an array of pointers (as in int *pointer[280];) is not the same thing as an array of arrays. Commented Feb 11, 2013 at 9:11
  • 1
    hello, ferdinand. i have corrected the 1st solution with parenthesis. Apologizes. Commented Feb 11, 2013 at 9:25
  • To get rid of gcc warnings in the assignation steps I needed the casts pointer = (int(*)[280])tab1 and pointer = (int(*)[100][280])&tab1 respectively. Commented Oct 11, 2023 at 12:57

4 Answers 4

47
//defines an array of 280 pointers (1120 or 2240 bytes)
int  *pointer1 [280];

//defines a pointer (4 or 8 bytes depending on 32/64 bits platform)
int (*pointer2)[280];      //pointer to an array of 280 integers
int (*pointer3)[100][280]; //pointer to an 2D array of 100*280 integers

Using pointer2 or pointer3 produce the same binary except manipulations as ++pointer2 as pointed out by WhozCraig.

I recommend using typedef (producing same binary code as above pointer3)

typedef int myType[100][280];
myType *pointer3;

Note: Since C++11, you can also use keyword using instead of typedef

using myType = int[100][280];
myType *pointer3;

in your example:

myType *pointer;                // pointer creation
pointer = &tab1;                // assignation
(*pointer)[5][12] = 517;        // set (write)
int myint = (*pointer)[5][12];  // get (read)

Note: If the array tab1 is used within a function body => this array will be placed within the call stack memory. But the stack size is limited. Using arrays bigger than the free memory stack produces a stack overflow crash.

The full snippet is online-compilable at gcc.godbolt.org

int main()
{
    //defines an array of 280 pointers (1120 or 2240 bytes)
    int  *pointer1 [280];
    static_assert( sizeof(pointer1) == 2240, "" );

    //defines a pointer (4 or 8 bytes depending on 32/64 bits platform)
    int (*pointer2)[280];      //pointer to an array of 280 integers
    int (*pointer3)[100][280]; //pointer to an 2D array of 100*280 integers  
    static_assert( sizeof(pointer2) == 8, "" );
    static_assert( sizeof(pointer3) == 8, "" );

    // Use 'typedef' (or 'using' if you use a modern C++ compiler)
    typedef int myType[100][280];
    //using myType = int[100][280];
    
    int tab1[100][280];
    
    myType *pointer;                // pointer creation
    pointer = &tab1;                // assignation
    (*pointer)[5][12] = 517;        // set (write)
    int myint = (*pointer)[5][12];  // get (read)
  
    return myint;
}
Sign up to request clarification or add additional context in comments.

5 Comments

hello, please review my first solution : i previously forgot parenthesis in pointer creation. Apologizes.
@user216993 You also wrote (pointer*) instead of (*pointer) in the second version. ;-) Ok I update my answer. Cheers
Thank you, olibre for your clear response. (and apologizes again for typos...).
"Using pointer2 or pointer3 produce the same binary" - That is not true when performing pointer-math. ++pointer2 will advance the pointer one "row". ++pointer3 will advance the pointer one entire matrix. They're different types
You are right @WhozCraig my sentence is wrong. Thanks for the feedback :) I will change it. Cheers ;)
13

Both your examples are equivalent. However, the first one is less obvious and more "hacky", while the second one clearly states your intention.

int (*pointer)[280];
pointer = tab1;

pointer points to an 1D array of 280 integers. In your assignment, you actually assign the first row of tab1. This works since you can implicitly cast arrays to pointers (to the first element).

When you are using pointer[5][12], C treats pointer as an array of arrays (pointer[5] is of type int[280]), so there is another implicit cast here (at least semantically).

In your second example, you explicitly create a pointer to a 2D array:

int (*pointer)[100][280];
pointer = &tab1;

The semantics are clearer here: *pointer is a 2D array, so you need to access it using (*pointer)[i][j].

Both solutions use the same amount of memory (1 pointer) and will most likely run equally fast. Under the hood, both pointers will even point to the same memory location (the first element of the tab1 array), and it is possible that your compiler will even generate the same code.

The first solution is "more advanced" since one needs quite a deep understanding on how arrays and pointers work in C to understand what is going on. The second one is more explicit.

1 Comment

Thanks for your response, Ferdinand. I agree with you : the second solution seems more explicit. I will continue to use this one.
10

int *pointer[280]; //Creates 280 pointers of type int.

In 32 bit os, 4 bytes for each pointer. so 4 * 280 = 1120 bytes.

int (*pointer)[100][280]; // Creates only one pointer which is used to point an array of [100][280] ints.

Here only 4 bytes.

Coming to your question, int (*pointer)[280]; and int (*pointer)[100][280]; are different though it points to same 2D array of [100][280].

Because if int (*pointer)[280]; is incremented, then it will points to next 1D array, but where as int (*pointer)[100][280]; crosses the whole 2D array and points to next byte. Accessing that byte may cause problem if that memory doen't belongs to your process.

1 Comment

hello again, please review my first solution : i previously forgot parenthesis in pointer creation. Apologizes.
1

Ok, this is actually four different question. I'll address them one by one:

are both equals for the compiler? (speed, perf...)

Yes. The pointer dereferenciation and decay from type int (*)[100][280] to int (*)[280] is always a noop to your CPU. I wouldn't put it past a bad compiler to generate bogus code anyways, but a good optimizing compiler should compile both examples to the exact same code.

is one of these solutions eating more memory than the other?

As a corollary to my first answer, no.

what is the more frequently used by developers?

Definitely the variant without the extra (*pointer) dereferenciation. For C programmers it is second nature to assume that any pointer may actually be a pointer to the first element of an array.

what is the best way, the 1st or the 2nd?

That depends on what you optimize for:

  • Idiomatic code uses variant 1. The declaration is missing the outer dimension, but all uses are exactly as a C programmer expects them to be.

  • If you want to make it explicit that you are pointing to an array, you can use variant 2. However, many seasoned C programmers will think that there's a third dimension hidden behind the innermost *. Having no array dimension there will feel weird to most programmers.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.