0

I'm trying to write a code for a Conway's Game of Life in C. I have some problems with 2D array which contain my universe. I will show you a parts of code that cause me a problems. Actually there a two problems that I can't handle.

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

typedef enum { false, true } bool;

void *allocateMemoryFor2DArray(int x, int y);                                   //allocate memory for 2D array
void initializeUniverseFromFile(char* path, int x, int y, int array[x][y]);     //set values of cells from a file   
void initializeUniverseRandomly(int x, int y, int array[x][y]);                 //set random values 
int getNumberOfColumns (FILE *file);                                            //get number of columns in file (yDim of Universe)
int getNumberOfLines (FILE *file);                                              //get number of lines in file (xDim of Universe)
void print2DArray (int x, int y, int array[x][y]);                              //print 2D array    
void setDimensionsFromFile(char* path,int* xDim, int* yDim, int* xDimB, int* yDimB);//set dimensions of Universe

int main(int argc, char * argv[])
{
    int i, j, n;        //loop indexes
    int nsteps;         //Number of generations
    int im, ip, jm, jp; //neighbour boundaries
    int yDim, xDim;     //Universe boundarier defined by user (Living area);
    int xDimB, yDimB;   //Universe boundaries expanded to each side by 1
    int nsum, isum;     //sum of neighbours, sum of alive cells after iteration
    //int **old, **new; //Universe 
    int (*old)[xDimB];      //Universe
    int (*new)[xDimB];      //Universe

    float x;            //to randomize first population
    char *inputPath;    //Path to file with ixDimBtial uxDimBverse
    char *outputPath;   //Path to output ppm files


    //temporary copy
    int yDim_copy, xDim_copy;

    printf("argc: %d\n", argc);
    switch (argc){
        case 4:         //Read the initial Universe from file
            //Take arguments
            nsteps = atoi(argv[1]); //Get number of iterations
            outputPath = argv[2];   //Get path for outputimages
            inputPath = argv[3];    //File with initialize uxDimBverse;
            setDimensionsFromFile(inputPath, &xDim, &yDim, &xDimB, &yDimB);
            old = allocateMemoryFor2DArray(xDimB, yDimB);   //Alocate memory for expanded Universes
            new = allocateMemoryFor2DArray(xDimB, yDimB);
            printf("Before initialize: yDim: %d, xDim: %d\n", yDim, xDim);      //Here the values are good
            //tmp copy
            yDim_copy = yDim;
            xDim_copy = xDim;
            initializeUniverseFromFile(inputPath, xDim, yDim, old);
            printf("After initialize: yDim: %d, xDim: %d\n", yDim, xDim);       //And here one dim is changed
            yDim = yDim_copy;       //I took a copy to avoid this problem for now
            xDim = xDim_copy;

            printf("After taking from copy: yDim: %d, xDim: %d\n", yDim, xDim);//Here dimensions are good again
            memcpy (new, old, xDimB*yDimB*sizeof(old[xDimB][yDimB]));           //Copy old to new
            break;


        default:
            printf("Usage: %s iter_number, output_name, yDim, xDim\n", argv[0]);
            printf("or\n");
            printf("Usage: %s iter_number, output_name, ixDimBtial_input_name\n", argv[0]);
            printf("Note: Initial file have to be in ./data/\n");
            return 1;
    }

    print2DArray(xDim, yDim, old);      //this works fine
    printf("In main: yDim: %d, xDim: %d\n", yDim, xDim);
    printf("%d\n", old[0][0]);          //this works fine
    printf("%d\n", old[2][2]);          //Segmentation failure

    return 0;
}

void *allocateMemoryFor2DArray(int x, int y){
    int (*array)[y] = malloc( sizeof(int[x][y]) );      //Allocate memory
    if (array == NULL) {                                /* always check the return of malloc */
        perror("malloc");
        exit(EXIT_FAILURE);
    }
    return array;
}

int getNumberOfColumns (FILE *file){
    int yDim = 0;
    char ch;

    do 
    {
        ch = getc(file);                            //Get 1 char from file

        if (ch == '\n')             //If ch is new line
        {
            rewind(file);           //Move file pointer to beginxDimBng of file
            return yDim;            //Found number of columns       
        }       
        else                
            ++yDim;

    }while (ch != EOF);
}

