为结构内的变量分配动态内存

Allocating dynamic memory for a variable inside a structure

下面是一个小代码,目标是学习和练习使用结构创建学生数据库,动态分配每个学生获得的分数。我 运行 遇到的问题是当我尝试为这里的每个学生提供分数时,

scanf("%d",((学生+i)->(ptr_marks+j)));

// creating a structure with variable students and the marks they have obtained

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

#define debugging

struct Student {
    int age;
    int roll_no;
    int* ptr_marks;
};

int main (void) {

// total number of students
    int num_students;
    printf("Enter # of Students: ");
    scanf("%d",&num_students);

// structure array definition
    struct Student* students;
    students = (struct Student*) malloc (num_students * sizeof(struct Student));
    assert(students != NULL);

    int marks;

    for (int i = 0 ; i < num_students ; i ++)   {

        (students + i )->roll_no = i + 1;
        (students + i )->age = i + 10;
        printf("Enter #'s of Subjects for Student %d: ",i);
        scanf("%d", &marks);

        // allocating memory for marks obtained by each student
        (students + i)->ptr_marks = (int *) malloc (sizeof(int) * marks);
        for (int j = 0 ; j < marks ; j ++)  {

            printf("Enter Mark for Subject %d: ", j+1);
            scanf("%d",((students + i)->(ptr_marks + j)));

        }
    }

#ifdef debugging

    for (int j = 0 ; j < num_students ; j ++)   {

        printf("The roll # of student %d are %d \n", j+1, (students+j)->roll_no);

    }

#endif

    free(students);
    //TODO
            // code to free up memory for marks
    return 0;
}

您尝试使用指针表示法而不是使用数组索引表示法会使事情变得更加困难。例如,您分配 students 没问题,但请参阅 Do I cast the result of malloc?

当您开始使用学生时,虽然 (students + i)->roll_no = i + 1; 在技术上很好,但它更具可读性:students[i].roll_no = i + 1;[..] 也充当取消引用。您对每个学生的分配 ptr_mark 将是:

    students[i].ptr_marks = malloc (sizeof(int) * marks);
    assert (students[i].ptr_marks);

注:验证每个分配)

其余部分只是清理并与在 students[]... 上使用数组索引概念保持一致,并确保您 验证每个输入,例如

    for (int j = 0 ; j < marks ; j++)  {
        printf ("Enter Mark for Subject %d: ", j+1);
        if (scanf ("%d", &students[i].ptr_marks[j]) != 1)
            return 1;
    }

然后您可以释放所有分配的内存块:

    for (int i = 0; i < num_students; i++)
        free (students[i].ptr_marks);      /* free storage for ptr_marks */
    free(students);                        /* free pointers */

总而言之,你可以这样做:

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

#define debugging

struct Student {
    int age;
    int roll_no;
    int* ptr_marks;
};

int main (void) {

    // total number of students
    int num_students;
    printf("Enter # of Students: ");
    if (scanf("%d",&num_students) != 1)
        return 1;

    // structure array definition
    struct Student *students;
    students = malloc (num_students * sizeof(struct Student));
    assert(students != NULL);

    int marks;

    for (int i = 0 ; i < num_students ; i ++)   {

        students[i].roll_no = i + 1;
        students[i].age = i + 10;
        printf("Enter #'s of Subjects for Student %d: ",i);
        if (scanf("%d", &marks) != 1)
            return 1;

        // allocating memory for marks obtained by each student
        students[i].ptr_marks = malloc (sizeof(int) * marks);
        assert (students[i].ptr_marks);
        for (int j = 0 ; j < marks ; j++)  {
            printf ("Enter Mark for Subject %d: ", j+1);
            if (scanf ("%d", &students[i].ptr_marks[j]) != 1)
                return 1;
        }
    }

#ifdef debugging

    for (int j = 0 ; j < num_students ; j ++)   {

        printf("The roll # of student %d are %d \n", j+1, (students+j)->roll_no);

    }

#endif

    for (int i = 0; i < num_students; i++)
        free (students[i].ptr_marks);
    free(students);

    return 0;
}

例子Use/Output

使用杂项输入进行锻炼会导致:

$ ./bin/struct_ptr_alloc
Enter # of Students: 2
Enter #'s of Subjects for Student 0: 3
Enter Mark for Subject 1: 90
Enter Mark for Subject 2: 91
Enter Mark for Subject 3: 94
Enter #'s of Subjects for Student 1: 3
Enter Mark for Subject 1: 87
Enter Mark for Subject 2: 72
Enter Mark for Subject 3: 93
The roll # of student 1 are 1
The roll # of student 2 are 2

内存Use/Error检查

在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址 因此,(2) 当不再需要它时可以释放

您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。

对于Linux valgrind是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。

$ valgrind ./bin/struct_ptr_alloc
==5051== Memcheck, a memory error detector
==5051== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5051== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==5051== Command: ./bin/struct_ptr_alloc
==5051==
Enter # of Students: 2
Enter #'s of Subjects for Student 0: 2
Enter Mark for Subject 1: 99
Enter Mark for Subject 2: 100
Enter #'s of Subjects for Student 1: 2
Enter Mark for Subject 1: 89
Enter Mark for Subject 2: 92
The roll # of student 1 are 1
The roll # of student 2 are 2
==5051==
==5051== HEAP SUMMARY:
==5051==     in use at exit: 0 bytes in 0 blocks
==5051==   total heap usage: 5 allocs, 5 frees, 2,096 bytes allocated
==5051==
==5051== All heap blocks were freed -- no leaks are possible
==5051==
==5051== For counts of detected and suppressed errors, rerun with: -v
==5051== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认您已释放所有分配的内存并且没有内存错误。

检查一下,如果您还有其他问题,请告诉我。

您是否要动态分配结构和数据,在结构中使用零(或可变)长度数组而不是指针。

typedef struct {
    int age;
    int roll_no;
    int ptr_marks[0]; // or int ptr_marks[0] 
 } student;

然后您可以动态分配和重新分配整个对象。

student *allocstudent(student *st, size_t nmarks)
{
     student *tmp = realloc(st, sizeof *st + nmarks * sizeof(st-> marks[0]));
    return tmp;
}