Why should we use pointer-to-arrays in C/C++?

228 views Asked by At
#include<stdio.h>

#define SIZE 3

int main()
{   
    char intArrayOne[SIZE] = {'A', 'B', 'C'};

    char (*ptrToAnOneDimArray)[SIZE] = &intArrayOne;

    int i = 0;
    for(i=0 ; i<SIZE ; i++)
    {
        printf("%c ", (*ptrToAnOneDimArray)[i]);
    }
}

Output
A B C 

When should we use "ptrToAnOneDimArray" - kinds of usages in C/C++? Please give me a real-world example.

Can we avoid these kinds of complex and fuzzy usages of pointers?

3

There are 3 answers

5
AudioBubble On BEST ANSWER

For example, when you want to implement a dynamic multidimensional array:

int (*ptrArr)[WIDTH] = malloc(sizeof ptrArr[0] * HEIGHT);

is much better than

int **ptrPtr = malloc(sizeof ptrPtr[0] * HEIGHT);
for (size_t i = 0; i < HEIGHT; i++) {
    ptrPtr[i] = malloc(sizeof ptrPtr[0][i] * WIDTH);
}

for various reasons (it does actually point to a 2D array that is contiguous in memory, it requires less allocations and frees, so it's less likely that you get it wrong, etc.)

0
Artur On

Suppose that you are embedded programmer. Now you have some piece of hardware with exchangable modules. With each module you must communicate differently ie. you init/read/write to/from it differently.

Now you have your software that must handle all of these module types. You have 3 routines (simplified here) to init, read, write each type module (HW0 is module A, HW1 is module B).

void HW0_init() { printf("HW0_init\n"); }
void HW0_read() { printf("HW0_read\n"); }
void HW0_write(){ printf("HW0_write\n"); }

void HW1_init() { printf("HW1_init\n"); }
void HW1_read() { printf("HW1_read\n"); }
void HW1_write(){ printf("HW1_write\n"); }

Now imagine you want to init your module and read sth from it so you do:

int hw_id = 1;

// want to init hardware
switch(hw_id)
{
    case 0: HW0_init(); break;
    case 1: HW1_init(); break;
    // ...
}

// now I want to read
switch(hw_id)
{
    case 0: HW0_read(); break;
    case 1: HW1_read(); break;
    // ...
}

This may be accomplished differently using pointers to arrays. If you declare arrays of pointers to your functions like so:

// as many arrays as you have modules
void (*hw0[3])() = { HW0_init, HW0_read, HW0_write };
void (*hw1[3])() = { HW1_init, HW1_read, HW1_write };

your code may be simplified to this:

enum HW_ACTION
{
    HW_INIT = 0,
    HW_READ = 1,
    HW_WRITE = 2
};

// pointer to array of pointers to funcs taking nothing
// and returning nothing
void (*(*f)[3])(void);

// detect hardware and set 'f'
f = &hw1;

(*f)[HW_INIT](); // same as HW1_init(); <=> hw1[HW_INIT]();
(*f)[HW_READ](); // same as HW1_read(); <=> hw1[HW_READ]();

Same effect - 'easier code'.

You may treat it as poor's man virtual methods for C users having no C++ compiler where you would normally create base abstract class with init, read, write methods and implement them for every kind of module.

Real life here http://en.wikipedia.org/wiki/Virtual_method_table.

6
abasterfield On

Pointers-to-pointers (hence pointers-to-arrays by proxy) are really useful. If you have a function/method and it takes a pointer-to-value argument you can change the value inside your function and that value stays in scope after you leave the function - pass-by-reference of course. However you can't change the address your pointer points at - e.g. make the pointer you passed into a NULL pointer, or point it at a different value somewhere else in memory. If you use a pointer-to-pointer-to-value then you can change the value of the 'middle' pointer inside your function. I think the MySQL C-connector library is an example of where this is used.

In your example you could pass ptrToAnOneDimArray into a function and make *ptrToAnOneDimArray be a NULL pointer or a pointer to some other data rather than intArrayOne - as intArrayOne is a fixed size by the compiler (on the stack) then you could dynamically update *ptrToAnOneDimArray from the stack to be an array malloc()'d on the heap.

#include <stdio.h>
#include <stdlib.h>
#define SIZE 3

void display(char* data) {
    int i = 0;
    for(i=0 ; i<SIZE ; i++) {
        printf("%c ", data[i]);
    }
}

void changeMyArgument(char** pointerToPointer) {

    *pointerToPointer = (char*) malloc(SIZE * sizeof(char));

    /* now we use array notation for a change */
    (*pointerToPointer)[0] = 'X';
    (*pointerToPointer)[1] = 'Y';
    (*pointerToPointer)[2] = 'Z';
}

int main() {

    /* intArrayOne is implicitly char* */
    char intArrayOne[SIZE] = {'A', 'B', 'C'};
    char* arraysArePointers = intArrayOne;   

    /* ptrToAnOneDimArray is implicitly char** */
    char** ptrToAnOneDimArray;
    ptrToAnOneDimArray = &arraysArePointers;

    display(*ptrToAnOneDimArray);

    changeMyArgument(ptrToAnOneDimArray);

    display(*ptrToAnOneDimArray);

}