0

I'm quite new to C programming, and I have stumbled on some tricky problem. What I want to do is define a dynamic structure array in another structure in C.

Here is my code: First I define the structure shapes:

struct cap_prof_arrays
   {
   double zarr;
   double profil;
   double sx;
   double sy;
   double d_arr;
   };

struct cap_profile
   {
   int nmax;
   double rtot1;
   double rtot2;
   double cl;
   double binsize;
   struct cap_prof_arrays arr[]; /*Will get proper size allocated to it later*/
   };

void read_cap_profile(struct inp_file cap) /* I defined the inp_file structure cap before, everything works great on that account */
   {
    FILE *fptr;
    int i, n_tmp;
    struct cap_profile profile;

    fptr = fopen(cap.prf,"r");
    fscanf(fptr,"%d",&profile.nmax);
    // Increase structure array sizes to fit demands
    profile.arr = malloc(sizeof(struct cap_profile)+profile.nmax*sizeof(double));
    //continue reading data
    for(i=0; i<=profile.nmax; i++){
      fscanf(fptr,"%lf %lf",&profile.arr[i].zarr,&profile.arr[i].profil);
      }
    fclose(fptr);

    //rest of program

Now, the main problem is that during compilation I get some errors on the line where I try to perform the memory allocation, no matter what I try. I was wondering if any of you could help me out? I realize I could probably make my life a lot easier by just defining a very large array size during structure declaration, yet I would love to do it with the dynamic sizing. So I hope you can help me out :)

Thanks a lot!

EDIT1: So I changed some things based on answers I got here. This is what my program looks like now:

struct cap_prof_arrays
  {
  double zarr;
  double profil;
  double sx;
  double sy;
  double d_arr;
  };

struct cap_profile
  {
  int nmax;
  double rtot1;
  double rtot2;
  double cl;
  double binsize;
  struct cap_prof_arrays *arr; /* will get proper size allocated to it later */
  };

struct cap_profile read_cap_profile(struct inp_file cap)
{
 FILE *fptr;
 int i, n_tmp;
// struct cap_profile profile;

 printf("Reading capillary profiles...\n");
 fptr = fopen(cap.prf,"r"); /* READING THE CAPILLARY PROFILE */
 if(fptr == NULL){
   printf("%s file does not exist.\n",cap.prf);
   exit(0);
   }
 fscanf(fptr,"%d",&n_tmp);
 // increase structure array sizes to fit demands;
 struct cap_profile *profile = malloc(sizeof(*profile)+(n_tmp+1)*sizeof(*profile->arr));
 profile->nmax = n_tmp;
 // continue reading the data;
 for(i=0; i<=profile->nmax; i++){
   fscanf(fptr,"%lf %lf",&profile->arr[i].zarr,&profile->arr[i].profil);
   }
 fclose(fptr);

 n_tmp = profile->nmax;

 fptr = fopen(cap.axs,"r"); /* READING THE AXIS DEFINITION FILE */
 if(fptr == NULL){
   printf("%s file does not exist.\n",cap.axs);
   exit(0);
   }
 fscanf(fptr,"%d",&profile->nmax);
 if(profile->nmax != n_tmp){
   printf("Inconsistent axis.dat file: number of intervals different.\n");
   exit(0);
   }
 for(i=0; i<=profile->nmax; i++)
   fscanf(fptr,"%lf %lf %lf",&profile->arr[i].zarr,&profile->arr[i].sx,&profile->arr[i].sy);
 fclose(fptr);

 fptr = fopen(cap.out,"r"); /* READING THE OUTER PROFILE */
 if(fptr == NULL){
   printf("%s file does not exist.\n",cap.out);
   exit(0);
   }
 for(i=0; i<=profile->nmax; i++)
   fscanf(fptr,"%lf %lf",&profile->arr[i].zarr,&profile->arr[i].d_arr);
 fclose(fptr);

 profile->rtot1 = profile->arr[0].d_arr;
 profile->rtot2 = profile->arr[profile->nmax].d_arr;
 profile->cl = profile->arr[profile->nmax].zarr;
 cap.d_screen = cap.d_screen + cap.d_source + profile->cl;
 profile->binsize = 20.e-4;

 return *profile;
}

The error I get now is a Segmentation fault (core dumped) error (during program testing, not during compilation). I suppose I'm doing something wrong with the pointers again, but I don't really understand what I'm doing wrong... Any help?

user2257570
  • 13
  • 1
  • 4

3 Answers3

4

You cannot allocate an array inside a struct separate from the struct. Instead, for a flexible array member*, do this:

Read the number of elements you want to allocate into an int, perhaps one named nmax (a separate int by itself, not the nmax member in a struct).

Allocate space for the entire struct plus the array, and assign address of that space to a pointer:

struct cap_profile *profile = malloc(sizeof *profile + nmax * sizeof *profile->arr);

