0

I'm programming in C; I have a program I'm trying to write and I want to get input of 2 numbers from the user and use those numbers to set the dimensions of a 2d array.

For context: I'm trying to create a small drawing app and I want the user to set the size of the board though I can't seem to find an answer as to if this is possible and if it is how is it done.

#include<stdio.h>

int main() {
    int x, y;
    char sign[1];
    char board = 'O';
    int count2;
    int pos;
    // sets number of rows

    printf("enter the number of columns:\n ");
    scanf_s("%d", &x);
    // sets number of columns
    printf("enter the number of rows: \n");
    scanf_s("%d", &y);
    //sets the sign
    printf("enter the sign you want to draw with: \n");
    scanf_s("%s", &sign, 1);

// this is to visualize the board size for the user
    for (int count = 0; count != x; count++) {
        printf("%c", board);
        for (int count2 = 0; count2 != y; count2++)
            printf("%c", board);
        printf("\n");
    }

    pos[x][y] = {
        {}
    };
}

I realize why this wouldn't work but 2d arrays are a new concept to me and I can't seem to get it to work.

The error I get is

Expression must have pointer-to-object type but it has type int

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Punz
  • 15
  • 5
  • 1
    C have [variable-length arrays](https://en.wikipedia.org/wiki/Variable-length_array). `int pos[x][y];` is a valid definition. Such arrays can't be initialized at definition though, you have to initialize it after definition. It's important that the sizes of the arrays are known before you define the array. – Some programmer dude Oct 04 '21 at 13:40
  • You need to declare the array _after_ you know the dimensions. See [How to declare variable-length arrays correctly?](https://software.codidact.com/posts/283440) – Lundin Oct 04 '21 at 13:41
  • Also, C doesn't really have "2d" arrays. What is has are arrays *of arrays*. So if you know how "1d" arrays work, then you can use that exact same knowledge to work with "2d" arrays. – Some programmer dude Oct 04 '21 at 13:41
  • On another and unrelated note, why use `%s` format to read a single character into `sign[0]`? Why not use a plain single `char` variable (`char sign;`) and read a single characters into it (`scanf(" %c", &sign);`)? – Some programmer dude Oct 04 '21 at 13:43
  • Also the `%s` format for `scanf` expects an argument of type `char *`. The expression `&sign` (in your shown code) have the type `char (*)[1]`. Mismatching format specifier and argument type leads to *undefined behavior*. You want `&sign[0]`, which is what plain `sign` itself will decay to. – Some programmer dude Oct 04 '21 at 13:45
  • thanks for the tips im practicing my coding since im pretty new and i appreciate it – Punz Oct 04 '21 at 14:06

5 Answers5

1

Assuming that you are using integers, you should replace lines:

pos[x][y] = {
    {}
};

with:

int pos[x][y];

This declares the array to be of type integer with the size that you are looking for.

Since this is a variable-size array, it must be initialized after the declaration:

for(unsigned int idx1 = 0; x > idx1; idx1++)
{
    for(unsigned int idx2 = 0; y > idx2; idx2++)
    {
        pos[idx1][idx2] = 0;
    }
}
Jonathon S.
  • 1,928
  • 1
  • 12
  • 18
  • This isn't valid C. You can't initialize VLA. – Lundin Oct 04 '21 at 14:05
  • @Lundin You are right. Forgot about that. Answer is corrected. – Jonathon S. Oct 04 '21 at 14:10
  • i get: cannot allocate an array of constant size 0. and: pos missing subscript – Punz Oct 04 '21 at 14:12
  • @Lundin In this case `x` is the length and the looping will take place until the index `idx1` is equal to `x`, so I believe that this should work correctly. – Jonathon S. Oct 04 '21 at 14:17
  • @Punz What compiler are you using? Sounds like something a dinosaur compiler from the previous millennium (or a C++ compiler) would say. – Lundin Oct 04 '21 at 14:17
  • i guess im using cl since im coding on vs – Punz Oct 04 '21 at 14:18
  • 2
    @Punz Then that's your problem. That C compiler is from 1990. You need to use a C language standard compiler. – Lundin Oct 04 '21 at 14:19
  • @Punz - I modified the answer. Please try again without the `{{0}}`. If you are trying to initialize the array as I originally noted you may get the message `cannot allocate an array of constant size 0`. Try again either using `memset` or the `for` loop noted. – Jonathon S. Oct 04 '21 at 14:19
  • do you have any recommendations for a good compiler? – Punz Oct 04 '21 at 14:20
  • @Punz gcc is a good compiler. In Windows, that would be gcc/mingw64. You can download the Codeblocks IDE and it comes with that one pre-installed, I believe. – Lundin Oct 04 '21 at 14:23
  • @Punz Your compiler should support ANSI C99. I am not sure whether it does or not. If you need a new compiler, I would install GCC. Before you go that far, I would try once without the `{{0}}`. – Jonathon S. Oct 04 '21 at 14:23
1

to set the size of a 2d array

Variable Length Array

Available in C99 and optionally afterwords.

// Validate array dimensions
// Some reasonable threshold
#define BIG 1000

if (x <= 0 || y <= 0 || x <= BIG/y) {
  Handle_Error();
}

int pos[x][y];
memset(pos, 0, sizeof pos);  // zero out if desired.

// Use pos

// No clean-up required

Allocate

Alternatively, allocate memory for an array pointers, each pointing to an allocated array of int.

// Validate dimensions (or consider using type `size_t`)
if (x < 0 || y < 0) {
  Handle_Error();
}

int **pos = calloc(x, sizeof *pos);
if (pow == NULL) {
  Handle_OutOfMemory();
}
for (int xi = 0; xi < x; xi++) {
  pos[xi] = calloc(y, sizeof pos[0][0]);
  if (pos[xi] == NULL) {
    Handle_OutOfMemory();
  }
}

// Use pos

// Free when done
if (pos) {
  for (int xi = 0; xi < x; xi++) {
    free(pos[xi]);
  }
  free(pos);
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

Example use of VLA array and array pointers


void *allocate(size_t rows, size_t cols)
{
    int (*arr)[cols] = malloc(rows *sizeof(*arr));
    //or if you want to initialize the array to zeroes
//    int (*arr)[cols] = calloc(rows, sizeof(*arr));
    return arr;
}

void printarray(size_t rows, size_t cols, int (*arr)[cols])
{
    for(size_t row = 0; row < rows; row++)
    {
        for(size_t col = 0; col < cols; col++)
        {
            printf("%4d ", arr[row][col]);
        }
        printf("\n");
    }
}

void initarray(size_t rows, size_t cols, int (*arr)[cols])
{
    for(size_t row = 0; row < rows; row++)
    {
        for(size_t col = 0; col < cols; col++)
        {
            arr[row][col] = 100*row + col;
        }
    }    
}

int main(void)
{
    size_t rows,cols;

    if(scanf("%zu %zu", &rows, &cols) == 2)
    {
        int (*arr)[cols] = allocate(rows, cols);
        if(arr)
        {
            initarray(rows,cols,arr);
            printarray(rows, cols, arr);
        }
        else
        {
            /*handle allocation error*/
        }
    }
    else
    {

        /*handle scanf error*/
    }
}

https://godbolt.org/z/rnWafcd9c

0___________
  • 60,014
  • 4
  • 34
  • 74
1

In a standards compliant environment, you can define a variable-length array (VLA) by using non-constant expressions in the variable definition. This expression must evaluate to a value greater than zero.

VLAs cannot be initialized in their definitions, and must be initialized "by hand" after they are defined.

memset can be used to quickly fill a number of bytes with some constant byte. This can be useful for initializing all bytes to zero, which for integers is guaranteed to represent zero1.

Otherwise, use basic loops to initialize.

Because VLAs exist on the stack, care must be taken to ensure they do no exceed the maximum stack size on your platform.

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

void die(const char *msg) {
    fprintf(stderr, "%s\n", msg);
    exit(EXIT_FAILURE);
}

int valid(size_t n) {
    /* arbitrary upper bounds, platform dependent */
    return n > 0 && n < 512;
}

int main(void) {
    size_t x, y;

    printf("Enter X dimension: ");
    if (1 != scanf("%zu", &x))
        die("Could not read X dimension.");
    printf("Enter Y dimension: ");
    if (1 != scanf("%zu", &y))
        die("Could not read Y dimension");

    if (!valid(x) || !valid(y))
        die("Invalid dimensions.");

    int data[x][y];
    printf("[data] size in bytes is: %zu\n", sizeof data);

    /* All bytes zero */
    memset(data, 0, sizeof data);

    /* Or, a particular value */
    for (size_t i = 0; i < x; i++)
        for (size_t j = 0; j < y; j++)
            data[i][j] = 42;
}

Some additional notes:

  • Formally, the proper type for dealing with memory sizes is size_t. Its format specifier is "%zu".

  • The return value of scanf should always be checked to ensure the expected number of conversions took place.

  • The format string " %c" (note the leading space) can be used to read a single character while ignoring leading white space. This is useful because previous calls to scanf may leave characters such as the newline (\n) in the input buffer.


From various comments we can see one of your issues is that you are using the MSVC C compiler. This is an ancient, non-standards compliant compiler that has very poor support for C99 features, including VLAs.

In this case, you may want to use dynamic memory allocation to create your array. You'll need similar validation, but generally speaking heap memory is more vast.

Every call to malloc needs a mirrored called to free when you are done using the memory, and care must be taken in cleaning up memory in the event of a partial failure.

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

void die(const char *msg) {
    fprintf(stderr, "%s\n", msg);
    exit(EXIT_FAILURE);
}

int valid(size_t n) {
    return n > 0 && n < 2048;
}

int main(void) {
    size_t x, y;

    printf("Enter X dimension: ");
    if (1 != scanf("%zu", &x))
        die("Could not read X dimension.");
    printf("Enter Y dimension: ");
    if (1 != scanf("%zu", &y))
        die("Could not read Y dimension");

    if (!valid(x) || !valid(y))
        die("Invalid dimensions.");

    int **data = malloc(sizeof *data * x);

    if (data == NULL)
        die("Could not allocate memory.");

    for (size_t i = 0; i < x; i++) {
        if (!(data[i] = malloc(sizeof *data[i] * y))) {
            for (size_t j = 0; j < i; j++)
                free(data[j]);

            free(data);
            die("Failed to allocate memory.");
        }
    }

    printf("[data] size in bytes is: %zu\n", sizeof (int) * x * y);

    for (size_t i = 0; i < x; i++)
        free(data[i]);

    free(data);
}

1 All bits zero is not a guaranteed representation of zero for all types, e.g., floating point numbers.

Oka
  • 23,367
  • 6
  • 42
  • 53
0

If you consider using dynamic memory allocation with malloc:

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

void delete2DimArr(int** pos, int x) {
    for (int i = 0; i < x; i++) {
        free(pos[i]);
    }
    free(pos);
}

int** create2DimArr(int x, int y) {
    int** pos = malloc(x * sizeof(int*));
    int fail_idx = -1;
    if (pos) {
        for (int i = 0; i < x; i++) {
            pos[i] = malloc(y * sizeof(int));
            // if you need to initial the value
            if (pos[i]) {
                memset(pos[i], 0, y * sizeof(int));
            } else {
                fail_idx = i;
                break;
            }
        }
    }

    if (fail_idx != -1) {
        delete2DimArr(pos, fail_idx);
        pos = NULL;
    }
    if (!pos) {
        printf("create2DimArr malloc failed!");
    }

    return pos;
}

int main() {
    // test dimensions
    int x = 5, y = 7;

    // code continue here
    // this is to visualize the board size for the user
    for (int count = 0; count < x; count++) {
        for (int count2 = 0; count2 < y; count2++) {
            printf("%c", '0');
        }
        printf("\n");
    }

    int** pos = create2DimArr(x, y);
    printf("Show pos:\n");
    if (pos) {
        for (int count = 0; count < x; count++) {
            for (int count2 = 0; count2 < y; count2++) {
                printf("%d", pos[count][count2]);
            }
            printf("\n");
        }
    }

    delete2DimArr(pos, x);
}

With an online validation.

rustyhu
  • 1,912
  • 19
  • 28
  • Curious `if (pos[i]) {` used to detect out-of-memory. yet code lacks prior `if (pos) {`. – chux - Reinstate Monica Oct 04 '21 at 14:54
  • When allocating memory like this, each call to `malloc` should be verified before going on. If a failed call occurs, break out of the loop, free any memory that _has_ been allocated in the loop and log the failure. – ryyker Oct 04 '21 at 14:55
  • I think this code could be improved upon. I don't think `malloc` should be called more than once – Tim Randall Oct 04 '21 at 15:05
  • @TimRandall Do you mean to `malloc` only once for `x * y * sizeof(int)`? That's simpler, but I think the questioner prefer the form `pos[i][j]`. – rustyhu Oct 04 '21 at 15:31
  • it is not 2D array only array of pointers. They are completely different. – 0___________ Oct 04 '21 at 16:00
  • 1
    There are several ways to emulate a 2D array (rows, columns) using a pointer and a single call to `[m][c]alloc()`. I favor (and have used) [this one](https://stackoverflow.com/a/36799070/645128) recently. – ryyker Oct 04 '21 at 17:56