Visual Studio中检测到堆损坏,但程序在另一个C编译器中运行良好:为什么?

Visual Studio中检测到堆损坏,但程序在另一个C编译器中运行良好:为什么?,c,visual-studio-2019,heap-corruption,C,Visual Studio 2019,Heap Corruption,我试图寻找这个问题,但找不到任何答案。我编写了一个程序,用链表实现堆栈及其操作。该程序可在C Web IDE上完美编译和运行 当我在Visual Studio中运行该程序时,它失败,并显示以下错误: 调试错误! 程序:C:\Users。。。我的文件路径 检测到堆损坏:在0x011058C8的正常块78之后。 CRT检测到应用程序在堆缓冲区结束后写入内存 由于我的代码在其他地方运行良好,这一定是我如何使用VisualStudio的问题。有什么想法吗?我是VisualStudio的新手,我担心这可能

我试图寻找这个问题,但找不到任何答案。我编写了一个程序,用链表实现堆栈及其操作。该程序可在C Web IDE上完美编译和运行

当我在Visual Studio中运行该程序时,它失败,并显示以下错误:

调试错误! 程序:C:\Users。。。我的文件路径 检测到堆损坏:在0x011058C8的正常块78之后。 CRT检测到应用程序在堆缓冲区结束后写入内存

由于我的代码在其他地方运行良好,这一定是我如何使用VisualStudio的问题。有什么想法吗?我是VisualStudio的新手,我担心这可能是件愚蠢的事情,但我似乎无法理解

我在下面包含了我的代码,请注意,失败是由VisualStudio中的pop函数引起的

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

struct Node {
    int data;
    struct Node* next;
};

struct Node* top = NULL; //initialize head

void push(int x);
void push(int x) {
    struct Node* add = (struct Node*)malloc(sizeof(struct Node*));
    add->data = x;
    add->next = top; //make add point to what top (head) points to (old 1st)
    top = add; //make top point to add (new 1st)
}

void pop();
void pop() {
    if (top == NULL) return;
    struct Node* temp = top;
    top = top->next;
    free(temp);
}

int topp();
int topp() {
    return top->data;
}

int exist();
int exist() {
    if (top->next) {
        return 1;
    }
    else {
        return 0;
    }
}

void PrintIt();
void PrintIt() {
    struct Node* temp = top;
    while (temp!= NULL) {
        printf("%d ", temp->data);
        temp = temp->next;
    }
    printf("\n");
}

int main() {
    push(1); PrintIt();
    push(44); PrintIt();
    push(23); PrintIt();
    pop(); PrintIt();
    push(9); PrintIt();
    return 0;
}

如果您正在做一些未定义的行为,那么动态内存分配就是这些问题的一个真正的温床,可能发生的未定义的事情之一就是它可以正常工作

这并不意味着你在做正确的事情,UB是应该避免的事情,因为它可能在另一个系统、另一个编译器上,甚至在下周二下午3:05时起不同的作用:-

我要提到几件事:

您不需要在定义之前立即对原型进行所有这些函数声明。如果尚未声明定义,则定义充当声明。 您的exist函数可能会因空列表而崩溃,因为它会取消对top的引用。如果您的目的是检测非空列表,则可以使用return top!=无效的 您不应该在C中显式强制转换malloc的返回值,它可能会导致某些细微的错误。 事实上,在看最后一点的时候,你的错误并不是特别与铸造有关,只是它在那一行上:

struct Node* add = (struct Node*)malloc(sizeof(struct Node*));
//                                                        ^
//                                                      oops!
struct Node*的大小是指向结构的指针的大小,通常但不一定是四个或八个八位字节。如果您的实际结构有一个整数和一个指针,那么这个大小将不够大

它可以在一些提供最小动态内存分配的系统上工作,但绝对不建议这样做。你应该做:

struct Node *add = malloc(sizeof(struct Node));
//                               ^^^^^^^^^^^
//                      size of the node, not pointer

该行已删除了强制类型和正确的大小。

如果您正在执行未定义的行为,动态内存分配是这些问题的真正温床,可能发生的未定义的事情之一是它将正常工作

这并不意味着你在做正确的事情,UB是应该避免的事情,因为它可能在另一个系统、另一个编译器上,甚至在下周二下午3:05时起不同的作用:-

我要提到几件事:

