134

I'm trying to make an array of structs where each struct represents a celestial body.

I don't have that much experience with structs, which is why I decided to try to use them instead of a whole bunch of arrays. However, I keep on running into numerous different errors. I've tried to implement the techniques that I've seen on various threads and on Stack Overflow (such as Array of structs in C and Initialize array of structs in C), however not all of them were applicable.

Further information for those who have read this far: I don't need any of this to be dynamic, I know/define the size of everything beforehand. I also need this to be a global array as I'm accessing this in several different methods which have defined arguments (i.e., GLUT methods).

This is how I'm defining the struct in my header:

struct body
{
    double p[3]; // Position
    double v[3]; // Velocity
    double a[3]; // Acceleration
    double radius;
    double mass;
};

I have a list of other global variables that I'm defining before I define the interior of the struct, and one of those is the array of this struct (basically, if I'm being too unclear in my fogged speak, the line below is above the stuff above):

struct body bodies[n];

Just so you know, n is something that I've legitimately defined (i.e. #define n 1).

I use this array in several different methods, but the easiest and least space consuming one is a simplified form of my main. Here I initialize all of the variables in each of the structs, just to set the variables for certain before I modify them in some way:

int a, b;
for(a = 0; a < n; a++)
{
    for(b = 0; b < 3; b++)
    {
        bodies[a].p[b] = 0;
        bodies[a].v[b] = 0;
        bodies[a].a[b] = 0;
    }
    bodies[a].mass = 0;
    bodies[a].radius = 1.0;
}

The current error that I'm facing is nbody.c:32:13: error: array type has incomplete element type where line 32 is where I'm making the array of the structs.

One last clarification: By header I mean the space above int main(void), but in the same *.c file.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Amndeep7
  • 2,022
  • 4
  • 17
  • 26
  • Well, it works fine for me. Aren't you declaring `struct body bodies[n];` before `struct body {}` declaration? – Jack May 06 '12 at 04:50
  • Note that using variable-length arrays can often cause mysterious bugs or crashes when the size of the array exceeds the program's stack size on your system (which is completely out of your control as a programmer). It is better to use malloc() for this sort of thing. – adrian Jul 10 '20 at 19:00
  • @adrian I think since it's a `#define`d value, it's not variable. It would be just the same as `struct body bodies[1]`, or whatever the value of `n` is. – Radvylf Programs Sep 17 '20 at 23:41
  • @RedwolfPrograms ah sorry, I did not consider that. For the record, if `n` is a constant which can be determined at compile time you are probably safe. – adrian Sep 17 '20 at 23:57

10 Answers10

139

Use:

#include<stdio.h>

#define n 3

struct body
{
    double p[3]; // Position
    double v[3]; // Velocity
    double a[3]; // Acceleration
    double radius;
    double mass;
};

struct body bodies[n];

int main()
{
    int a, b;
    for(a = 0; a < n; a++)
    {
        for(b = 0; b < 3; b++)
        {
            bodies[a].p[b] = 0;
            bodies[a].v[b] = 0;
            bodies[a].a[b] = 0;
        }
        bodies[a].mass = 0;
        bodies[a].radius = 1.0;
    }

    return 0;
}

This works fine. Your question was not very clear by the way, so match the layout of your source code with the above.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
nims
  • 3,751
  • 1
  • 23
  • 27
  • 1
    Also, I have a question about the placement between the declaration of the struct type and then actually making an instance of it - I have a different struct, one that I defined the contents of _below_ where I first make an instance of it (just one this time, not an array), so why didn't this make the massive series of errors? It worked just fine, which led me to think that my attempt at making an array should have worked as well, but this time it didn't work. Also, thank you for your answer, it worked. – Amndeep7 May 06 '12 at 15:07
  • 1
    Hi, .... 59 likes ? I didn't see arrays of struct, I only see arrays of variable from struck... –  Mar 15 '17 at 23:10
21

Another way of initializing an array of structs is to initialize the array members explicitly. This approach is useful and simple if there aren't too many struct and array members.

Use the typedef specifier to avoid re-using the struct statement everytime you declare a struct variable:

typedef struct
{
    double p[3];//position
    double v[3];//velocity
    double a[3];//acceleration
    double radius;
    double mass;
}Body;

Then declare your array of structs. Initialization of each element goes along with the declaration:

Body bodies[n] = {{{0,0,0}, {0,0,0}, {0,0,0}, 0, 1.0}, 
                  {{0,0,0}, {0,0,0}, {0,0,0}, 0, 1.0}, 
                  {{0,0,0}, {0,0,0}, {0,0,0}, 0, 1.0}};

To repeat, this is a rather simple and straightforward solution if you don't have too many array elements and large struct members and if you, as you stated, are not interested in a more dynamic approach. This approach can also be useful if the struct members are initialized with named enum-variables (and not just numbers like the example above) whereby it gives the code-reader a better overview of the purpose and function of a structure and its members in certain applications.

Abdel Aleem
  • 667
  • 10
  • 15
20

So to put it all together by using malloc():

int main(int argc, char** argv) {
    typedef struct{
        char* firstName;
        char* lastName;
        int day;
        int month;
        int year;

    }STUDENT;

    int numStudents=3;
    int x;
    STUDENT* students = malloc(numStudents * sizeof *students);
    for (x = 0; x < numStudents; x++){
        students[x].firstName=(char*)malloc(sizeof(char*));
        scanf("%s",students[x].firstName);
        students[x].lastName=(char*)malloc(sizeof(char*));
        scanf("%s",students[x].lastName);
        scanf("%d",&students[x].day);
        scanf("%d",&students[x].month);
        scanf("%d",&students[x].year);
    }

    for (x = 0; x < numStudents; x++)
        printf("first name: %s, surname: %s, day: %d, month: %d, year: %d\n",students[x].firstName,students[x].lastName,students[x].day,students[x].month,students[x].year);

    return (EXIT_SUCCESS);
}
Ryan
  • 123
  • 1
  • 12
Dounchan
  • 319
  • 3
  • 10
  • 8
    Should your malloc line have numStudents * sizeof(STUDENT)? – Todd Jul 24 '14 at 16:57
  • 1
    @Todd It's better not to. `sizeof *students` is the same thing and it won't be wrong if STUDENT ever happens to change. – trent Mar 11 '16 at 18:18
  • @trentcl He has allocated memory for {numStudents} amount of pointers. Not structures, but pointers. Pointer size is most usually 4 bytes, meanwhile size of the structure is 20 bytes. 20 bytes is number divisible by 4, so no padding is needed and structure is stored on every 20th byte from starting address. It only works because you allocate memory for only one particular case. Otherwise other mallocs could overwrite memory of your structures, because its not allocated and you have overflown your student memory. – John Smith Oct 14 '19 at 17:36
  • 4
    @JohnSmith You are mistaken; read it again. `sizeof *students` is the size of *what's pointed at*, i.e. `sizeof(STUDENT)`, not `sizeof(STUDENT*)`. You are making the exact mistake that the `ptr = malloc(num * sizeof *ptr)` idiom is supposed to guard against. Check it [here](http://coliru.stacked-crooked.com/a/477e444a4cbcb96a) (note the server, like most modern PCs, has 8 byte pointers so the sizes are 8 and 32 instead of 4 and 20). – trent Oct 14 '19 at 18:10
  • 1
    @trentcl Thank you for explanation. I got confused by the syntax of dereferencing pointer in it's own initialization. I'd say it's a little confusing, but definitely more compact solution. – John Smith Oct 14 '19 at 18:49
  • 1
    @JohnSmith You should be familiar with this idiom; it's very common in C code. [The second-most-highly-upvoted answer in the C tag mentions it, for example](https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc/605858#605858). – trent Oct 14 '19 at 19:36
13

I think you could write it this way too. I am also a student, so I understand your struggle.

#include <stdio.h>
#define n 3

