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

问题描述

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

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

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

#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() ?

它是一个用于动态分配内存块的函数。它保留指定大小的内存空间并返回指向内存位置的空指针。

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

什么是 calloc() ?

Calloc() 函数用于分配多个内存块。它是一种动态内存分配函数,用于将内存分配给复杂的数据结构,例如数组和结构体。如果此函数未能按指定分配足够的空间,它将返回空指针。 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.

ma​​lloc() 的语法 这是 malloc() 的语法

ptr = (cast_type *) malloc (byte_size);

n 以上语法,ptr 是 cast_type 的指针。 malloc函数返回一个指向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个相同大小的内存块。分配内存空间后,所有字节都初始化为零。返回当前位于已分配内存空间第一个字节的指针。

C 中的 calloc() 示例
下面的 C 语言程序计算前十项的总和。如果指针值为空,则不会分配内存空间。 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 作为多个相关对象的大小。如果您决定将来更改该值,将很难找到常量 30 的所有出现并更改所有这些(以及您使用的风险 30 还用于其他任何事情和它在中间变了)

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

#define NAME_LENGTH  (30)

和所有定义: ...

    char name[NAME_LENGTH];

在结构中...

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

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

另一方面,您需要在每个 Element 上包含一个指针,以将每个指针链接到列表的下一个元素。这是在 name 之后立即完成的:

    struct Element *next; /* we need to include struct as the type Element is not yet defined */

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

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

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

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

    strncpy(element->name,name,sizeof element->name);

还可以使用 sizeof 运算符,因为它可以在您将来决定更改指针类型时保护您。

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

最后,您的代码结尾为(包括链接列表中 Element 的链接):

#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,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");
}