创建表示模块的 C 结构,指针指向字符串和浮点数组



我正在研究以下问题:

编写定义名为 moduleStruct 的结构化类型的"C"代码 将模块名称存储为字符数组的成员,数量 学生将模块作为整数,学生的姓名采用 模块作为指向字符串数组的指针,学生的结果存储为浮点数数组。

我不确定如何在不知道学生编号的情况下在结构中包含浮点数组,所以我只是假设它们的意思是指向浮点数组的指针。

这是我做的:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct mS{
char name[50];
int studentNum;
char (*studentNames)[50];
float *studentGrades;
}moduleStruct;
void deleteModule(moduleStruct* ms);
void deleteModule(moduleStruct* ms){
free(ms->studentNames);
free(ms->studentGrades);
free(ms);
}
int main(int argc, char* argv[])
{   moduleStruct* m1 = malloc(sizeof(moduleStruct));
strcpy(m1->name, "Programming");
m1 -> studentNum = 5;
char students[][50]  = {"Alan", "Bob", "Charles", "James", "Peter"};
m1 -> studentNames = malloc(sizeof(students));
memcpy(m1 -> studentNames, &students, sizeof(students));
float grades[5]= {1.1, 2.2, 3.3, 4.4, 5.5};
m1->studentGrades = malloc(sizeof(grades));
memcpy(m1->studentGrades, grades, sizeof(grades));
printf("%sn", m1->name);
printf("%dn", m1->studentNum);
printf("%sn", m1->studentNames[2]);
printf("%fn", m1->studentGrades[4]);
deleteModule(m1);
printf("%sn", m1->name);
return 0;
}

所以我有3个问题:

  1. 有没有办法在不知道学生编号的情况下直接使用浮点数组?
  2. 如何改进此解决方案?
  3. 有没有办法让我不必在学生姓名中预定义最大名称大小?

马上:

main.c: In function 'main':
main.c:32:14: warning: format '%f' expects argument of type 'double', but argument 2 has type 'float *' [-Wformat=]
32 |     printf("%fn", m1->studentGrades);
|             ~^     ~~~~~~~~~~~~~~~~~
|              |       |
|              double  float *
main.c:32:14: warning: format '%f' expects argument of type 'double', but argument 2 has type 'float *' [-Wformat=]

因此,让我们修复它以打印值。printf("%fn", *m1->studentGrades);

Programming
5
Alan
1.100000

那更好。


char* heapStudents = malloc(sizeof(students));
heapStudents = *students;
m1 -> studentNames = heapStudents;

这似乎不对。让我们看看发生了什么。

Breakpoint 2, main () at main.c:19
19          char students[][20]  = {"Alan", "Bob", "Charles", "James", "Peter"};
(gdb) s
20          char* heapStudents = malloc(sizeof(students));
(gdb) p students
$5 = {"Alan", '00' <repeats 15 times>, "Bob", '00' <repeats 16 times>, "Charles", '00' <repeats 12 times>,
"James", '00' <repeats 14 times>, "Peter", '00' <repeats 14 times>}
(gdb) x students
0x22fdd0:       0x6e616c41
(gdb) s
21          heapStudents = *students;
(gdb) s
22          m1 -> studentNames = heapStudents;
(gdb) p *heapStudents
$8 = 65 'A'
(gdb) x heapStudents
0x22fdd0:       0x6e616c41