struct {
    double p[3]; // Position
    double v[3]; // Velocity
    double a[3]; // Acceleration
    double radius;
    double mass;
} bodies[n];
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Christakitos
  • 146
  • 1
  • 11
  • What did you change? Can you explain your answer, please? From [the Help Center](https://stackoverflow.com/help/promotion): *"...always explain why the solution you're presenting is appropriate and how it works"*. Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/30716813/edit), not here in comments (but ******* ******* ******* ******* ******* ***without*** ******* ******* ******* ******* ******* "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Mar 24 '23 at 13:57
8

Move

struct body bodies[n];

to after

struct body
{
    double p[3]; // Position
    double v[3]; // Velocity
    double a[3]; // Acceleration
    double radius;
    double mass;
};

The rest all looks fine.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
vrk001
  • 355
  • 1
  • 2
  • 10
2

Solution using pointers:

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

#define n 3

struct body
{
    double p[3]; // Position
    double v[3]; // Velocity
    double a[3]; // Acceleration
    double radius;
    double *mass;
};


int main()
{
    struct body *bodies = (struct body*)malloc(n*sizeof(struct body));
    int a, b;
    for(a = 0; a < n; a++)
    {
        for(b = 0; b < 3; b++)
        {
            bodies[a].p[b] = 0;
            bodies[a].v[b] = 0;
            bodies[a].a[b] = 0;
        }
        bodies[a].mass = 0;
        bodies[a].radius = 1.0;
    }

    return 0;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Helping Bean
  • 147
  • 7
2

You can do it in a same manner as you create the array of numbers but wrap the element's values in braces like this ->

struct Wrestler studs[count] = {
        {"John", "Cena"},
        {"The", "Undertaker"},
        {"The", "Big Show"},
        {"The", "Rock"},
        {"Triple", "H"},
        {"Scott", "Hall"},
        {"Roman", "Reings"},
        {"Dean", "Ambrose"}};

Here is full code

#include <stdio.h>

struct Wrestler
{
    char firstName[20];
    char secondName[20];
};

void pIntro(struct Wrestler *s)
{
    printf("Hi, I am %s %s.\n", s->firstName, s->secondName);
};

int main(int argc, char const *argv[])
{
#define count 8
    struct Wrestler studs[count] = {
        {"John", "Cena"},
        {"The", "Undertaker"},
        {"The", "Big Show"},
        {"The", "Rock"},
        {"Triple", "H"},
        {"Scott", "Hall"},
        {"Roman", "Reings"},
        {"Dean", "Ambrose"}};

    for (int i = 0; i < count; i++)
    {
        pIntro(&(studs[i]));
    }

    return 0;
}


Ace
  • 1,398
  • 13
  • 24
1

That error means that the compiler is not able to find the definition of the type of your struct before the declaration of the array of structs, since you're saying you have the definition of the struct in a header file and the error is in nbody.c then you should check if you're including correctly the header file. Check your #include's and make sure the definition of the struct is done before declaring any variable of that type.

David
  • 29
  • 2
  • 8
  • i doubt OP means header as header file, as he wrote, "the line below is above the stuff above" before the struct array declaration which is in his nbody.c file. Lets wait for him to wake up and clear the doubt. – nims May 06 '12 at 04:52
  • @nims is correct, by header I meant the area above the `main` statement. – Amndeep7 May 06 '12 at 14:39
0

Attempting to reconstruct your program, I get

struct body
{
    double p[3];//position
    double v[3];//velocity
    double a[3];//acceleration
    double radius;
    double mass;
};

#define n 9

struct body bodies[n];

int main(void)
{
    int a, b;
    for (a = 0; a < n; a++) {
        for (b = 0; b < 3; b++) {
            bodies[a].p[b] = 0;
            bodies[a].v[b] = 0;
            bodies[a].a[b] = 0;
        }
        bodies[a].mass = 0;
        bodies[a].radius = 1.0;
    }
}

This compiles without error using gcc-11 -std=c17 -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Wconversion -Wstrict-prototypes -fanalyzer, so it's unclear why you have a problem.

However, we still have opportunity to simplify it by using a constant struct body for initialisation:

int main(void)
{
    static const struct body default_body =
        { {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, 0.0, 1.0 };

    for (unsigned i = 0;  i < n;  ++i) {
        bodies[i] = default_body;
    }
}
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
-1

I don't know who came up with this one but. YES, you can build structs containing other structs! case in point work on a flight sim requires knowing start, finish, and current locations (Latitude and Longitude). An example that I use:

struct DM {
   int degrees;
   float minutes;
   char hemi; };

Now I need two of them one for Lat and the other for Lon so;

struct DM Lat;
struct DM Lon;

Now let's put it all together and call it Position

struct Position {
    struct DM Lat;
    struct DM Lon; } Current, Start, Finish;

and to access start I will use Addison, Texas // Addison: 32-58.113333N 096-50.186667W

Finish.Lon.degrees = 32;
Finish.Lon.minutes = 58.113333;
Finish.Lon.hemi    = 'N';
Finish.Lat.degrees = 96;
Finish.Lat.minutes = 0.186667;
Finish.Lat.hemi    = 'W'; 

Fins ps Now what's all this crud about not making a struct within a struct??

Phoenixcomm
  • 35
  • 1
  • 2
  • 13
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/34322157) – user16217248 May 06 '23 at 01:09