Using triple+ pointers is harming both readability and maintainability.

Let's suppose you have a little function declaration here:

void fun(int***);

Hmmm. Is the argument a three-dimensional jagged array, or pointer to two-dimensional jagged array, or pointer to pointer to array (as in, function allocates an array and assigns a pointer to int within a function)

Let's compare this to:

void fun(IntMatrix*);

Surely you can use triple pointers to int to operate on matrices. But that's not what they are. The fact that they're implemented here as triple pointers is irrelevant to the user.

Complicated data structures should be encapsulated. This is one of manifest ideas of Object Oriented Programming. Even in C, you can apply this principle to some extent. Wrap the data structure in a struct (or, very common in C, using "handles", that is, pointers to incomplete type - this idiom will be explained later in the answer).

Let's suppose that you implemented the matrices as jagged arrays of double. Compared to contiguous 2D arrays, they are worse when iterating over them (as they don't belong to a single block of contiguous memory) but allow for accessing with array notation and each row can have different size.

So now the problem is you can't change representations now, as the usage of pointers is hard-wired over user code, and now you're stuck with inferior implementation.

This wouldn't be even a problem if you encapsulated it in a struct.

typedef struct Matrix_
{
    double** data;
} Matrix;

double get_element(Matrix* m, int i, int j)
{
    return m->data[i][j];
}

simply gets changed to

typedef struct Matrix_
{
    int width;
    double data[]; //C99 flexible array member
} Matrix;

double get_element(Matrix* m, int i, int j)
{
    return m->data[i*m->width+j];
}

The handle technique works like this: in the header file, you declare a incomplete struct and all the functions that work on the pointer to the struct:

// struct declaration with no body. 
struct Matrix_;
// optional: allow people to declare the matrix with Matrix* instead of struct Matrix*
typedef struct Matrix_ Matrix;

Matrix* create_matrix(int w, int h);
void destroy_matrix(Matrix* m);
double get_element(Matrix* m, int i, int j);
double set_element(Matrix* m, double value, int i, int j);

in the source file you declare the actual struct and define all the functions:

typedef struct Matrix_
{
    int width;
    double data[]; //C99 flexible array member
} Matrix;

double get_element(Matrix* m, int i, int j)
{
    return m->data[i*m->width+j];
}

/* definition of the rest of the functions */

The rest of the world doesn't know what does the struct Matrix_ contain and it doesn't know the size of it. This means users can't declare the values directly, but only by using pointer to Matrix and the create_matrix function. However, the fact that the user doesn't know the size means the user doesn't depend on it - which means we can remove or add members to struct Matrix_ at will.

Answer from milleniumbug on Stack Overflow
Top answer
1 of 5
23

Using triple+ pointers is harming both readability and maintainability.

Let's suppose you have a little function declaration here:

void fun(int***);

Hmmm. Is the argument a three-dimensional jagged array, or pointer to two-dimensional jagged array, or pointer to pointer to array (as in, function allocates an array and assigns a pointer to int within a function)

Let's compare this to:

void fun(IntMatrix*);

Surely you can use triple pointers to int to operate on matrices. But that's not what they are. The fact that they're implemented here as triple pointers is irrelevant to the user.

Complicated data structures should be encapsulated. This is one of manifest ideas of Object Oriented Programming. Even in C, you can apply this principle to some extent. Wrap the data structure in a struct (or, very common in C, using "handles", that is, pointers to incomplete type - this idiom will be explained later in the answer).

Let's suppose that you implemented the matrices as jagged arrays of double. Compared to contiguous 2D arrays, they are worse when iterating over them (as they don't belong to a single block of contiguous memory) but allow for accessing with array notation and each row can have different size.

So now the problem is you can't change representations now, as the usage of pointers is hard-wired over user code, and now you're stuck with inferior implementation.

This wouldn't be even a problem if you encapsulated it in a struct.

typedef struct Matrix_
{
    double** data;
} Matrix;

double get_element(Matrix* m, int i, int j)
{
    return m->data[i][j];
}

simply gets changed to

typedef struct Matrix_
{
    int width;
    double data[]; //C99 flexible array member
} Matrix;

double get_element(Matrix* m, int i, int j)
{
    return m->data[i*m->width+j];
}

The handle technique works like this: in the header file, you declare a incomplete struct and all the functions that work on the pointer to the struct:

