程序在重复调用 calloc() 时崩溃

program crashes at repetitive calloc() call

编辑:由 kaylums 的小评论解决。谢谢!

早上好, 我对 C 还是比较陌生,我正在尝试制作一个双向链表。 我让我的程序正确地 运行 具有这种元素的所有功能:

在我的 insertElement() 函数的 calloc() 调用中,程序在列表中插入 2 个或 3 个元素后崩溃。我没有得到任何 SIGSEGV 或任何东西,程序只是以随机负值 return 停止。 我将尝试给出函数和函数调用的最小代码示例:

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

typedef struct Element {
    char name[30];
}Element;

typedef struct List {
    int size;
    Element* first;
    Element* last;
}List;



Element* insertElement(List* List, char name[30]) {
    Element* element;
    element = (Element*)calloc(0, sizeof(Element));
    strncpy_s(element->name, name, 30);
    return element;
}

List globalList;
char name[30];

int main() {
    while (true) {
        printf("insert the name >>");
        if (fgets(name, 30, stdin) != NULL)
            name[strcspn(name, "\n")] = 0;
        insertElement(&globalList, name);
    }
}

那些基本的东西已经有明显的错误了吗? 非常感谢您!任何建议将不胜感激,祝你有美好的一天!

element = (Element*)calloc(0, sizeof(Element));

第一个参数中的 0 是什么?
实际上你从记忆中要求你的类型为 0!

这里有一些关于动态内存分配的解释:
动态内存分配是在运行时间分配内存的过程。有四个库例程,calloc()、free()、realloc() 和 malloc(),可用于在程序执行期间分配和释放内存。这些例程在名为 stdlib.h.

的头文件中定义

什么是 malloc()?

这是一个动态分配内存块的函数。它保留指定大小的内存 space 和 returns 指向内存位置的空指针。

返回的指针通常是void类型。这意味着我们可以将 malloc 函数分配给任何指针。 malloc的完整形式是内存分配。

什么是 calloc() ?

Calloc() 函数用于分配多个内存块。它是一个动态内存分配函数,用于将内存分配给复杂的数据结构,如数组和结构。如果此函数未能按指定分配足够的 space,它 returns 将为空指针。 calloc函数的完整形式是连续分配。

为什么要使用 malloc()?

这里是使用malloc()的原因

You should use malloc() when you have to allocate memory at runtime.
You should use malloc when you have to allocate objects which must exist beyond the execution of the current memory block.
Go for malloc() if you need to allocate memory greater than the size of that stack.
It returns the pointer to the first byte of allocated space.
It enables developers to allocate memory as it is needed in the exact amount.
This function allocates a memory block size of bytes from the heap.

为什么要使用 calloc() ?

这里是使用calloc()的原因

When you have to set allocated memory to zero.
You can use calloc that returns a pointer to get access to memory heap.
Used when you need to initialize the elements to zero to returns a pointer to the memory.
To prevent overflow that is possible with malloc()
Use calloc() to request a page that is known to already be zeroed.

malloc() 的语法 这是 malloc()

的语法
ptr = (cast_type *) malloc (byte_size);

n 以上语法,ptr 是cast_type 的指针。 malloc 函数 returns 指向分配内存的指针 byte_size.

C 中的 malloc() 示例
在下面的代码中,sizeof(*ptr) 用于分配一个包含 15 个整数的内存块。在 printf 语句中,我们找到第 6 个整数的值。

#include<stdlib.h>
#include<stdio.h>
int main(){
int *ptr;
ptr = malloc(15 * sizeof(*ptr)); 
    if (ptr != NULL) {
      *(ptr + 5) = 480; 
      printf("Value of the 6th integer is %d",*(ptr + 5));
    }
}

输出:

第6个整数的值为480

calloc()的语法
这是 malloc()

的语法
ptr = (cast_type *) calloc (n, size);

以上语法用于分配n个大小相同的内存块。分配内存space后,所有字节都初始化为零。返回当前位于已分配内存 space 第一个字节的指针。

C 中的 calloc() 示例
下面的 C 语言程序计算前十项的总和。如果指针值为null,那么内存space将不会被分配。 For循环用于迭代变量“i”的值并打印总和。最后,函数 free 用于释放指针。

#include <stdio.h>
#include <stdlib.h>
    int main() {
        int i, * ptr, sum = 0;
        ptr = calloc(10, sizeof(int));
        if (ptr == NULL) {
            printf("Error! memory not allocated.");
            exit(0);
        }
        printf("Building and calculating the sequence sum of the first 10 terms \n");
        for (i = 0; i < 10; ++i) { * (ptr + i) = i;
            sum += * (ptr + i);
        }
        printf("Sum = %d", sum);
        free(ptr);
        return 0;
    }

输出:

构建并计算前10项的序列和 n Sum = 45

我不会扩展实际问题(指定 0 作为向 calloc() 请求的元素数)。我将向您指出在您的代码中发现的其他几项内容。

阅读您的代码的第一个问题是您没有包含文件 <stdbool.h>,这是使用常量 truefalse 以及类型 bool 所必需的.我已经在第一行添加了。