您可以在堆栈上分配字符数组数组(在 0x6e616c41 处(。然后,在堆上分配相同数量的内存,并将指向它的指针保存为*heapStudents。然后,你用等于指向students字符数组中第 0 个元素的衰减指针的值的"地址"覆盖指针,在此过程中泄漏malloc内存。哎呀。

让我们解决这个问题。

m1->studentNames = malloc(sizeof(students));
memcpy(m1->studentNames, &students, sizeof(students));

现在我们分配内存,并在结构中分配m1指向的*studentNames指针,以指向malloc返回的地址。然后我们将堆栈数组复制到堆上分配的内存中。我们没有泄露任何内存,因为我们以后可以打电话给free(m1->studentNames)


必须对studentGrades应用相同的修复程序。但是,让我们尝试一些不同的东西;在堆上分配内存,而不先在堆栈上分配数组。

m1->studentGrades = malloc(sizeof(float[5]));
m1->studentGrades[0] = 1.1;
m1->studentGrades[1] = 2.2;
m1->studentGrades[2] = 3.3;
m1->studentGrades[3] = 4.4;
m1->studentGrades[4] = 5.5;

请注意,我们可以将数组索引与float* studentGrades指针一起使用。


关于正确的问题:

有没有办法在不知道学生编号的情况下直接使用浮点数组?

在不知道最大学生数的情况下,您可以#define MAX_STUDENTS 100初始化数组,然后使用m1->studentNum进行迭代。或者,跳过堆栈上的分配,只使用malloc(m1->studentNum * sizeof(var))/calloc(m1->studentNum, sizeof(var));.

或者,在需要时使用realloc

如何访问第二个学生的名字?

更改结构定义,以便具有指向字符数组的指针数组。char* studentNames[20];有 20 个指针,然后将数据存储在堆上的某个位置,并将指针保存在结构的数组成员中:

m1->studentNames[0] = strcpy(calloc(20, sizeof(char)), "Alan");
m1->studentNames[1] = strcpy(calloc(20, sizeof(char)), "Bob");
m1->studentNames[2] = strcpy(calloc(20, sizeof(char)), "Charles");
m1->studentNames[3] = strcpy(calloc(20, sizeof(char)), "James");
m1->studentNames[4] = strcpy(calloc(20, sizeof(char)), "Peter");

使用printf("%sn", m1->studentNames[1]);打印"鲍勃"等。

或者,在结构中使用双指针,并使其指向堆上的指针数组。这种方式可以动态分配超过20个元素限制的内存。

如何正确设置指向浮点数组的指针并访问每个成员?

如果结构中有float studentGrades[20];,则可以像这样制作指针

float *p = &m1->studentGrades[0];
printf("%f %fn", p[0], p[1]); // prints 1.100000 2.200000

完全:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct mS{
char name[50];
int studentNum;
char* studentNames[20];
float studentGrades[20];
} moduleStruct;
int main(void) {
moduleStruct *m1 = malloc(sizeof(moduleStruct));
strcpy(m1->name, "Programming");
m1->studentNum = 5;
//char students[][20]  = {"Alan", "Bob", "Charles", "James", "Peter"};
m1->studentNames[0] = strcpy(calloc(20, sizeof(char)), "Alan");
m1->studentNames[1] = strcpy(calloc(20, sizeof(char)), "Bob");
m1->studentNames[2] = strcpy(calloc(20, sizeof(char)), "Charles");
m1->studentNames[3] = strcpy(calloc(20, sizeof(char)), "James");
m1->studentNames[4] = strcpy(calloc(20, sizeof(char)), "Peter");
/*float grades[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
m1->studentGrades = malloc(sizeof(grades));
memcpy(m1->studentGrades, &grades, sizeof(grades));*/
//m1->studentGrades[0] = malloc(sizeof(float[5]));
m1->studentGrades[0] = 1.1;
m1->studentGrades[1] = 2.2;
m1->studentGrades[2] = 3.3;
m1->studentGrades[3] = 4.4;
m1->studentGrades[4] = 5.5;
printf("%sn", m1->name);
printf("%dn", m1->studentNum);
printf("%sn", m1->studentNames[0]);
printf("%fn", m1->studentGrades[0]);
return 0;
}

编辑

所以我有两个问题:

(问题 0 到 2 也许,但我肯定看到 3!

有没有办法在不知道学生编号的情况下直接使用浮点数组?

见上文。

如何改进此解决方案?

除了去除implicit declaration of function 'deleteModule'? 见下文。

有没有办法让我不必在学生姓名中预定义最大名称大小?

使结构包含指向指针数组的指针。 每次添加学生时realloc此数组。分配数组中的一个指针指向malloc个字符数组以存储学生姓名。

typedef struct mS{
// ...
char **studentNames;
}
// ...
char str[] = "Alan";
m1->studentNames = realloc(m1->studentNames, ++(m1->studentNum)*sizeof(*m1->studentNames);
m1->studentNames[m1->studentNum-1] = strcpy(calloc(strlen(str)+1, sizeof(char)), str);

分配5个字节来存储名称,并将指针的大小(可能是8B(添加到m1->studentNames的空间中以存储名称的地址。

您将有 2 种方法:

  1. 要么假设模块将具有最大容量(非常现实,但可能不是练习所需的容量(
  2. 动态分配的阵列,您可以根据需要在其中进行分配

最大(固定(容量

#define moduleCapacity 200
#define moduleMaxName 50
struct moduleStruct {
char name[moduleMaxName];
unsigned int studentsCount;
const char *studentsName[moduleCapacity];
float studentsGrade[moduleCapacity];
};
void populateModule(
struct moduleStruct *target,
const char *moduleName,
const unsigned int studentsCount,
const char* studentsName[],
const float studentsGrade[]) {
strncpy(target->name, moduleName, moduleMaxName);
target->studentsCount = studentsCount;
for(unsigned int i = 0; i < studentsCount; ++i) {
if(i >= moduleCapacity) return; // student over the limit are thrown away, sorry first come first served basis
target->studentsName[i] = studentsName[i];
target->studentsGrade[i] = studentsGrade[i];
}
}
void printModule(struct moduleStruct *input) {
printf("name : %sn", input->name);
printf("students : %in", input->studentsCount);
for(unsigned int i = 0; i < input->studentsCount; ++i) {
printf("%u %s %fn", i+1, input->studentsName[i], input->studentsGrade[i]);
}
}
int main(int argc, char** argv) {
struct moduleStruct m1;
const char *names[] = {"Alan", "Bob", "Charles", "James", "Peter"};
const float grades[] = {1.1f, 2.2f, 3.3f, 4.4f, 5.5f};
populateModule(
&m1,
"Programming",
5,
names,
grades
);
printModule(&m1);
}

动态分配的数组

#define moduleMaxName 50
struct moduleStruct {
char name[moduleMaxName];
unsigned int studentsCount;
const char **studentsName;
float *studentsGrade;
};

void populateModule(
struct moduleStruct *target,
const char *moduleName,
const unsigned int studentsCount,
const char* studentsName[],
const float studentsGrade[]) {
strncpy(target->name, moduleName, moduleMaxName);
target->studentsCount = studentsCount;
// we have the student count, we can allocate an array that is sized just for the class size
target->studentsName = calloc(target->studentsCount, sizeof(char*)); 
target->studentsGrade = calloc(target->studentsCount, sizeof(float));
for(unsigned int i = 0; i < studentsCount; ++i) {
target->studentsName[i] = studentsName[i];
target->studentsGrade[i] = studentsGrade[i];
}
}
// printModule & main remain the same

最新更新