// struct declaration with no body. 
struct Matrix_;
// optional: allow people to declare the matrix with Matrix* instead of struct Matrix*
typedef struct Matrix_ Matrix;

Matrix* create_matrix(int w, int h);
void destroy_matrix(Matrix* m);
double get_element(Matrix* m, int i, int j);
double set_element(Matrix* m, double value, int i, int j);

in the source file you declare the actual struct and define all the functions:

typedef struct Matrix_
{
    int width;
    double data[]; //C99 flexible array member
} Matrix;

double get_element(Matrix* m, int i, int j)
{
    return m->data[i*m->width+j];
}

/* definition of the rest of the functions */

The rest of the world doesn't know what does the struct Matrix_ contain and it doesn't know the size of it. This means users can't declare the values directly, but only by using pointer to Matrix and the create_matrix function. However, the fact that the user doesn't know the size means the user doesn't depend on it - which means we can remove or add members to struct Matrix_ at will.

2 of 5
14

Most of the time, the use of 3 levels of indirection is a symptom of bad design decisions made elsewhere in the program. Therefore it is regarded as bad practice and there are jokes about "three star programmers" where, unlike the the rating for restaurants, more stars means worse quality.

The need for 3 levels of indirection often originates from the confusion about how to properly allocate multi-dimensional arrays dynamically. This is often taught incorrectly even in programming books, partially because doing it correctly was burdensome before the C99 standard. My Q&A post Correctly allocating multi-dimensional arrays addresses that very issue and also illustrates how multiple levels of indirection will make the code increasingly hard to read and maintain.

Though as that post explains, there are some situations where a type** might make sense. A variable table of strings with variable length is such an example. And when that need for type** arises, you might soon be tempted to use type***, because you need to return your type** through a function parameter.

Most often this need arises in a situation where you are designing some manner of complex ADT. For example, lets say that we are coding a hash table, where each index is a 'chained' linked list, and each node in the linked list an array. The proper solution then is to re-design the program to use structs instead of multiple levels of indirection. The hash table, linked list and array should be distinct types, autonomous types without any awareness of each other.

So by using proper design, we will avoid the multiple stars automatically.


But as with every rule of good programming practice, there are always exceptions. It is perfectly possible to have a situation like:

  • Must implement an array of strings.
  • The number of strings is variable and may change in run-time.
  • The length of the strings is variable.

You can implement the above as an ADT, but there may also be valid reasons to keep things simple and just use a char* [n]. You then have two options to allocate this dynamically:

char* (*arr_ptr)[n] = malloc( sizeof(char*[n]) );

or

char** ptr_ptr = malloc( sizeof(char*[n]) );

The former is more formally correct, but also cumbersome. Because it has to be used as (*arr_ptr)[i] = "string";, while the alternative can be used as ptr_ptr[i] = "string";.

Now suppose we have to place the malloc call inside a function and the return type is reserved for an error code, as is custom with C APIs. The two alternatives will then look like this:

err_t alloc_arr_ptr (size_t n, char* (**arr)[n])
{
  *arr = malloc( sizeof(char*[n]) );

  return *arr == NULL ? ERR_ALLOC : OK;
}

or

err_t alloc_ptr_ptr (size_t n, char*** arr)
{
  *arr = malloc( sizeof(char*[n]) );

  return *arr == NULL ? ERR_ALLOC : OK;
}

It is quite hard to argue and say that the former is more readable, and it also comes with the cumbersome access needed by the caller. The three star alternative is actually more elegant, in this very specific case.

So it does us no good to dismiss 3 levels of indirection dogmatically. But the choice to use them must be well-informed, with an awareness that they may create ugly code and that there are other alternatives.

🌐
Quora
quora.com › Is-a-triple-pointer-possible-in-C-and-C-If-yes-what-does-it-imply
Is a triple pointer possible in C and C++? If yes, what does it imply? - Quora
Answer (1 of 4): It is possible. Using such a thing probably implies that you allowed raw speed to be your goal (or you might have made classes with public methods that would set whatever you needed set eventually, slower but much easier to maintain and think about). It implies that making rigid,...
Discussions

