1

I have a set of codes that look like this:

#define BLOCKS 20
#define SEG 5

int memory[BLOCKS][SEG];

int main() {

    FILE* stream = fopen("file.csv", "r");

    printf("Please enter a number:");
    int input = scanf("%d", &input);

If I try to change the SEG away from being #DEFINE and simply int SEG = 0; and try to let the int input = scanf("%d", &input); become SEG, Visual studio tells me that my SEG is now an undeclared identifier and expression for

int memory[BLOCKS][SEG];

must have a constant value.

My question is, how can I make this SEG value be determined by a user input? Eg. if user enters 10, my variable SEG will become 10 and the program will continue running.

I have tried the following link but it did not work for me. Array definition - Expression must have a constant value

I would appreciate help. Thanks!

Gaurav Sehgal
  • 7,422
  • 2
  • 18
  • 34
UBUNTU_new
  • 43
  • 1
  • 8
  • 3
    Why do you expect `int input = scanf("%d", &input);` to work, when you Access `input` before declaring it??? – goodvibration Mar 08 '19 at 17:54
  • @PSkocik: This dude says: "If I try to change the SEG away from being #DEFINE and simply `int SEG = 0`", which means that he's not trying to change a compile-time constant (all you needed to do is to read a little beyond the opening statement). – goodvibration Mar 08 '19 at 17:55
  • *Where* are you declaring `int SEG = 0;`? Please show the code that doesn't work, not code that does work – UnholySheep Mar 08 '19 at 17:58
  • Looks like the real question here is about dynamic allocation or VLAs. – Eugene Sh. Mar 08 '19 at 17:58

4 Answers4

0

#define is a preprocessor directive. You cannot change it based on user input in one program. You can try to use malloc to make the array!

Try

int input, i;

printf("Please enter a number:");
scanf("%d", &input);

int** memory = malloc(sizeof(int*) * BLOCKS);
for(i = 0; i < BLOCKS; i++)
    memory[i] = malloc(sizeof(int) * input);

Access it like you would your original 2D array. memory[row][col];

Mini
  • 445
  • 5
  • 17
  • The question says: "If I try to change the SEG away from being #DEFINE and simply `int SEG = 0`", which means that this dude is **not** trying to change a preprocessor directive (you could have figured that out if you had just bothered to read a little beyond the opening statement). – goodvibration Mar 08 '19 at 17:57
  • @goodvibration but the point Mini is making is to use malloc as the way to not use a preprocessor directive. – Marlin Pierce Mar 08 '19 at 18:00
  • Please add reading user input for the sizes to make this a better answer. – stark Mar 08 '19 at 18:04
  • @Mini it would be better to do just one memory allocation instead of one for each block. – Marlin Pierce Mar 08 '19 at 18:07
  • @MarlinPierce You are correct but this way allows for access like [i][j] which is what I believe OP wanted – Mini Mar 08 '19 at 18:09
  • @Mini I think there is a way to declare an int array point type, which would allow [][] addressing. I just didn't have time to look up the details. – Marlin Pierce Mar 08 '19 at 18:12
0

A #define preprocessor directive's value must be known at compile time, and cannot wait for runtime. Likewise, declaring an array is usually something to be known at compile time.

What you need to do is malloc some memory. (Remember to free it when you are done.) Then address the memory as a two dimensional array.

int *memory = (int *)malloc(BLOCKS * input * sizeof(int))

You can address block 2, seg 3 as

memory[ 3 * BLOCKS + 2]

or

memory[ seg * BLOCKS + block]

Alternatively, if you want to use the two dimensional array notation, and you are not fussy about what order to have the dimensions in, you can declare a pointer to an array,

typedef int (*memseg_t)[BLOCKS];
memseg_t memory = (memseg_t)malloc(BLOCKS * input * sizeof(int));

referenced by:

memory[seg][block]
Marlin Pierce
  • 9,931
  • 4
  • 30
  • 52
0

For C89 and earlier, the array dimensions in an array declaration must be constant expressions, meaning they must be evaluable at compile time (numeric constants, sizeof expressions, expressions involving numeric constants and/or sizeof expressions, or macros that expand to any of the previous).

C99 introduced "variable-length arrays", where array dimensions determined using runtime expressions (which places some restrictions on where VLAs may be used):

int blocks = some_value();
int seg = some_other_value();

int memory[blocks][segs];

Unfortunately, Microsoft's Visual Studio implementation does not support variable-length arrays (or much else beyond C89).

So, you have a choice - you can either use a different compiler that supports C99 or later (such as MinGW), or you will need to use dynamic memory allocation. If you want to keep BLOCKS constant but SEG variable, you would need to do something like this:

int *memory[BLOCKS];
int seg;
...
scanf( "%d", &seg );
...
for ( int i = 0; i < BLOCKS; i++ )
{
  memory[i] = malloc( sizeof *memory[i] * seg );
  if ( !memory[i] )
    // handle memory allocation failure
}

When you're done, you'll need to free that memory:

for ( int i = 0; i < BLOCKS; i++ )
  free( memory[i] );

The main drawback to this method is that the rows of the array won't be contiguous in memory - there will be a gap between memory[i][seg-1] and memory[i+1][0]. If that matters, you may have to allocate memory as a single block, then fake 2D indexing:

int *memory = malloc( sizeof *memory * BLOCKS * seg );
if ( !memory )
  // handle memory allocation failure
...
memory[i * BLOCKS + j] = some_value();

EDIT

Here’s an (untested!) example based on the snippet in your question - you’re trying to read a .csv file with a fixed number of rows (BLOCKS) and a variable number of columns (SEG):

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

#define BLOCKS 20

int *memory[BLOCKS];

int main( void )
{
  int seg;
  FILE *stream = fopen( “file.csv”, “r” );
  if ( !stream )
    // print error and exit

  printf( “Please enter a number: “);
  if ( scanf( “%d”, &seg ) != 1 )
    // print error and exit

  for ( size_t b = 0; b < BLOCKS; b++ )
  {
    /**
     * Allocate memory for a block (row)
     */
    memory[b] = malloc( sizeof *b * seg );
    if ( !memory[b] )
      // print error and exit

    /**
     * Read that row from the file - since it has 
     * a .csv extension, I am assuming it is
     * comma-delimited.  Note that malloc is not
     * required to initialize memory to any specific
     * value - the initial value of each memory[b][s]
     * is indeterminate.
     */
    for ( size_t s; s < seg; s++ )
      if ( fscanf( stream, “%d%*c”, &memory[b][s] )) != 1 )
        // print error and exit
  }
  fclose( stream );

  /**
   * Do stuff with memory here
   */

  /**
   * After you’re done with memory, free it
   */
  for ( size_t b = 0; b < BLOCKS; b++ )
    free( memory[b] );

  return EXIT_SUCCESS;
}
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • Hi, I tried this and my memory became -10923481948. Does this mean there is a stack overflow? – UBUNTU_new Mar 09 '19 at 17:47
  • @Python_new: What do you mean, your memory “became” that value? What exactly did you try? How did you check that value? – John Bode Mar 09 '19 at 18:51
  • I tried to print it out using this way for (b = 0; b < BLOCKS; b++) { for (s = 0; s < SEG; s++) { printf("%5d | %5d | %9d\n", c, b, memory[b][s]); c += 1; } and also where do I free the memory? I tried to free it outside of this for loop but it too doesnt seem to work. – UBUNTU_new Mar 10 '19 at 06:40
  • @Python_new: `malloc` isn’t required to initialize the memory it allocates, so the initial value of each `memory[b][s]` is *indeterminate*. See my edit for an example. – John Bode Mar 10 '19 at 12:37
0

Arrays in C are very basic. A multi-dimensional array is just an array of arrays; so when you say:

int A[10][11];
...
A[3][4] = 17;

you could have just as well written:

int A[110];
...
A[37] = 17;

Why 37? 3*11 + 4 = 37.

How did the compiler know to multiply by 11? You told it in the definition of A!

Not satisfied with the disaster of const and volatile, the standards [sic] body added dynamic array sizes to a special case of arrays, permitting this idiocy:

void f(int n, int m) {
    int A[n][m];
    int i,j;
    for (i = 0; i < n; i++) {
        for (j = 0; j < m; j++) {
            A[i][j] = i*m+j;
        }
    }
    int *a = A;
    for (i = 0; i < n*m; i++) {
        printf("%d\n", a[i]);
    }
}

which only works in restricted contexts, and specifically not in the one you are attempting. So, you can either write a bunch of strange accessor functions (nobody will ever thank you for that), or implement the storage map yourself:

int memory[A Very Big Number];
int *getmem(int blk, int segsize) {
     int off = blk * segsize;
     if (off >= 0 && off < A Very Big Number) {
         return memory + off;
     } else {
         return NULL;
     }
}
mevets
  • 10,070
  • 1
  • 21
  • 33