c-程序在重复调用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()函数用于分配多个内存块。它是一种动态内存分配函数,用于将内存分配给复杂的数据结构,如数组和结构。如果此函数未能按指定分配足够的空间,则返回将为null的指针。胼胝函数的完整形式是连续分配。

为什么要使用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);

在上面的语法中,ptr是cast_type的指针。malloc函数返回一个指针,指向字节大小的已分配内存。

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语言程序计算前十项的总和。如果指针值为null,则不会分配内存空间。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(和friends)的头文件(<stdlib.h>),您将需要很长时间来检测和查看程序崩溃的原因。

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

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

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

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

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

最后,代码结束为(包括链表中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, 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 = "nn{ ";
for (p = globalList.first; p; p = p->next) {
printf("%s"%s"", sep, p->name);
sep = ", ";
}
printf(" };n");
}

最新更新