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?

2
  • 1
    What is the exact error message you're getting? Are you compiling in C or C++? Commented Apr 8, 2013 at 13:07
  • Suggest you investigate flexible array members Commented Apr 8, 2013 at 13:12

3 Answers 3

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.

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

7 Comments

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: 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).
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: 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.
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 :(
|
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.

5 Comments

Sure you can... that's a trick commonly used in network communications where the size of the payload is unknown.
@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.
@Joachim -- your explanation of your answer should have been your answer.
@EricPostpischil Yes you can allocate memory on the heap, buy you can't assign the pointer malloc returns to an array.
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!
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 <=

1 Comment

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 :)

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.