您不需要在定义之前立即对原型进行所有这些函数声明。如果尚未声明定义,则定义充当声明。 您的exist函数可能会因空列表而崩溃,因为它会取消对top的引用。如果您的目的是检测非空列表,则可以使用return top!=无效的 您不应该在C中显式强制转换malloc的返回值,它可能会导致某些细微的错误。 事实上,在看最后一点的时候,你的错误并不是特别与铸造有关,只是它在那一行上:

struct Node* add = (struct Node*)malloc(sizeof(struct Node*));
//                                                        ^
//                                                      oops!
struct Node*的大小是指向结构的指针的大小,通常但不一定是四个或八个八位字节。如果您的实际结构有一个整数和一个指针,那么这个大小将不够大

它可以在一些提供最小动态内存分配的系统上工作,但绝对不建议这样做。你应该做:

struct Node *add = malloc(sizeof(struct Node));
//                               ^^^^^^^^^^^
//                      size of the node, not pointer

该行已删除了演员阵容,并且大小正确。

Hmm,这是一个有助于思考的框架。在我编写代码之后,我在谷歌上搜索,看看其他人是如何做到这一点的,并在一个教程中找到了这个确切的实现。因此,我不确定我和教程出错的可能性有多大,但我不会完全排除这种可能性。谢谢!未定义行为的概念对我来说是新的,我很欣赏这种解释。看来我又把自己和指针搞混了。关于你的答复有一个问题。您说过:不应该在C中显式强制转换malloc的返回值,这可能会导致某些细微的错误;添加=结构节点*mallocsizeofstruct节点,而不是:结构节点*添加=结构节点*mallocsizeofstruct节点*@Joshua,add的初始化与声明相反,然后分配就可以了。我的意思是不要马上在马洛克的左边施法。我的最后一行显示:struct Node*add=mallocsizeofstruct Node;`。明白了,很抱歉在你的回复中没有注意到这一点。谢谢你的澄清。嗯,这是一个很有帮助的思考框架。在我写了代码之后,我
谷歌搜索查看其他人是如何做到这一点的,并在一个教程中找到了这个确切的实现。因此,我不确定我和教程出错的可能性有多大,但我不会完全排除这种可能性。谢谢!未定义行为的概念对我来说是新的,我很欣赏这种解释。看来我又把自己和指针搞混了。关于你的答复有一个问题。您说过:不应该在C中显式强制转换malloc的返回值,这可能会导致某些细微的错误;添加=结构节点*mallocsizeofstruct节点,而不是:结构节点*添加=结构节点*mallocsizeofstruct节点*@Joshua,add的初始化与声明相反,然后分配就可以了。我的意思是不要马上在马洛克的左边施法。我的最后一行显示:struct Node*add=mallocsizeofstruct Node;`。明白了,很抱歉在你的回复中没有注意到这一点。感谢您的澄清。这是未定义行为意外改变的典型症状。通常由内存损坏引起。mallocsizeofstruct Node*应该是mallocsizeofstruct Node*更好。mallocsizeof*添加错误消息:Debug error!程序:C:\Users。。。检测到我的文件路径堆损坏:在0x011058C8的正常块78之后。CRT检测到应用程序在堆缓冲区结束后写入内存。写入的代码超过了动态分配的内存缓冲区的末尾。修复一个问题,其余的问题应该非常容易将函数原型放在代码中第一个函数签名之前。当函数不带参数时,原型应该在参数之间有void,否则编译器将生成允许任意数量参数的代码。@user3629249感谢您指出这一点,我实际上从来都不知道。发布的代码调用推送了几次,每次调用都分配了更多的动态内存。对pop的调用会将当前“top”分配的内存指针传递到free,但是,要推送的调用比pop的调用多,因此当程序退出时会出现内存泄漏,这是未定义行为的典型症状意外更改。通常由内存损坏引起。mallocsizeofstruct Node*应该是mallocsizeofstruct Node*更好。mallocsizeof*添加错误消息:Debug error!程序:C:\Users。。。检测到我的文件路径堆损坏:在0x011058C8的正常块78之后。CRT检测到应用程序在堆缓冲区结束后写入内存。写入的代码超过了动态分配的内存缓冲区的末尾。修复一个问题,其余的问题应该非常容易将函数原型放在代码中第一个函数签名之前。当函数不带参数时,原型应该在参数之间有void,否则编译器将生成允许任意数量参数的代码。@user3629249感谢您指出这一点,我实际上从来都不知道。发布的代码调用推送了几次,每次调用都分配了更多的动态内存。对pop的调用会将当前“top”分配的内存指针传递到free,但是,要推送的调用比pop的调用多,因此程序退出时会出现内存泄漏