#include <stdbool.h>

接下来,您在多个地方使用值 30 作为多个相关对象的大小。如果您将来决定更改该值,将很难找到 constan 30 的所有出现并更改所有这些(并且您也使用 30 进行任何其他操作的风险和它在中间改变了)

我已经包含了一个包含以下行的常量:

#define NAME_LENGTH  (30)

和所有定义: ...

    char name[NAME_LENGTH];

在结构中...

Element* insertElement(List* List, char name[NAME_LENGTH]) {

insertElement 的原型中(你不需要因为 name 实际上被定义为 char *,而不是 NAME_LENGTH 元素的数组......

另一方面,您需要在每个 Element 到 link 上包含一个指向列表下一个元素的指针。这是在 name:

之后立即完成的
    struct Element *next; /* we need to include struct as the type Element is not yet defined */

接下来,将 sizeof *element 作为 calloc() 的第二个参数,将 1 作为第一个参数。更好的是,如果你要初始化Element结构中的所有字段,那么最好调用malloc()(见最终代码,贴在最后)

从不,从不,从不转换由 malloc() 编辑的值 return (和朋友们)这是一个导致很多人的遗产 错误,未被发现(并且很难找到), 由于演员。当你投你告诉编译器: 把它留在我手中,因为我知道我在做什么。还有这个 让编译器在应该抱怨的时候保持沉默。 问题主要与忘记包含有关 声明 malloc(和朋友)的头文件 (<stdlib.h>) 你会花很长时间来检测和 查看您的程序崩溃的原因。

出于同样的原因,不要使用类型的大小,当 您可以使用指向的表达式作为 类型。这是因为如果你改变 指向对象,你需要记住,在这里你有 放置对象的类型(您也需要更改它) 这样,这个表情 如果您将对象更改为非 指针对象。此外,您已请求 0 个元素 指定类型的,这在其他答案中已经注意到。这将使 calloc() 变为 return NULL,您没有签入代码的值,您稍后会尝试使用它。这会使你的程序崩溃,但在最好的情况下,它是未定义的行为(并且是一个很难找到的错误,所以要小心并始终检查由 malloc() 编辑的值)。

接下来,不要使用 strncpy_s(),因为它是 Microsoft 特定的例程,不包含在任何标准中。 strncpy():

提供了合适的替代品
    strncpy(element->name, name, sizeof element->name);

也使用 sizeof 运算符,因为如果您将来决定更改指针的类型,它会保护您。

最后,main()while语句的测试表达式最好用fgets()。原因是您可以在检测到文件结尾时结束循环。

最后,您的代码结束为(包括 linked 列表中 Element 的 linking):

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

#define NAME_LENGTH     (30)

typedef struct Element {
    char name[NAME_LENGTH];
    struct Element *next;
} Element;

typedef struct List {
    int size;
    Element* first;
    Element* last;
} List;



Element* insertElement(List* List, char name[NAME_LENGTH]) {
    Element* element;
    /* NEVER, NEVER, NEVER cast the value returned by malloc
     * (and friends) This is a legacy that causes a lot of
     * errors, that get undetected (and very difficult to find),
     * due to the cast.  When you cast you tell the compiler:
     * leave it in my hands, as I know what I'm doing.  And this
     * makes the compiler silent, when it should be complaining.
     * The problem mainly has to do with forgetting to include
     * the header file where malloc (and friends) are declared
     * (<stdlib.h>)  and you will take long time to detect and
     * see why your program has crashed. */
    /* for the same reason, don't use the size of the type, when
     * you can use the pointed to expression as template of the
     * type.  This is because if you change the type of the
     * pointed to object, you need to remember that here you have
     * put the type of the object.  This way, this expression
     * will only be bad if you change the object into a non
     * pointer object.  Also, you have requested for 0 elements
     * of the specified type. */
    element = malloc(sizeof *element);
    /* don't use strncpy_s as it is not standard. Use the sizeof
     * operator again, to protect the expression if you change
     * the type of element->name */
    strncpy(element->name, name, sizeof element->name);
    element->next = NULL;
    if (List->last) {
        List->last->next = element;
        List->last = element;
    } else {
        List->first = List->last = element;
    }
    return element;
}

List globalList;
char name[NAME_LENGTH];

int main() {
    /* if you put the fgets() call as the test of the while
     * statement below, you will process each line until you get
     * an end of file condition. Then you can do both things: to
     * null the occurence of the \n char, and the call to
     * insertElement()  I have not corrected because it's a
     * question of taste. */
    printf("insert the name >> ");
    while (fgets(name, sizeof name, stdin) != NULL) {
        /* sizeof name is better than the constant, as if you
         * change the type definition of object name, you have to
         * remember that you are using here its size.  sizeof
         * does the job for you. */
        name[strcspn(name, "\n")] = 0;
        insertElement(&globalList, name);
        printf("insert the name >> ");
    }
    Element *p;
    char *sep = "\n\n{ ";
    for (p = globalList.first; p; p = p->next) {
        printf("%s\"%s\"", sep, p->name);
        sep = ", ";
    }
    printf(" };\n");
}