在 C 中实现和使用动态数组

Implementing and using a dynamic array in C

我正在尝试创建一个学生列表,这些学生使用动态分配的数组来存储结构。为此,我有以下结构:

typedef struct s_student 
{ 
    char name[64];     
    int matrikelNummer; 
} student; 

typedef struct s_studentList 
{ 
    student* students;
    int numberOfStudents; 
} studentList;

到目前为止我是这样使用它们的:

int main(int argc, char** argv) {

    studentList* list = NULL;
    list = (studentList*) malloc(sizeof(studentList));

    list->numberOfStudents =1;
    //list->students->matrikelNummer = 1;
    //strcpy(list->students->name , "karim");

    printf("numberOfStudents : %d\n" , list->numberOfStudents );
    //printf("matrikelNummer   : %d\n" , list->students->matrikelNummer);
    //printf("name             : %d\n" , list->students->name);

    free(list);

    return 0;
}

这似乎没有问题。但是,当我尝试按照注释行中的概述将数据分配给学生(matrikelNummername)时,我收到了分段错误。

我做错了什么?

在你的 s_studentlist 结构中你有一个名为 students 的指针,你从未在这个成员上使用过 malloc 所以它只是一个指向内存中随机块的指针,分配它的方式与你对 list变量

您需要为学生分配内存。使用 malloc 时,您只为列表类型分配内存,其中包括指向学生类型的指针,但不包括学生类型的内存:

studentList* list = NULL;
list = (studentList*) malloc(sizeof(studentList));
list->students = (student*) malloc(sizeof(student));

你列出的另一个问题是我不知道它是如何设计的。 列表通常包含指向下一个元素的指针:

typedef struct s_student {
    struct s_student* next;
    char name[64];
    int matrikelNummer; 
} student; 

另一种方法是将学生列表作为数组存储在 studentList 中,但有时您需要为多个学生分配内存 student:

list->students = (student*) malloc(list->numberOfStudents * sizeof(student));

这不是列表,而是一个数组,您可以使用

访问条目
list->students[0].matrikelNummer

没有分配指针

问题是你做的:

// list points to null after that line
studentList* list = NULL;

// allocate memory for the list struct
list = (studentList*) malloc(sizeof(studentList));

// set field inside list struct
list->numberOfStudents =1;

// list->students is a pointer, but pointers should point to something valid
// So the question is:  Was list->students set to NULL?
// Or was it mallocated?
list->students->matrikelNummer = 1;

所以你访问 list->students 那是一个指针。
列表本身是通过 malloc 分配的,所以很好。
但是 malloc 只为你想要的列表对象保留 space 。 它不会分配任何其他内容。

所以 list->students 是一个未分配的指针 - 这就是我们得到分段错误的原因。

这个问题的解决方案非常简单——我们不仅要分配一个列表,还要分配我们使用的所有指针(在本例中是它的 students 成员):

// That was earlier there:
studentList* list = NULL;
list = (studentList*) malloc(sizeof(studentList));

// After allocating place for list also allocate place for list->students:
list->students = (student*) malloc(sizeof(student));

无效内存使用检测

万一遇到分段错误或内存泄漏,最好知道有很多工具可以帮助程序员检测此类严重错误。

其中之一是Valgrind

Valgrind 可用于 Linux(并且可能用于 Windows,但有缺陷且未经测试)。

它是很棒的工具,可以遍历您的程序并通知您任何泄漏、无效释放和禁止内存地址的使用。

Valgrind 的用法示例:

# Compile your code
gcc list.c -o list
# Use Valgrind
valgrind --tool=memcheck ./list

以及 valgrind 显示的内容:

==26761== Memcheck, a memory error detector
==26761== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==26761== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==26761== Command: ./list
==26761==
==26761== Use of uninitialised value of size 8
==26761==    at 0x4006A1: main (in /home/students/inf/p/ps386038/stack/list)
==26761==
==26761== Invalid write of size 4
==26761==    at 0x4006A1: main (in /home/students/inf/p/ps386038/stack/list)
==26761==  Address 0x40 is not stack'd, malloc'd or (recently) free'd
==26761==
==26761==
==26761== Process terminating with default action of signal 11 (SIGSEGV)
==26761==  Access not within mapped region at address 0x40
==26761==    at 0x4006A1: main (in /home/students/inf/p/ps386038/stack/list)
==26761==  If you believe this happened as a result of a stack
==26761==  overflow in your program's main thread (unlikely but
==26761==  possible), you can try to increase the size of the
==26761==  main thread stack using the --main-stacksize= flag.
==26761==  The main thread stack size used in this run was 8388608.
==26761==
==26761== HEAP SUMMARY:
==26761==     in use at exit: 16 bytes in 1 blocks
==26761==   total heap usage: 1 allocs, 0 frees, 16 bytes allocated
==26761==
==26761== LEAK SUMMARY:
==26761==    definitely lost: 0 bytes in 0 blocks
==26761==    indirectly lost: 0 bytes in 0 blocks
==26761==      possibly lost: 0 bytes in 0 blocks
==26761==    still reachable: 16 bytes in 1 blocks
==26761==         suppressed: 0 bytes in 0 blocks
==26761== Rerun with --leak-check=full to see details of leaked memory
==26761==
==26761== For counts of detected and suppressed errors, rerun with: -v
==26761== Use --track-origins=yes to see where uninitialised values come from
==26761== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

所以这表明您在函数 main 中访问了无效地址:

==26761== Invalid write of size 4
==26761==    at 0x4006A1: main (in /home/students/inf/p/ps386038/stack/list)
==26761==  Address 0x40 is not stack'd, malloc'd or (recently) free'd

并说出地址(甚至没有分配指针)!

正确的 C 列表实现

如果你想实现一个包含学生列表的指针结构 那么一种常见的方法是将指向下一个学生(列表中的下一个)的指针放入 s_student 结构中。

以及指向学生列表中第一个和最后一个学生的指针。

下面是我自己写的一个工作示例:

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

typedef struct s_student student;

struct s_student 
{ 
  char name[64];     
  int matrikelNummer; 
  // This will point to the next student on the list
  student* nextStudent;
}; 

typedef struct s_studentList 
{ 
  student* student;
  // This will point to the last available student
  student* lastStudent;
  // This will point to the first available student
  student* firstStudent;
  int numberOfStudents; 
} studentList;

// Allocates the list
void allocStudentsList(studentList** list) {
    if(list == NULL) return;
    *list = (studentList*) malloc(sizeof(studentList));
    (*list)->lastStudent = NULL;
    (*list)->firstStudent = NULL;
}

// To add the student to the list
void addStudentToList(studentList* list, student studentData) {

    if(list == NULL) return;

    // Allocate a place for the next student
    student* st = (student*) malloc(sizeof(student));

    // If it's first student in the list
    if(list->lastStudent == NULL) {
        list->lastStudent = st;
        list->firstStudent = st;
    } else {
        // The next student after the current last student will be the newly created one
        list->lastStudent->nextStudent = st;
    }

    // Fill the student data
    *st = studentData;
    st->nextStudent = NULL;

    // Set the last available student to the one created
    list->lastStudent = st;

}

// To recurisvely free the students
void freeStudent(student* stud) {
    if(stud->nextStudent != NULL) {
        // Free next student recursively
        freeStudent(stud->nextStudent);
    }
    free(stud);
}

// To free the students list
void freeStudentsList(studentList* list) {
    if(list != NULL) {
        freeStudent(list->firstStudent);
        free(list);
    }
}

// Function that prints single student and returns next one (after him on the list)
student* printStudent(student* stud) {
    if(stud == NULL) return NULL;
    printf("  * Student { matrikelNummer = %d }\n", stud->matrikelNummer);
    // Return next student
    return stud->nextStudent;
}

// Function that prints students list
void printStudentsList(studentList* list) {
    if(list == NULL) return;

    printf("StudentsList [\n");

    student* current_student = list->firstStudent;
    while(current_student != NULL) {
        current_student = printStudent(current_student);
    }

    printf("]\n");
}

int main(int argc, char** argv) {

  studentList* list = NULL;
  allocStudentsList(&list);

  // Create some student data
  student st1;
  st1.matrikelNummer = 1;

  // Another student...
  student st2;
  st2.matrikelNummer = 2;

  // Put them into the list (allocates students and take care of everything)
  addStudentToList(list, st1);
  addStudentToList(list, st2);

  // Print the list
  printStudentsList(list);

  // Free the list (recursively free's all students and take care of all the nasty stuff)
  freeStudentsList(list);

  return 0;
}

网上有很多关于如何编写 C 风格列表结构的教程。 你可以自己找一个。

那里有一个教程:Learn-c linked list tutorial