int getNumberOfLines (FILE *file){
    int xDim = 0;
    char ch;

    do  
    {
        ch = getc(file);                            //Get 1 char from file
        if(ch == '\n')                              //If new line
        {
            ++xDim;                             //Increase xDim (another row)
        }
    }while(ch != EOF);
    rewind(file);                                   //Move file pointer to beginxDimBng of file
    return xDim;
}


void print2DArray (int x, int y, int array[x][y])
{
    int i, j;
    printf("x: %d, y: %d\n", x, y);
    for (i = 1; i <= x; ++i) 
    {
        for (j = 1; j <= y; ++j) 
        {
            printf("%d ", array[i][j]);
        }
        printf("\n");
    }
}

void setDimensionsFromFile(char* path, int* xDim, int* yDim, int* xDimB, int* yDimB)
{
    FILE *file;
    int i,j;
    file = fopen(path, "r");        //open file 
    if (file)
    {
        *yDim = getNumberOfColumns (file);                  
        *xDim = getNumberOfLines (file);
        *xDimB = *xDim + 2;  // add 2 for left and right torus topology
        *yDimB = *yDim + 2;
        if (*xDim > 0 && *yDim > 0)
        {   
            printf("UxDimBverse dimension %d x %d\n", *xDim , *yDim);
        }           
        else
        { 
            perror("Wrong initialize file");
            exit(EXIT_FAILURE); 
        }       

        fclose(file);                       //Close file
    }
    else
    {
        perror("Open initialize file error");   
        exit(EXIT_FAILURE);
    }
}


void initializeUniverseFromFile(char* path, int x, int y, int array[x][y])
{ 
    FILE *file;
    int i,j;
    file = fopen(path, "r");        //open file 
    if (file)
    {
        //IxDimBtialize array
        char *ch;
        for (i = 1; i <= x; i++)
        {
            //printf("i: %d ", i);
            for (j = 1; j <= y; j++)
            {
                //printf("j: %d ", j);
                if (fscanf(file, "%c", ch) != 1)
                {
                    perror("Read error\n");
                }
                else if (isdigit((unsigned char)*ch))
                {
                    printf("%d", atoi(ch));
                    array[i][j] = atoi(ch);
                } 
                else 
                {
                    //printf("Numer znaku: %d ", ch);
                    j--;
                }
            }
            printf("\n");
        }
        printf("Done reading\n");
        fclose(file);           //Close file 
    } 
    else 
    {               //File open error
        perror("Open initialize file error");   
        exit (EXIT_FAILURE);
    }
    printf("In initialize yDim: %d, xDim: %d\n", x, y);
}

void initializeUniverseRandomly(int x, int y, int array[x][y])
{
    int i, j;
    float r;
    for (i = 1; i <= x; i++) {
        for (j = 1; j <= y; j++) {
            r = rand() / ((float)RAND_MAX + 1);
            if (r<0.5) {
                array[i][j] = i*x+j;
            }
            else {
                array[i][j] = i*x+j;
            }
        }
    }
}

