0

Just like the title says. Is there anyway to allow the user to specify both parameters of a 2 dimensional array in C? The array will be called to a function and changed within the function.

Functions require at least the second parameter for the size of an array, and I don't want to specify an arbitrary large number for the size of the array.

I should point out that I am a very new C programmer, and I know little about C in general. Don't be offended if you expose me to an entirely new concept and I ask a ton of questions about it!

ViperZer0
  • 33
  • 5

3 Answers3

1

Yes, since C99 you can write:

void func(int rows, int cols, int array[rows][cols])
{
// stuff...
}

and call it:

int main()
{
    int c = rand() % 10 + 1;
    int arr1[6][c];  func(6, c, arr1);
    int arr2[9][3]; func(9, 3, arr2);

    size_t large = 100000000;
    int (*arr3)[c] = malloc(sizeof(int[large][c]));
    func(large, c, arr3);
    free(arr3);
}

In C11 this feature was marked "optional" for political reasons, however all compilers except MSVC support it.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Probably best to use `size_t` as the array dimensions, but people seem to like to use `int` in examples – M.M Sep 16 '15 at 04:16
  • The use of `int` rather than `size_t` could be based on the examples from the standard, which all use `int` rather than `size_t`. – Jonathan Leffler Sep 16 '15 at 04:18
  • @JonathanLeffler yeah, I included the standard writers in "people" :) – M.M Sep 16 '15 at 04:18
  • But, with regards to good practice in C, the standard writers are not 'mere people'; they are rather privileged (or do I mean exalted), or at least authoritative in a way that plebs like me and you are not. – Jonathan Leffler Sep 16 '15 at 04:19
  • All right, this seems to be what I need. How can I tell if I have C99? I have Code::Blocks 13.12 – ViperZer0 Sep 16 '15 at 04:32
  • @ViperZer0: One way is to try compiling it — a C89/C90 compiler will reject it. However, until GCC version 5.1.0, the default compilation mode for GCC was `-std=gnu90` — it is now `-std=gnu11`. You might need to enable it (`-std=c99` or `-std=gnu99`, or `-std=c11`, etc). You can look to see what compiler your IDE is using — I don't know how Code::Blocks chooses which compiler to use. – Jonathan Leffler Sep 16 '15 at 04:46
  • In the C::B compiler options you can specify which standard to use, pick C11 or GNU11. However gcc has allowed its own version of VLAs for a long time before C99 so the code might work anyway. – M.M Sep 16 '15 at 04:48
  • @M.M Thanks! This will work just fine for what I'm doing. – ViperZer0 Sep 16 '15 at 16:48
1

This demonstrates VLAs (variable length arrays) at work. It is based on code for SO 32565694, which asked about VLAs in structures. This code avoids using structures.

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

typedef struct
{
    int nr, nc;
    void *data;     // Actually double a[nr][nc]
} Matrix;

static double variant1(int nr, int nc, int r, int c)
{
    assert(nr != 0);
    return (r * nc) + c + 1;
}

static double variant2(int nr, int nc, int r, int c)
{
    return ((nr - r) * nc) + (nc - c) + 1;
}

typedef double (*Initializer)(int nr, int nc, int r, int c);

static void mat_init(int nr, int nc, double m[nr][nc], Initializer init)
{
    assert(m != 0 && nr > 0 && nc > 0);
    printf("Set: [%dx%d]\n", nr, nc);
    for (int i = 0; i < nr; i++)
    {
        printf("[%d]:", i);
        for (int j = 0; j < nc; j++)
        {
            double v = init(nr, nc, i, j);
            m[i][j] = v;
            printf(" %6.1f", v);
        }
        putchar('\n');
    }
}

static void mat_dump(const char *tag, int nr, int nc, double m[nr][nc])
{
    assert(m != 0 && nr > 0 && nc > 0);
    printf("Matrix %s: %dx%d\n", tag, nr, nc);
    for (int i = 0; i < nr; i++)
    {
        printf("[%d]:", i);
        for (int j = 0; j < nc; j++)
            printf(" %6.1f", m[i][j]);
        putchar('\n');
    }
}

static void mat_multiply(int r1, int c1, double m1[r1][c1],
                         int r2, int c2, double m2[r2][c2],
                         int r3, int c3, double m3[r3][c3])
{
    assert(r1 > 0 && c1 > 0 && r2 > 0 && c2 > 0 && r3 > 0 && c3 > 0);
    printf("m1[%d][%d] x m2[%d][%d] = m3[%d][%d]\n", r1, c1, r2, c2, r3, c3);
    assert(r1 == r3 && c2 == c3 && c1 == r2);

    for (int i = 0; i < r1; i++)
    {
        for (int j = 0; j < c2; j++)
        {
            double sum = 0.0;
            for (int k = 0; k < c1; k++)
                sum += m1[i][k] * m2[k][j];
            m3[i][j] = sum;
        }
    }
}

int main(void)
{
    int r1 = 3;
    int c1 = 5;
    int r2 = c1;
    int c2 = 4;
    int r3 = r1;
    int c3 = c2;

    double m1[r1][c1];
    double m2[r2][c2];
    double m3[r3][c3];

    printf("m1:\n");
    mat_init(r1, c1, m1, variant1);
    printf("m2:\n");
    mat_init(r2, c2, m2, variant2);

    mat_dump("m1", r1, c1, m1);
    mat_dump("m2", r2, c2, m2);
    mat_multiply(r1, c1, m1, r2, c2, m2, r3, c3, m3);
    mat_dump("m3", r3, c3, m3);

    return 0;
}

Example output:

m1:
Set: [3x5]
[0]:    1.0    2.0    3.0    4.0    5.0
[1]:    6.0    7.0    8.0    9.0   10.0
[2]:   11.0   12.0   13.0   14.0   15.0
m2:
Set: [5x4]
[0]:   25.0   24.0   23.0   22.0
[1]:   21.0   20.0   19.0   18.0
[2]:   17.0   16.0   15.0   14.0
[3]:   13.0   12.0   11.0   10.0
[4]:    9.0    8.0    7.0    6.0
Matrix m1: 3x5
[0]:    1.0    2.0    3.0    4.0    5.0
[1]:    6.0    7.0    8.0    9.0   10.0
[2]:   11.0   12.0   13.0   14.0   15.0
Matrix m2: 5x4
[0]:   25.0   24.0   23.0   22.0
[1]:   21.0   20.0   19.0   18.0
[2]:   17.0   16.0   15.0   14.0
[3]:   13.0   12.0   11.0   10.0
[4]:    9.0    8.0    7.0    6.0
m1[3][5] x m2[5][4] = m3[3][4]
Matrix m3: 3x4
[0]:  215.0  200.0  185.0  170.0
[1]:  640.0  600.0  560.0  520.0
[2]: 1065.0 1000.0  935.0  870.0

It would be perfectly feasible to use command line arguments or user input to set the values of r1, c1 (or r2) and c2 (but the values in r2 (or c1), r3 and c3 are constrained, for this code, by r1, c1 and c2). The initialization code would work fine, and so would the multiplication.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

When declaring array the size should be known by compile time. In order to do what you ask you should use dynamic allocation, like malloc.

You can read an int from user, and then call malloc with that size for a multidimensional array

CIsForCookies
  • 12,097
  • 11
  • 59
  • 124