1

Im tryin to read a ascii-file consisting of 2 columns of and a variable nr of rows. Reading is done by processor 0 and then the data is distributed using MPI_Bcast(...). I had to create a 1-dim buffer array which contains the data and is sent to all other procs because I haven't found a way to directly broadcast the 2-dim. data. There's something wrong with the allocation of the array arr. When I try to print the data, the first 3 rows are printed and then I get a segmentation fault. Most likely there are several mistakes.

I really tried to make the example as simple as possible. my code:

//global variables 
...
double **testarray;
int      dim; 
...
int main(int argc, char **argv){
   ...
   ...
    read_file("test.txt",&testarray,&dim);  
}

int read_file(char* infilename,double ***arr,int *rowsout)
{

  FILE* infile;
  int i,j,ch,number_of_lines=0;
  int rows=0;    
  //first count lines
  if(myid==0)      //myid is processor id
  {
    infile = fopen(infilename, "r");
    do
    {
       ch = fgetc(infile);
       if(ch == '\n')
         number_of_lines++;
    } while (ch != EOF);
    if(ch != '\n' && number_of_lines != 0)
      number_of_lines++;
    //close file
    fclose(infile);
    rows=number_of_lines-1;
    *rowsout=rows;
  }
  // every proc should know about length of file in order
  //to be able to allocate memory
  MPI_Bcast(rowsout,1,MPI_INT,0,MPI_COMM_WORLD);

  //allocate memory
  double *buf;     //1D-buffer for 2D-array
  MPI_Alloc_mem((*rowsout)*2*sizeof(double), MPI_INFO_NULL, &buf);
  MPI_Alloc_mem((*rowsout)*sizeof(double*), MPI_INFO_NULL,arr);
  for (i = 0; i < (*rowsout); i++) {
    MPI_Alloc_mem(2*sizeof(double),MPI_INFO_NULL,&arr[i]);
  }
  // Now read file on proc 0
  if(myid==0)
  {
    infile=fopen(infilename,"r");
    for(i=0;i<rows;i++)
    {
      for(j=0;j<2;j++)
      {
        fscanf(infile,"%lf",arr[i][j]);
        printf("arr[%d][%d]:%e\n",i,j,(*arr)[i][j]);
      }
    }
    fclose(infile);
  }
  return 0;

  //dont go further, error occurs before loop finishs
  MPI_Bcast(buf,(rows)*2,MPI_DOUBLE,0,MPI_COMM_WORLD);

  //now reconstruct array from buffer  
  for(i=0;i<(*rowsout);i++)
  {
    for(j=0;j<2;j++)
    {
      *arr[i][j]=buf[i*2+j];
    }
  }
  MPI_Free_mem(buf);
  return 0;
}
5
  • I see you allocate arr, so you will also most likely (I don't know what this MPI stuff is, but with normal malloc(), you need to have a matching free() for every one); you'd probably have to free every (*arr)[i] and then finally free *arr as well. Also, unless you're using C99 or above, and know it won't be ran on C89/M$, you should explicitly return 0; from main(). Did you try stepping through this with a debugger? Also, there doesn't seem to be a reason to have both rowsout and rows? Also, if you are reading the same file twice, why bother closing it and re-opening it? Commented Aug 11, 2016 at 18:15
  • I don't know why you have buf if you are just reading directly into arr. Also, your loop at the bottom (not only is it never reached because you are returning early in your function, so this loop and the free is never reached) probably should have (*arr)[i][j] and not *arr[i][j]. Also just an FYI, the parenthesis around (*rowsout) are not needed. Also, your do loop is only going to exit when ch is equal to EOF, so testing if it's not equal to '\n' afterwards is pointless and making number_of_lines one extra only to subtract 1 for *rowsout is pointless. Commented Aug 11, 2016 at 18:45
  • And you don't really need number_of_lines since you can just use *rowsout directly. Commented Aug 11, 2016 at 18:46
  • actually rows is not needed. Its a relict from an older version of that code. The same is true for number_of_lines. I guess Closing and reopening the file also doesnt cause the error. I did it that way because I want to jump back to the beginning of the file before reading. But thatnks for that hint. The main problem is the memory allocation of arr...I got it to work using the traditional malloc(...), however I find MPI_alloc_mem is more tricky Commented Aug 11, 2016 at 18:47
  • 1
    You can use fseek() or rewind() for that. Commented Aug 11, 2016 at 18:49

1 Answer 1

1

You have some issues with your arr as you guessed. Here is a fixed version of your code (minus the MPI stuff):

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

void read_file(char *filename, double ***arr, int *rowsout);

int main(void)
{
    double **testarray;
    int dim;

    read_file("test.txt", &testarray, &dim);

    return 0;
}

void read_file(char *filename, double ***arr, int *rowsout)
{
    FILE *infile;
    int i, j, ch;

    infile = fopen(filename, "r");

    do
    {
            ch = fgetc(infile);
            if (ch == '\n')
                    ++*rowsout;
    } while (ch != EOF);
    rewind(infile);

    *arr = malloc(sizeof **arr * *rowsout);

    for (i = 0; i < *rowsout; ++i)
            (*arr)[i] = malloc(sizeof ***arr * 2);

    for (i = 0; i < *rowsout; ++i)
    {
            for (j = 0; j < 2; ++j)
            {
                    fscanf(infile, "%lf", &(*arr)[i][j]);
                    printf("(*arr)[%d][%d]: %e\n", i, j, (*arr)[i][j]);
            }
    }

    fclose(infile);
    for (i = 0; i < *rowsout; ++i)
            free((*arr)[i]);
    free(*arr);
}

Example input: test.txt

0.01 0.02
0.3 0.4
5.0 6.0

Example output:

(*arr)[0][0]: 1.000000e-02
(*arr)[0][1]: 2.000000e-02
(*arr)[1][0]: 3.000000e-01
(*arr)[1][1]: 4.000000e-01
(*arr)[2][0]: 5.000000e+00
(*arr)[2][1]: 6.000000e+00

I believe the mistake comes with your fscanf() line. fscanf() wants a double * when you are using "%lf", but you are instead passing arr[i][j] and there's two things wrong with that: since arr is actually a pointer to your 2-D array, you will need to deference it first ((*arr)), and second, since it needs the address of the double, you will need to use &: &(*arr)[i][j].

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks a lot. Its serial code but I will try to make it parallel.
Yeah, I don't know about that MPI stuff, but this at least fixes your segfault (or should, anyways, because I had segfaults trying to read into arr the way you were). Don't forget error checking for fopen() and probably your alloc too.
Thanks for your effort...I really appreciate it. Thing are getting really complicated when you want to make a code as universal as possible ^^. I also didn't know I can use sizeof **arr and sizeof ***arr....really smart
You're welcome. Did it fix your issue? If so, you can 'accept' my answer ;). Also, I read recently someone suggest to use size_t for array index/looping through arrays (or ptrdiff_t if you need the signed version (if you're using negative numbers), but assuming you know you won't have more than however many lines int can represent, using int for i should be fine.
Thanks for the accept :). Something that might make your code more readable... you might want to try: rename read_file()'s formal parameter 'double ***arr' to something like 'double ***arrp' (or whatever), and then you declare a double **arr = *arrp; and then replace every (*arr) with arr, e.g., (*arr)[i][j]/(*arr)[i]/&(*arr)[i][j], and then for all of the other occurrences of arr, you can remove one asterisk (i.e., change sizeof *arr to sizeof arr and sizeof ***arr to sizeof **arr and free(*arr); to free(arr);). Optional, but may be a little easier with less *'s.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.