如何在C语言中创建一个结构体数组?

135

我正在尝试创建一个结构体数组,其中每个结构体代表一个天体。

我对结构体并不是很熟悉,这就是为什么我决定尝试使用它们而不是一堆数组的原因。然而,我一直遇到许多不同的错误。我已经尝试实现我在各种线程和Stack Overflow上看到的技术(例如C中的结构体数组初始化C中的结构体数组),但并非所有技术都适用。

对于那些已经读到这里的人,以下是更多信息:我不需要任何动态内容,我知道/定义了所有东西的大小。我还需要将其作为全局数组,因为我在几个不同的方法中访问它,这些方法具有定义的参数(即GLUT方法)。

这是我在头文件中定义结构体的方式:

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

在定义结构体内部之前,我有一系列其他全局变量,其中之一是该结构体的数组(基本上,如果我的话说得太不清楚了,下面的行就在上面的东西之上):

struct body bodies[n];

只是让你知道,n 是我合法定义的东西(即 #define n 1)。

我在几种不同的方法中使用这个数组,但最简单、占用空间最少的方法是我的主要方法的简化形式。在这里,我初始化每个结构体中的所有变量,只是为了在某种方式下修改它们之前设置变量:

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;
}

我目前遇到的错误是 nbody.c:32:13: error: array type has incomplete element type,其中第32行是我正在创建结构体数组的地方。
最后澄清一下:我所说的头文件是指在同一个*.c文件中 int main(void) 上面的空间。

嗯,对我来说它运行得很好。你在struct body {}声明之前有没有声明struct body bodies[n]; - Jack
请注意,使用可变长度数组往往会在数组大小超过系统程序堆栈大小时导致神秘的错误或崩溃(作为程序员,这完全超出您的控制)。最好使用malloc()来处理这种情况。 - adrian
@adrian 我认为,由于它是一个#define的值,它并不是一个变量。这将只是struct body bodies[1]的同义词,或者无论n的值是什么。 - Radvylf Programs
@RedwolfPrograms 啊,抱歉,我没有考虑到这一点。就记录而言,如果“n”是一个可以在编译时确定的常量,那么你可能是安全的。 - adrian
10个回答

140

使用:

#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;
}

这个代码正常工作。顺便提一下,你的问题并不是很清楚,所以要把你的源代码布局与上面的代码匹配。

1
此外,我有一个关于结构体类型声明和实际创建实例之间位置的问题——我有一个不同的结构体,在首次创建实例(这次只是单个实例,而不是数组)之前我定义了它的内容,那么为什么这不会产生大量错误?它完全正常工作,这让我想到我的尝试创建数组也应该可以工作,但这一次它没有工作。另外,谢谢你的答案,它起作用了。 - Amndeep7
1
嗨,59个赞?我没有看到结构体数组,我只看到了从结构体中获取的变量数组... - user3402040

22

初始化结构体数组的另一种方法是明确初始化数组成员。如果结构体和数组成员不太多,这种方法是有用且简单的。

使用typedef关键字可以避免每次声明结构体变量时都重新使用struct语句:

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

接下来声明您的结构体数组。初始化每个元素与声明一起进行:

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}};

重申一遍,如果您的数组元素不太多,结构成员也不太大,并且像您所说,您不需要更具动态性的方法,那么这是一个相当简单和直接的解决方案。如果结构成员使用了命名的枚举变量进行初始化(而不仅仅是像上面的例子中那样使用数字),则该方法也可以在某些应用程序中为代码读者提供更好的结构和功能概述。


1
喜欢这个!回答简单明了。 - Ethan
对于简单结构体使用typedef是一种不好的做法。 - meni181818

21

那么,使用 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);
}

8
你的 malloc 行代码应该是 numStudents * sizeof(STUDENT) 吗? - Todd
1
@Todd 最好不要这样做。sizeof *students 是同样的事情,如果 STUDENT 以后发生变化也不会出错。 - trent
@trentcl 他已经为 {numStudents} 个指针分配了内存。不是结构体,而是指针。指针大小通常为4个字节,而结构体的大小为20个字节。20个字节是4的倍数,因此不需要填充,并且结构体存储在从起始地址开始的每20个字节上。这仅适用于您仅为一个特定情况分配内存的情况。否则,其他malloc可能会覆盖您的结构体的内存,因为它没有被分配并且您已经溢出了学生内存。 - John Smith
4
@JohnSmith 你误解了,再仔细读一遍。 sizeof *students 是指向的东西的大小,即 sizeof(STUDENT),而不是 sizeof(STUDENT*)。你正在犯下使用 ptr = malloc(num * sizeof *ptr) 习语所防范的精确错误。在这里检查它(请注意服务器像大多数现代PC一样具有8字节指针,因此大小为8和32而不是4和20)。 - trent
1
@trentcl 谢谢你的解释。我被指针解引用在自己的初始化中的语法搞混了。我想说这有点令人困惑,但绝对是更紧凑的解决方案。 - John Smith
1
@JohnSmith 你应该熟悉这个习语;在C代码中非常常见。例如,在C标签中第二高赞的答案就提到了它(https://dev59.com/dHRB5IYBdhLWcg3wgHWr#605858)。 - trent

13

我觉得你也可以这样写。我也是一名学生,所以我理解你的困境。

#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

8

移动

struct body bodies[n];

到之后

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

其余部分看起来都很好。


2
使用指针的解决方案:
#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;
}

2
您可以以与创建数字数组相同的方式进行操作,但需要将元素值用大括号括起来,如下所示 ->
struct Wrestler studs[count] = {
        {"John", "Cena"},
        {"The", "Undertaker"},
        {"The", "Big Show"},
        {"The", "Rock"},
        {"Triple", "H"},
        {"Scott", "Hall"},
        {"Roman", "Reings"},
        {"Dean", "Ambrose"}};

这里是完整的代码

#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;
}



1

这个错误意味着编译器无法在结构体数组声明之前找到结构体类型的定义,因为您说您在头文件中有结构体定义,而错误出现在 nbody.c 中,那么您应该检查是否正确包含了头文件。 检查您的 #include 并确保在声明任何该类型变量之前完成结构体类型的定义。


我怀疑OP所说的头部并不是指头文件,因为他在结构体数组声明之前写道,“下面的行在上面的东西之上”,这是在他的nbody.c文件中。让我们等他醒来澄清一下疑惑。 - nims
@nims 是正确的,我所说的标题是指 main 语句上方的区域。 - Amndeep7

0
尝试重构您的程序,我得到了以下内容:

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;
    }
}

使用 gcc-11 -std=c17 -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Wconversion -Wstrict-prototypes -fanalyzer 编译没有错误,所以不清楚为什么你会遇到问题。

然而,我们仍有机会通过使用常量 struct body 进行初始化来简化它:

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;
    }
}

-1

我不知道是谁想出了这个,但是没错,你可以构建包含其他结构的结构体! 例如,在飞行模拟器上工作需要知道起点、终点和当前位置(纬度和经度)。 以下是我使用的一个例子:

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

现在我需要两个变量,一个用于纬度(Lat),另一个用于经度(Lon);

struct DM Lat;
struct DM Lon;

现在让我们把所有东西放在一起,称之为“位置”。
struct Position {
    struct DM Lat;
    struct DM Lon; } Current, Start, Finish;

为了访问起点,我将使用德克萨斯州阿迪森市 // 阿迪森: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'; 

最近有人说不要在结构体内部再定义一个结构体,这是怎么回事呢?


这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - user16217248

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接