Problem 1: I set the dimensions of universe (variables xDim and yDim). I print them in line 45 (check comment //Here the values are good). Then I call initializeUniverseFromFile(inputPath, xDim, yDim, old); And one of the dimensions values is changed. I have no idea why. That's why I make temporary copy of this variables, because I've spent 2 hours looking for that error. I even print out this variables at the end of that initializeUniverseFromFile function, and they are fine. But when I'm back in main again (line 50), the value is changed. This is minor problem. The worse is:

Problem 2 I have a pointer int (*old)[xDimB];

xDimB is xDim+2 so it is always bigger than xDim

I initialize the array with function allocateMemoryFor2DArray(xDimB, yDimB)

Then I do this:

print2DArray(xDim, yDim, old);      //this works fine
printf("In main: yDim: %d, xDim: %d\n", yDim, xDim); //Dimensions are fine
printf("%d\n", old[0][0]);          //this works fine
for (i=0; i<=xDim; ++i)
     printf("%d ", old[0][i];       //this works fine too
printf("%d\n", old[2][2]);          //Segmentation failure

Printing whole array with function is fine Printing values from first row are fine Printing value from middle of array create error.

Marcin
  • 35
  • 4
  • `int (*old)[xDimB];` : `xDimB` is unitinialized. – BLUEPIXY Apr 21 '16 at 23:16
  • regarding the first problem, why are you sending the complete array in the last parameter? shouldnt u send a reference to the array instead (double pointer parameter)? – Tarek Apr 21 '16 at 23:26
  • @BLUEPIXY I initialize xDimB in `setDimensionsFromFile` before allocating the memory for array. Or xDimB must be initialized before initiation of `int (*old)[xDimB]` ? – Marcin Apr 21 '16 at 23:32
  • @Tarek Name of table is it address, so I give only the address of array. The function expect `int (*old)[xDimB]`; which is pointer to rows of array. I think it is good – Marcin Apr 21 '16 at 23:38
  • 2
    yes, `xDimB` must be initialized before initiation of `int (*old)[xDimB];` – BLUEPIXY Apr 21 '16 at 23:38
  • 1
    `//int **old, **new; //Universe ` Good you use a 2D array and not these pointers. But why not return the correct pointer from your allocation function? You never should use `void *` or a cast if not absolutely necessary. – too honest for this site Apr 21 '16 at 23:47

2 Answers2

2

I set the dimensions of universe (variables xDim and yDim)

You need these to contain valid numbers before you declare the VLAs int (*old)[xDimB]; and int (*new)[xDimB];. Move these VLA declarations to a latter point in the program, when xDim and yDim have known, verified values.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Ok. So this solve the problem 2. I have access to all array. But what with problem one? It still changes the value of xDim... If xDim then is 5, then after initialize it goes to 49.. This is output: `Done reading In initialize yDim: 5, xDim: 5 After initialize: xDim: 49, yDim: 5` – Marcin Apr 22 '16 at 17:27
  • When I use `initializeUniverseRandomly()` this problem doesn't occurs. – Marcin Apr 22 '16 at 17:34
-2

there are two things that i couldnt explain in the code that may be a reason for a segmentation fault.

1)

void *allocateMemoryFor2DArray(int x, int y);

Why is it void when you are clearly returning an integer array.

2) How are you returning a 2D Array on a single pointer? To define a 2D array you need a double pointer:

int** array = new int*[x];

then you loop through x to define the second dimension:

for (int i=0;i<x;i++)
    array[i] = new int[y];

you finally return you double pointer which is a reference to you array.

your function will become like this:

int** allocateMemoryFor2DArray(int x, int y)
{

    int** array = new int*[x];
    for (int i=0;i<x;i++)
       array[i] = new int[y];
    return array;
}

Similarly these functions:

void initializeUniverseFromFile(char* path, int x, int y, int array[x][y])
void initializeUniverseRandomly(int x, int y, int array[x][y])
void print2DArray (int x, int y, int array[x][y])

should become:

void initializeUniverseFromFile(char* path, int x, int y, int** array)
void initializeUniverseRandomly(int x, int y, int** array)
void print2DArray (int x, int y, int** array)

Finally your pointers in main should also become double pointers.

The reason why the first row is printing while other rows are not is that you are not looping throw the first dimension to initialise the array in the second dimension.

Tarek
  • 687
  • 5
  • 11
  • 2
    "To define a 2D array [of `int`] you need a double pointer": absolutely, positively wrong. – John Bollinger Apr 22 '16 at 00:03
  • even if your comment is correct, he still needs to initialize the second dimension which is not happening causing the problem in printing. just because u say `int *array[x]` doesnt mean it is not in fact a double pointer one explicit and one implicit or in other words an array of integer pointers waiting to be initialized. – Tarek Apr 22 '16 at 00:08
  • 1
    `int *array[x]` is not a 2D array, it is a 1D array of pointers to `int`. It is *incompatible* with a *bona fide* 2D array of `int`. If you declare an array as, say, `int array[2][3]` then the pointer type it decays to is `int (*)[3]`. The object you get by dereferencing that is an array of 3 `int`, *not* a pointer. More generally, **C arrays are not pointers**. The array allocation in the OP's code is perfectly fine. That's actually a bit surprising, because people tend to get this wrong. – John Bollinger Apr 22 '16 at 00:14
  • 1
    This is tagged C. Please don't post an answer in a different programming language. – Lundin Apr 22 '16 at 06:37
  • 2
    And the reason why the OP isn't using pointer-to-pointer is because he just unlearned that bad, incorrect practice [from here](http://stackoverflow.com/a/36745029/584518). So I suggest to @Tarek that you go check that answer too. Note that C++ doesn't support VLAs. In C++ you would do `int (*array)[y] = new int[x][y];` though preferably use std::vector or similar container instead. – Lundin Apr 22 '16 at 07:01