* A flexible array member is an array with unspecified major dimension at the end of a struct.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Okay, so I left cap_prof_arrays unchanged and changed cap_profile as following, according to @Joachim Pileborg's advise: struct cap_profile { int nmax; double rtot1; double rtot2; double cl; double binsize; struct cap_prof_arrays *arr }; Furthermore, I changed the line Eric Postpischil advised, along with some other things to get rid of some compilation errors. Now however I get a segmentation fault error. See original post for full function overview. Any advise? – user2257570 Apr 08 '13 at 13:39
  • @user2257570: You should make one of those changes or the other, not both. Either the struct contains a flexible array member, and you allocate both as one object (the array is *inside* the struct), or the struct contains a pointer, and you allocate space for the struct and separately allocate space for the array (and then set the pointer to point to the array; the pointer is in the array, and it *points to* the array *outside* the struct). – Eric Postpischil Apr 08 '13 at 14:14
  • Ah, thanks a lot. That does do the trick :) I chose to use the "Struct containing a flexible array" option. My only problem now is that the structure is not returned to my main function properly. I finish the read_cap_profile function with return *profile; yet it doesn't seem to work. Any more very welcome advise on that? (As I want to make the program multithreaded later on I don't want to use global variables, as I heard this can be problematic). – user2257570 Apr 08 '13 at 14:42
  • @user2257570: You want to return `profile`, not `*profile`, for two reasons. One, returning `*profile` returns only a copy of the struct; the original that you allocated is forgotten, and the space is lost. (You should keep track of all your pointers and use `free` to release the space.) Two, `*profile` refers only to the main part of the struct; it ignores the flexible array member, because that part is special. You should return `profile`, and the caller should expect to receive a pointer, and it should free that pointer when done with the object. – Eric Postpischil Apr 08 '13 at 15:12
  • Sorry to bother again, really am... But the problem is when I return profile (literally write return profile; in the code above) I get a compilation error: Incompatible types when returning type 'struct cap_profile *' but 'struct cap_profile' was expected. In my main program I have the following: struct cap_profile profile; profile = read_cap_profile(cap); If I change struct cap_profile profile to struct cap_profile *profile instead (to expect pointer by the caller) I get the same error. Sorry for my weak C skills :( – user2257570 Apr 08 '13 at 15:33
  • @user2257570: When you change what type of thing a function returns in its `return` statement, you must also change the return type in the function’s declaration. – Eric Postpischil Apr 08 '13 at 15:57
  • Ok, I figured it out :) Thanks a lot for the help! (had some troubles first because I was comparing the wrong values to see if the program worked :P I suppose not having a headache or being hungry is a boon when programming in C ;) ) Anyway, thanks a lot. You really helped me in further understanding C and in further developing this program I'm writing. – user2257570 Apr 08 '13 at 18:01
1

When you allocate memory with malloc (or similar functions) it returns a pointer. You can't assign this pointer to an array. In other words, you can't make an array point to something else once created.

What you should do is declare the array as a pointer instead.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Sure you can... that's a trick commonly used in network communications where the size of the payload is unknown. – K Scott Piel Apr 08 '13 at 13:06
  • @KScottPiel If you have an empty array at the end of a structure, you can allocate an instance of that structure with a larger size than the structure and use the array normally. However, you can't allocate an memory of the heap and assign it to an array, which is the case here. – Some programmer dude Apr 08 '13 at 13:08
  • @Joachim -- your explanation of your answer should have been your answer. – K Scott Piel Apr 08 '13 at 13:13
  • @EricPostpischil Yes you can allocate memory on the heap, buy you can't assign the **pointer** `malloc` returns to an **array**. – Some programmer dude Apr 08 '13 at 13:13
  • I declared the array as a pointer, this did indeed solve my compilation errors. However, the returned values of the array were 0, so am looking Eric's answer now to further figure things out. Thanks for the fast help though! – user2257570 Apr 08 '13 at 13:29
0

You could do it this way:

struct cap_profile {
  int nmax;
  struct cap_prof_arrays **arr;
};

And then allocate it like this:

struct cap_profile *profile = calloc(1, sizeof(*profile));

fscanf(fptr, "%d", profile->nmax);
profile->arr = calloc(1, profile->nmax * sizeof(*(profile->arr)));
for (i = 0; i < profile->nmax; i++) {
  profile->arr[i] = calloc(1, sizeof(*(profile->arr[i])));
  fscanf(fptr, "%lf %lf", &profile->arr[i]->zarr, &profile->arr[i]->profil);
}

Two other points:

  1. Allocate the cap_profile structure and return a pointer to it from the read_cap_profile() function if you want to use values in it outside of that function.
  2. The 'for' loop should have < profile->nmax, not <=
  • I don't really understand what you mean (rather new at C, I'm sorry...) I chose to follow @Eric Postpischill's suggested solution. However, I'm having problems returning the structure to my main function, any idea how I could do this? Now I use return *profile; and have the function return as a struct. As for your second remark: I spotted that issue in time myself as well. I was forced however to increase the memory allocated to the structure instead of lowering the amount of i-values the loop runs through. Thanks for the advise though :) – user2257570 Apr 08 '13 at 14:48