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

标签 c list memory-management dynamic-memory-allocation

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

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)时,我收到了段错误。

我做错了什么?

最佳答案

未分配指针

问题是你这样做:

// 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 仅为您想要的列表对象保留空间。 它不会分配任何其他内容。

所以 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

关于c - 在 C 中实现和使用动态数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48170652/

相关文章:

c - 二叉搜索树递归和 ma​​lloc

c++ - 迭代 C++ 中的对列表?

python - 比较两个列表的元素

计算 CFStringRef/CFMutableArrayRef 使用的字节数

ios - Autorelease 用法说明

c++ - 内存管理的 C++ 单例

c - 来自用户输入的字符串被转换为字母模式。

c - 寄存器中使用 volatile

c - IP 头按位定义解释需要

python - 从 Python 列表中获取数据范围