-1

I'm trying to pass the address of a 2D array to a function in C. I initialize the 2D array as:

const int N = 3;
char t[N][N];

I try to convert this into a char***:

char*** t_ptr = &t;

But it fails with a:

warning: initialization from incompatible pointer type

The function I want to receive the array has a prototype such as:

void f(char*** t, int N) { ... };

What am I doing wrong? Thank you.

4
  • What does the function do with t? Commented Oct 24, 2018 at 3:26
  • Why a char ***? This is not the type of a pointer to a 2d array. Commented Oct 24, 2018 at 3:33
  • I'm sure you are aware you created a 2D Variable Length Array rather than a 2D array with automatic storage type. const int N = 3; does not create a literal integer constant. For that you either need #define N 3 or a global enum { N = 3 }; Commented Oct 24, 2018 at 5:01
  • Arrays and pointers are not the exact same thing, first you should understand the differences between them and then you can enjoy with them. Commented Oct 24, 2018 at 5:31

3 Answers 3

1

This

char*** t_ptr = &t;

is wrong as compiler pointed because t is an two dimensional array, instead of triple pointer like char*** use pointer to an array to point to it. For e.g

char t[3][3] = { "ab","de","gh"}; /* total 3 1D array & each 1D array has 3 elements */
char (*t_ptr)[3] = t; /* t_ptr is a pointer to an array, pointing to 3 elements at a time */

And you can print t_ptr like

for(int index = 0; index < 3 ; index++) {
        printf("%s\n",t_ptr[index]);
}
Sign up to request clarification or add additional context in comments.

3 Comments

I am not familiar with this syntax: char (*t_ptr)[3] What does it mean @Achal ?
@rica01 this is called "pointer to an array". You can read here to know more.
this may helps.
0

For reading c declarations, you can visit: https://cdecl.org/

Now using @Achal's example array:


#include <stdio.h>
#include <stdlib.h>

int main()
{
    // t is a 2D array (not a VLA)

    char t[3][3] =
    {
        {'a', 'b', '\0'},
        {'d', 'e', '\0'},
        {'g', 'h', '\0'}
    };
    printf("t is composed of 3 arrays of 3 characters each,\n");
    printf("with the following addresses (on my machine):\n");
    printf("--------------------------------------------------\n");
    printf("%p, %p, %p\n", t[0], t[1], t[2]);


// ------------------------------------------------
    // p is an array of 3 char pointers or char*
    // Notice initialization
    char* p[3] = {t[0], t[1], t[2]};
    // following line of code will break, if no '\0' is encountered
    // since %s requires a char* and a null terminator

    printf("\nPrint strings using p:\n");
    printf("------------------------\n");
    printf("%s, %s, %s\n", *p, *(p + 1), *(p + 2));

// ------------------------------------------------
    // q is a pointer to a char* or q has type char**
    // Notice initialization of q (q points to p)

    char** q = p;
    printf("\nUsing q:\n");
    printf("-----------\n");
    printf("%s, %s, %s\n", *q, *(q + 1), *(q + 2));

// ---------------- printing characters individually
    printf("\nIndividually:\n");
    printf("---------------\n");
    for(int i = 0; i < 2; i++)
    {
        for(int j = 0; j < 2; j++)
        {
            printf("%c ",
                  *(*(q + i) + j)
                   );
        }
        printf("\n");
    }
// -----------------------------------------------
    // r is a pointer to an array of size 3 containing char
    // r advances three char at a time (r doesn't know the size of t)

    char (*r)[3] = t;   // this is the type of t
    printf("\nUsing r:\n");
    printf("---------------\n");
    printf("%p, %p, %p\n", *r, *(r + 1), *(r + 2));

    // to extract chars
    printf("%c, %c", *(*(r + 0) + 0), *(*(r + 2) + 1));
// -----------------------------------------------

    return EXIT_SUCCESS;
}

Output:

t is composed of 3 arrays of 3 characters each,
with the following addresses (on my machine):
--------------------------------------------------
000000000022FE2F, 000000000022FE32, 000000000022FE35

Print strings using p:
------------------------
ab, de, gh

Using q:
-----------
ab, de, gh

Individually:
---------------
a b
d e