Is it bad to have 3 levels of pointers? If not, how much is too much?
how much is too much? The standard requires "at least 12 pointer, array, and function declarators (in any combinations)", so 13 levels is too much if you want to be standards-compliant. More on reddit.com
🌐 r/C_Programming
28
58
September 28, 2020
Example why someone should use triple-pointers in C/C++? - Stack Overflow
I'm searching for an example or explanation why someone should (or should not) use triple-pointers in C/C++. Are there any examples where triple-pointer arise? I am especially looking for source-code More on stackoverflow.com
🌐 stackoverflow.com
The purpose of a triple pointer in C - Stack Overflow
While I have seen some threads on this, I fail to understand the meaning behind triple pointers, since it seems that it's possible to do the same without them. void Reading(int *N, int ***M) { pr... More on stackoverflow.com
🌐 stackoverflow.com
Allocating memory to a triple pointer

I htink that malloc on line 10 should be cast to int* and also I am not sure where you define variable "matrix" used on lines 19 and 20, you passed x, not matrix.

Besides that, I do not know why arguemnts r and c are passed as pointers, I know it will work like that, but as you do not change their values in the function, I would just copy then without pointers. Loading data to matrix will also work with just int **x as you do not change the matrix pointer inside the function, this again is not a mistake, just an enhancement.

