0
#include <stdio.h>

void func(int **arr, int s) {
    for (int i = 0; i < s; i++) {
        for (int j = 0; j < s; j++) {
            printf("%d\n", arr[i][j]);
        }
    }
    printf("\n");
}

int main() {
    int arr[3][3] = {
        {11, 12, 13},
        {21, 22, 23},
        {31, 32, 33},
    };
    func((int**)arr, 3);
}

In this code I'm trying to pass a 2D array to another function and have the second function print out all it's values. I've chosen to specify the array as a int ** in the signature of the function (loosely following the second method specified in this example).

When I run this code I expect it to be able to print out all the items, but instead it seg faults and I'm not sure why. I've tried debugging it, but gdb points me to the printf's array access in the func function, but I already suspected that's the cause. There is obviously a discrepency between what I think the memory looks like and how i'm trying to access it and how it's actaully being accessed (or what's being accessed).

francium
  • 2,384
  • 3
  • 20
  • 29
  • 2
    Why are you casting the argument to `func`? You're passing in the incorrect type. Casting can't fix that. – tadman Feb 20 '19 at 01:13
  • @tadman I'm compiling with Wall and I get `-Wincompatible-pointer-types` otherwise. `note: expected 'int **' but argument is of type 'int (*)[3]'` – francium Feb 20 '19 at 01:16
  • 1
    Yes, that's what you've defined. An array of `int[3]`, not an array of `int*`. This is a huge difference. One is just three elements, the other is a pointer. – tadman Feb 20 '19 at 01:16
  • @tadman Is my approach wrong then? I can't remove the 3's - it complains. Should I use malloc here? – francium Feb 20 '19 at 01:17
  • A pointer to a pointer to `int` is not a 2d array. – Swordfish Feb 20 '19 at 01:18
  • 1
    `int[3][3]` and `int**` are not inter-compatible. You might want to switch this to a single `3*3`-sized array as honestly multidimensional arrays in C are an utter nightmare, not only from a code perspective but also from how they require *N+1* pointers instead of 1 and can scatter your data all over the heap. – tadman Feb 20 '19 at 01:20
  • @Swordfish What would be the correct approach based on what I'm trying to do? My underlying question, that I've omitted for the sake of simplicity here, has to go with passing a 2D array via a struct to another thread and then accessing elements on it. I figured if I can figure out how to access things on an `int **` I would solve that problem too. – francium Feb 20 '19 at 01:20
  • @tadman Good to know. I'm lacking the experience in C to know the best approach and figured pointer to a pointer was the best approach. I'll switch to a single 3*3 array. Thanks – francium Feb 20 '19 at 01:22
  • 1
    By comparison C++ has far better support for this sort of thing, but C, being much more low-level, expects you to to organize things a certain way or deal with the complexity yourself. `int[3][3]` has a memory layout similar to `int[3*3]`, there's no intermediate pointers. It's not equivalent to `int**`. – tadman Feb 20 '19 at 01:23
  • @tadman *but also from how they require N+1 pointers instead of 1 and can scatter your data all over the heap.* – what you're describing isn't a 2d-array either. – Swordfish Feb 20 '19 at 01:24
  • @tadman i'd call a pointer to an array of pointers to array a jagged array. – Swordfish Feb 20 '19 at 01:25
  • @Swordfish Like everything in C it's got a lot of sharp edges. – tadman Feb 20 '19 at 01:25
  • 2
    @tadman *but an array of arrays* – an array of arrays is a 2d-array (`T foo[COLUMNS][ROWS]`) but a pointer to one or more pointers to `T` is something different. Sorry for being that picky but i can't stand it when people mix those things up. – Swordfish Feb 20 '19 at 01:30
  • @Swordfish That's a much better explanation. – tadman Feb 20 '19 at 01:30
  • @Swordfish so if I understand you correctly. You're saying [[0, 1], [2, 3]] is a 2d array, but [0x1230, 0x1238], where 0x1230 contains [0, 1] and same for 0x1238, is something different. Is my understanding of this correct? The big difference being 2d array is a contiguous block of memory, but a pointer to a pointer of T could be anywhere in memory. – francium Feb 20 '19 at 01:33
  • @tadman multidimensional arrays do not require `N+1` pointers and also allocate the data contiguously. I get what you mean, but just not the right term. – Ajay Brahmakshatriya Feb 20 '19 at 01:38
  • 2
    `func(int **arr, int s)` --> `func(int arr[][3], int s)` – chux - Reinstate Monica Feb 20 '19 at 01:40
  • 1
    @chux One chux for the rescue ;) one more thing to say: `func(int arr[][3], int s)` is exactly the same as `func(int *arr[3], int s)`. – Swordfish Feb 20 '19 at 01:40
  • 1
    Might be helpful: [How to Read C Declarations](https://parrt.cs.usfca.edu/doc/how-to-read-C-declarations.html) and [cdecl: C gibberish ↔ English](https://cdecl.org/) – Swordfish Feb 20 '19 at 01:45
  • Try `void func(int s, int arr[s][s]) {` (C99) – chux - Reinstate Monica Feb 20 '19 at 01:48
  • 1
    @chux Thanks for the suggestion, but I did use that but it doesn't fit in my real world situation. my real situation was on in which I'm passing a 2d array to a thread via a struct. I didn't know how to pass an actual arr[s][s] type via a struct or how to case a (int **) back into a arr[s][s] without the compiler complaining, so I figured a (int **) was my best bet. I've followed the advice given here and used a 1d array of size s*s and that has worked perfectly for my situation. – francium Feb 20 '19 at 01:50
  • "I didn't know how to pass an actual `arr[s][s]` type" --> My suggest was not to _pass_ a `arr[s][s]`, but to use `arr[s][s]` as the receiving parameter. Just call as `func(3, arr)`. Better, post a more complete example of what your need. – chux - Reinstate Monica Feb 20 '19 at 01:54
  • You can define the array as a field of your struct. this is a live test of this approach: https://https://segfault.stensal.com/a/KTH9up3OKUzNsHpd – stensal Feb 20 '19 at 02:03

0 Answers0