1

I read a text file and store the contents in a 2D array called H inside a function, I need to return this 2D array to main somehow so I can use it there and pass it to other functions. I'm not sure how to get the return to work, maybe using pointers. I made the readTxtFile function into a void function to test if the file reading was working (it does) but I can't do anything with the 2D array outside the function. I have two functions getRows() and getCols() that I didn't show here, I can if needed though.

Heres my code so far:

int main(void){

    //  int *H;
    //  H = readTxtFile("H.txt");
    readTxtFile("H.txt");
    return 0;
}

void readTxtFile(char *filename){
    int rows, cols;
    FILE *fp = fopen(filename, "r");
    if (!fp){
        perror("can't open H.txt\n");
        //return EXIT_FAILURE;
    }
    rows = getRows(fp);
    cols = getCols(fp);
    int (*H)[cols] = malloc(sizeof(int[rows][cols]));
    if(!H){
        perror("fail malloc\n");
        exit(EXIT_FAILURE);
    }

    for(int r = 0; r < rows; ++r){
        for(int c = 0; c < cols; ++c){
            if(EOF==fscanf(fp, "%d", &H[r][c])){
                fprintf(stderr, "The data is insufficient.\n");
                free(H);
                exit(EXIT_FAILURE);
            }
        }
    }
    fclose(fp);

    // printH(rows,cols,H);
    //  return H;


}

This is what the text file looks like:

1 1 0 1 0 0
0 1 1 0 1 0
1 0 0 0 1 1
0 0 1 1 0 1
2 2 2 2 2 2

Any help would be appreciated

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
user3716193
  • 476
  • 5
  • 21

3 Answers3

3

What I would do is defining a structure for the 2D array in term of its:

  • number of columns
  • number of rows
  • pointer to array data in memory

Note that I would "linearize" the array, i.e. allocate a single block of memory of size Columns * Rows * sizeof(int), and given the i and j row and column index, these two indexes can be converted with simple math to a single index in the 1D array (e.g. index = rowIndex * Columns + columnIndex)

Then, I would just return a pointer to this structure from your ReadTxtFile function:

struct IntArray2D {
    int Rows;
    int Columns;
    int* Elements;
};

/* 
 * Define a couple of helper functions to allocate 
 * and free the IntArray2D structure. 
 */
struct IntArray2D* IntArray2D_Create(int rows, int columns);
void IntArray2D_Free(struct IntArray2D* array);

/* 
 * On success, returns a 2D array with data read from file.
 * On failure, returns NULL.
 * NOTE: callers must call IntArray2D_Free() when done 
 */
struct IntArray2D* ReadTxtFile(const char* filename);

EDIT As an alternative, you could define the array structure has having a header block with rows and columns count, immediately followed by the "linearized" 2D array elements, using a "flexible array member":

struct IntArray2D {
    int Rows;
    int Columns;
    int Elements[];
};

You can then define some convenient functions to operate on this custom array structure, e.g.:

struct IntArray2D* IntArray2D_Create(int rows, int columns)
{
    /* Check rows and columns parameters are > 0 */
    /* ... */

    struct IntArray2D *p = malloc(sizeof(struct IntArray2D)
                                  + rows * columns * sizeof(int));
    if (p == NULL) {
        return NULL;
    }

    p->Rows = rows;
    p->Columns = columns;

    /* May zero out the array elements or not... */
    memset(p->Elements, 0, rows * columns * sizeof(int));

    return p; 
}

void IntArray2D_Free(struct IntArray2D* array)
{
    free(array);
}

int IntArray2D_GetElement(struct IntArray2D* array, 
                                 int row, int column)
{
    /* Check array is not NULL; check row and column 
       indexes are in valid ranges ... */

    int index = row * (array->Columns) + column;
    return array->Elements[index];
}

void IntArray2D_SetElement(struct IntArray2D* array, 
                                  int row, int column,
                                  int value)
{
    /* Check array is not NULL; check row and column 
       indexes are in valid ranges ... */

    int index = row * (array->Columns) + column;
    array->Elements[index] = value;
}

Inside your ReadTxtFile function, instead of calling malloc, you can call IntArray2D_Create:

struct IntArray2D* ReadTxtFile(const char* filename) 
{ 
    struct IntArray2D* data = NULL;

    /* ... */

    rows = getRows(fp);
    cols = getCols(fp);
    data = IntArray2D_Create(rows, cols);
    if (data == NULL) {
        /* Handle error ... */ 
        return NULL;
    }

    /* Fill the array ... */

In particular, instead your:

if(EOF==fscanf(fp, "%d", &H[r][c])){

you can do:

    /* int valueReadFromFile */
    if (EOF == fscanf(fp, "%d", &valueReadFromFile)) {
        fprintf(stderr, "The data is insufficient.\n");
    }      
    IntArray2D_SetElement(data, r, c, valueReadFromFile);

And then at the end of the function, you can just have:

    return data;
}
Community
  • 1
  • 1
Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • This looks like it might work. How would I go about defining the IntArray2D_Create() and IntArray2D_Free() helper functions? Are they just like regular functions? – user3716193 Oct 17 '16 at 15:47
  • @user3716193: Yes, they are just _ordinary_ functions. You can call `malloc` and `free` inside them to dynamically allocate the array's memory. – Mr.C64 Oct 17 '16 at 15:49
  • Im pretty new to programming in C and confused about using struct. Do I need to define an IntArray2D variable inside my main function and then again inside my readTxtFile function and have H returned to main? – user3716193 Oct 17 '16 at 15:56
  • I added some sample code for the array create and free functions. – Mr.C64 Oct 17 '16 at 15:57
  • Thanks Mr.C64. I still have some confusion regarding how the struct is used in main and inside my readTxtFile function. Do I define an IntArray2D just once inside main and then pass it to my readTxtFile function? – user3716193 Oct 17 '16 at 16:04
  • @user3716193: I added some sample code for ReadTxtFile. – Mr.C64 Oct 17 '16 at 16:10
  • Everything seems to be working except for for the IntArray2D_SetElement function. I get this error: Undefined symbols for architecture x86_64: "_IntArray2D_SetElement", referenced from: _ReadTxtFile in sim-50782f.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [sim] Error 1 – user3716193 Oct 17 '16 at 16:34
  • Undefined symbols for architecture x86_64: "_IntArray2D_SetElement", referenced from: _ReadTxtFile in sim-c7beeb.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [sim] Error 1 – user3716193 Oct 17 '16 at 16:36
  • @user3716193: Try to remove the inline. – Mr.C64 Oct 17 '16 at 16:36
0

You can't return arrays from functions; return a pointer to an array instead.

Of course, you could create an array (or a pointer to an array) outside of any function (that is, 'global'), and access it from any other function in the same module (or even the same program by using the extern keyword), but I don't recommend this to you. It's best to follow the first aproach.

0

One way would be to return the number of rows and columns back to the caller along with the pointer to the array itself (I haven't tried compiling this...):

int main(void){

    //  int *H;
    //  H = readTxtFile("H.txt");
    int rows;
    int cols;
    void *array;
    readTxtFile("H.txt",&rows,&cols,&array);

    int (*H)[*cols] = array;

    /* process array here */

    free(array);

    return 0;
}

void readTxtFile(char *filename, int *rows, int *cols, void **array ){
    int rows, cols;
    FILE *fp = fopen(filename, "r");
    if (!fp){
        perror("can't open H.txt\n");
        //return EXIT_FAILURE;
    }
    *rows = getRows(fp);
    *cols = getCols(fp);
    int (*H)[cols] = malloc(sizeof(int[*rows][*cols]));
    if(!H){
        perror("fail malloc\n");
        exit(EXIT_FAILURE);
    }

    for(int r = 0; r < rows; ++r){
        for(int c = 0; c < cols; ++c){
            if(EOF==fscanf(fp, "%d", &H[r][c])){
                fprintf(stderr, "The data is insufficient.\n");
                free(H);
                exit(EXIT_FAILURE);
            }
        }
    }
    fclose(fp);

    /* cast is not necessary - added to clearly indicate that the
       pointer returned carries no row/column information with it */
    *array = ( void * ) H;
}

@Mr.C64's answer essentially does the same, in what I think is a much cleaner fashion.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56