More on reddit.com
🌐 r/C_Programming
10
1
February 26, 2021
🌐
Tutorjoes
tutorjoes.in › c_programming_tutorial › double_triple_in_c
Navigating Multi-level Pointers in C: A comprehensive guide to Single, Double and Triple Pointers
#include<stdio.h> int main() { int a=10,*p; int **q; // Pointer to Pointer or Double Pointer int ***r; //Triple Pointer p=&a; //Address of a printf("\n Value of A : %d",a); printf("\n Address of A : %d",&a); printf("\n Value of P : %d",p); printf("\n Address of P : %d",&p); printf("\n P Dereferencing : %d",*p); printf("\n------------------------------------"); q=&p; printf("\n Value of P : %d",p); printf("\n Address of P : %d",&p); printf("\n Value of q : %d",q); printf("\n Address of q : %d",&q); printf("\n **Q Dereferencing : %d",**q); printf("\n------------------------------------"); r=&q; printf("\n Value of q : %d",q); printf("\n Address of q : %d",&q); printf("\n Value of r : %d",r); printf("\n Address of r : %d",&r); printf("\n ***r Dereferencing : %d",***r); printf("\n------------------------------------"); return 0; } To download raw file Click Here
🌐
Scaler
scaler.com › home › topics › c – pointer to pointer (double pointer)
C – Pointer to Pointer (Double Pointer) - Scaler Topics
October 10, 2023 - ... A single pointer (*ptr) points to a variable. A double pointer (**ptr) points to a single pointer, which in turn points to a variable. A triple pointer (***ptr) points to a double pointer, which points to a single pointer, eventually leading to the variable.
🌐
Quora
quora.com › What-are-some-applications-of-double-or-triple-pointers-In-the-C-language-for-example-double***-or-MyStruct**-etc
What are some applications of double or triple pointers? In the C language, for example, double*** or MyStruct**, etc. - Quora
Answer (1 of 7): Let’s define a function that finds the minimum and maximum value in an array. [code]void find_min_max(double *vals, size_t size, double **min, double **max){ if(size==0){ *min=NULL; *max=NULL; return; } double *cmin=vals[0]; double *cmax=v...
🌐
Cprogramming
cboard.cprogramming.com › c-programming › 110854-triple-pointers-***-question.html
triple pointers *** question
January 6, 2009 - That doesn't look, to me, like an address that you would be able to read a pointer from, so it will fail there. You may want to do this: ... ... ptr2 = &ptr; ptr3 = &ptr2; ... Not only will that compile without warning [something I can almost guarantee that your original code produces when you compile it], but it will also work correectly. -- Mats · Compilers can produce warnings - make the compiler programmers happy: Use them! Please don't PM me for help - and no, I don't do help over instant messengers.
Find elsewhere
Top answer
1 of 6
15

The best example that comes to mind is a sparse multi-level table. For instance one way to implement properties for Unicode characters might be:

prop_type ***proptable;
...
prop_type prop = proptable[c>>14][c>>7&0x7f][c&0x7f];

In this case proptable would need to have a triple-pointer type (and possibly quadruple pointer if the final resulting type is a pointer type). The reason for doing this as multiple levels rather than one flat table is that, at the first and second levels, multiple entries can point to the same subtable when the contents are all the same (e.g. huge CJK ranges).

Here's another example of a multi-level table that I implemented; I can't say I'm terribly proud of the design but given the constraints the code has to satisfy, it's one of the least-bad implementation choices:

http://git.musl-libc.org/cgit/musl/tree/src/aio/aio.c?id=56fbaa3bbe73f12af2bfbbcf2adb196e6f9fe264

2 of 6
6

If you need to return an array of pointers to variable length strings via a function parameter:

int array_of_strings(int *num_strings, char ***string_data)
{
    int n = 32;
    char **pointers = malloc(n * sizeof(*pointers));
    if (pointers == 0)
        return -1;  // Failure
    char line[256];
    int i;
    for (i = 0; i < n && fgets(line, sizeof(line), stdin) != 0; i++)
    {
        size_t len = strlen(line);
        if (line[len-1] == '\n')
            line[len-1] = '\0';
        pointers[i] = strdup(line);
        if (pointers[i] == 0)
        {
            // Release already allocated resources
            for (int j = 0; j < i; j++)
                free(pointers[j]);
            free(pointers);
            return -1;  // Failure
        }
    }
    *num_strings = i;
    *string_data = pointers;
    return 0;  // Success
}

Compiled code.

🌐
Stack Overflow
stackoverflow.com › questions › 52848840
The purpose of a triple pointer in C - Stack Overflow
Closed 6 years ago. While I have seen some threads on this, I fail to understand the meaning behind triple pointers, since it seems that it's possible to do the same without them. void Reading(int *N, int ***M) { printf("Input an integer N: \n"); scanf("%d", N); *M = malloc(N * sizeof(int*)); int i; for (i = 0; i < N; i++) *(*M+i) = malloc(N * sizeof(int)); printf("Input N*N integers that will form a matrix \n"); int i, j; for (i = 0; i < *N; i++) for (j = 0; j < *N; j++) scanf("%d", &((*M)[i][j])); }
🌐
C-pointers
c-pointers.com › basic_ptr › basic_char_ptr › char_tp.html
Basics of Character Triple Pointers — C Pointers
#include <stdio.h> #include <string.h> #include <stdlib.h> int main(void) { char **dp; dp = malloc(sizeof(char *)); *dp = malloc(10 * sizeof(char)); memset(*dp, 0, 10); strcpy(*dp, "Laptop123"); char ***tp; tp = &dp; printf("*dp = %s\n", *dp); for (int i = 0; i < 10; i++) { printf("(*dp)[%d] = %c\n", i, (*dp)[i]); } printf("**tp = %s\n", **tp); for (int i = 0; i < 10; i++) { printf("(**tp)[%d] = %c\n", i, (**tp)[i]); } free(*dp); // or free(**tp); free(dp); // or free(*tp); return 0; } ... *dp = Laptop123 (*dp)[0] = L (*dp)[1] = a (*dp)[2] = p (*dp)[3] = t (*dp)[4] = o (*dp)[5] = p (*dp)[6] = 1 (*dp)[7] = 2 (*dp)[8] = 3 (*dp)[9] = **tp = Laptop123 (**tp)[0] = L (**tp)[1] = a (**tp)[2] = p (**tp)[3] = t (**tp)[4] = o (**tp)[5] = p (**tp)[6] = 1 (**tp)[7] = 2 (**tp)[8] = 3 (**tp)[9] = 1 Triple Pointer, 1 Double Pointer : Double Pointer Pointing to array of single pointers : Static
🌐
Reddit
reddit.com › r/c_programming › allocating memory to a triple pointer
r/C_Programming on Reddit: Allocating memory to a triple pointer
February 26, 2021 -

I am having trouble allocating memory to triple pointers (int ***x). I know that that this means that I have an address that points to an array of arrays. Since I know the amount of rows and columns from the parameters how do I allocate memory? I tried looking at a geeksforgeeks page about dynamic allocation but I am not understanding since when I try filling out the spots I get a SIGSEGV.

Here is what I have so far: https://pastebin.com/5LSPStqW

TLDR: I am having problems understanding how to allocate memory and load data into it

Edit: updated paste bin link

🌐
C-pointers
c-pointers.com › basic_ptr › basic_struct_ptr › struct_tp.html
Basics of Structure Triple Pointers — C Pointers
1 Triple Pointer, 1 Double Pointer, 1 Single Pointer : With Single pointer heap allocation ... sp[0].a = 1; sp[0].b = 2; sp[0].c = 3; sp[1].a = 10; sp[1].b = 20; sp[1].c = 30; sp[2].a = 100; sp[2].b = 200; sp[2].c = 300; ... for (int i = 0 ; i < sizeof(arr)/sizeof(arr[0]); i++) { printf("sp[%d].a = %d\n", i, sp[i].a); printf("sp[%d].b = %d\n", i, sp[i].b); printf("sp[%d].c = %d\n", i, sp[i].c); }
🌐
Quora
quora.com › How-many-pointers-do-I-declare-while-declaring-a-triple-pointer-on-C-or-C++
How many pointers do I declare while declaring a triple pointer on C or C++? - Quora
Answer (1 of 5): Just one pointer. I think it might help to review some of the involved concepts here: * A variable is a human-readable alias for a value stored in the computer’s memory. The format that describes what the value represents and how it is stored is its “type”. * The computer ...
🌐
YouTube
youtube.com › program code
Triple Pointer ***Pointer in C C++ - YouTube
I upload a new programming video on each Monday 10AM.I make Educational (programming, Interview preparation and HR discussion) videos which help students and...
Published: May 18, 2024
Views: 75
🌐
Blogger
tufangorel.blogspot.com › 2011 › 10 › triple-pointer-operations-in-c.html
Triple Pointer Operations in C++
October 20, 2011 - Triple pointer with size of 3 and pointing to 3 different matrices: #include <QCoreApplication> #include <iostream> using namespace std; //function prototype int** initializeMatrix(int** tempMatrix, int row, int column, int matrixCellValue); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); int size = 3; int*** triplePointer; //Allocate memory for triplePointer triplePointer = new int**[size]; int rowNum = 4; int columnNum = 3; int** newMatrix = 0; //Assign elements to cell values of each matrix for(int i=0; i<size; i++) triplePointer[i] = initializeMatrix(newMatrix,rowNum,col
🌐
Tek-Tips
tek-tips.com › home › forums › software › programmers › languages
Triple Pointer in C | Tek-Tips
March 8, 2003 - I scaned this thread quickly and did not see anyone pointing out that one possibility of that argument is that it represents the &quot;address&quot; of a variable of type &quot;char**&quot;, in which case it is an &quot;output&quot; parameter that is allocated and initialized by the function and the &quot;int&quot; return value is the number of char*'s allocated. -pete ... // reserve mem for 10 pointers char** argv = (char**)malloc( 10 * sizeof( char* ) ); // get the pointer to this mem block char*** argvp = &argv; [\code] i asume that int makeargv(char *s, char *delimiters, char ***argvp) expects 's' to be a string containing tokens which are separated by 'delimeters'.
🌐
BitDegree
bitdegree.org › courses › course › pointer-in-c-programming
Pointer in C Programming: Complete C Pointers Tutorial
This pattern is one of the possibilities of how to use pointers in C. Additionally, you can consider triple pointers. The latter concept means that you direct one pointer to a different pointer, and that pointer points to a different pointer. Sounds ...
Published: June 8, 2019
🌐
Yoonzh
yoonzh.com › posts › c-cpp › pointers-double-triple.html
Double and Triple pointers in C - yoonzh.com
February 16, 2025 - Single pointer: int *p; → stores the address of an int · Double pointer: int **p; → stores the address of a pointer that stores an int · Triple pointer: int ***p; → stores the address of a pointer that stores the address of a pointer that stores an int
🌐
Reddit
reddit.com › r/c_programming › passing array to triple pointer?
r/C_Programming on Reddit: Passing array to triple pointer?
September 4, 2021 -

So I've got an assignment where I need to pass a 2d array to a function that asks for a triple pointer.

void getData(int *** dataTable, int * value){}

When I'm calling this function I used my array name/its address and tried everything but it always gives me a compile error.

Let's say I have a 2d array called myArray[5][10]. All I need to do is to pass this array to the dataTable parameter.

My current process is this:

int myArray[5][10];
int val = 5;
int* p = &val;
getData(myArray, p)

It isn't working though. So can someone please tell me how to do this?

Any help is appreciated!