Using r:
---------------
000000000022FE2F, 000000000022FE32, 000000000022FE35
a, h
Process returned 0 (0x0)   execution time : -0.000 s
Press any key to continue.

3 Comments

You shouldn't build up pointer tables like char* p[3] = {t[0], t[1], t[2]}; etc unless you have good reasons though. They are needlessly slow, as they lead to inefficient cache use.
I have learned somethng @Lundin. Thanks. I should create a new post and ask you for further elaboration. Specifically, since this is done at compile time, why would it be slow? Excuse the lack of an embedded perspective on my part.
This isn't so much for embedded systems, as for high-end mainstream CPUs with lots of data cache: x86/x64, Cortex-A, PowerPC etc. The slow part isn't the initialization but variable access through multiple pointers, where each pointer points at different segments. See Correctly allocating multi-dimensional arrays where I discuss arrays vs pointer-based look-up tables.
-2
char t[N][N];

is in fact the same as

char t[N * N];

in memory. A pointer to such an array would in both cases be of type char *.

char *** is a pointer to a pointer, that is a pointer to a char, whereas char * is a pointer to a char and that's how you pass array references in C: You pass them as a pointer to the first element of that array and this first element is a char.

C cannot retain the exact type or structure of an array as soon as you pass it to functions. In memory, a char array is just a bunch of memory filled with chars and all you can pass around is a pointer to that memory. If that memory is char [] or char [][] or even char [][][] plays no role, in memory all three are a block full of chars and the function would have to explicitly know the structure in memory, otherwise all char arrays will always be char [] for a function.

I strongly discourage C beginners to ever use multidimensional arrays. Instead of

char t[N][N];
char c = t[y1][x1];
t[y2][x2] = 'X';

use

char t[N];
char c = t[y1 * N + x1];
t[y2 * N + x2] = 'X';

As that's basically what the compiler will internally do anyway.

Note that multidimensional arrays in C are not x-y, but y-x, the first value is the row, the second on the column, please see this tutorial.

Whoever disbelieves what I just said, try out this code:

int main ( ) {
    char a[5][5];
    for (int y = 0; y < 5; y++) {
        for (int x = 0; x < 5; x++) {
            a[y][x] = x + 10 * y;
        }
    }

    for (int y = 0; y < 5; y++) {
        for (int x = 0; x < 5; x++) {
            printf("%02d ", a[y][x]);
        }
        printf("\n");
    }

    printf("------\n");

    char * a2 = (char *)a;
    for (int y = 0; y < 5; y++) {
        for (int x = 0; x < 5; x++) {
            printf("%02d ", a2[y * 5 + x]);
        }
        printf("\n");
    }
}

You can run it online, if you like, the output is identical. Also have a look at the assembly code the compiler generates for either loop (e.g. gcc -S) and you will see it's almost identical as even in the first case the compiler uses an add and a mul instruction to access the correct memory location within the array.

5 Comments

Given char t1[N * N]; and char t2[N][N];, t1 will decay to a char * in most expressions, but t2 will decay to a char (*)[N]. This would happen in a function call if t1 or t2 were given as an argument, and happens because the first element of t1[] is a char, while the first element of t2[][] is not a char, but a char[N], i.e., an array of N chars.
@DavidBowling No. char[][] is not the same as char * []. The 1st one is just a 2D array of char values and the 2nd one is an array of char pointers. And C is not C++, albeit a subset of it but with different behavior. In C you can only pass an array as pointer to the array storage.
You said in your answer that: "char [N][N] is in fact the same as char [N * N]". This is wrong. char (*)[N] is a pointer to an array of N char, which is a type in C (no idea where you got C++ from). This is relevant in function calls, e.g. with void foo(size_t n, char arr[n][n]); the function foo() will be expecting char (*)[N], not char *. In fact, the type of the argument in the declaration is modified to char (*)[N] by the compiler. I said nothing about char *[], which is an array of pointers.
Sorry, I see the "in memory" that follows. The issue is that a pointer to the char [N][N] is not a char *. There is a pointer to a 2d array: char (*)[][], or a pointer to the first element of a 2d array: char (*)[]. The second is what is expected when an array identifier decays to a pointer to its first element.
@DavidBowling tpcg.io/NO6YLS In memory both are a block of 25 bytes and accessing them with two indices is syntactical sugar.

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.