为什么我会收到错误“Process finished with exit code -1073741819 (0xC0000005) in my code”,但如果我添加不相关的 print 语句,它仍然有效?

Why do I get the error "Process finished with exit code -1073741819 (0xC0000005) in my code", but it still works if I add a unrelated print statement?

提问人:Anton Sch9nfeld 提问时间:10/20/2023 最后编辑:chqrlieAnton Sch9nfeld 更新时间:10/21/2023 访问量:72

问:

我还是 C 语言的新手,所以我不知道这里会发生什么。

在这个应该以面向对象的方式运行的结构体的实现中,当我得到这个诊断时,函数中似乎有一些错误:Stringset

Process finished with exit code -1073741819 (0xC0000005)

这是我的 .h 文件:

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

typedef struct String_Struct
{
    char* value;
    unsigned int length;

    void (*set) (struct String_Struct* self, char* value);
    bool (*equals) (const struct String_Struct* self, const struct String_Struct* other);
    int (*compareTo) (const struct String_Struct* self, const struct String_Struct* other);
    void (*concat) (struct String_Struct* self, const struct String_Struct* other);
    void (*freeString) (struct String_Struct* self);
} String;

String* newString (char* value);
void set (String* self, char* value);
bool equals (const String* self, const String* other);
int compareTo (const String* self, const String* other);
void concat (String* self, const String* other);
void freeString (String* self);

这是它的实现:

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

void set (String* self, char* value)
{
    // Only proceed if self and value exist
    if (!self || !value)
    {
        return;
    }

    free(self->value);
    self->length = strlen(value) + 1;
    self->value = (char*) malloc(sizeof(char) * self->length);
    strcpy(self->value, value);
}

int compareTo (const String* self, const String* other)
{
    if ((!self && other) || (self && !other))
        return INT_MIN;

    return strcmp(self->value, other->value);
}

bool equals (const String* self, const String* other)
{
    if ((!self && other) || (self && !other))
        return false;

    if (self == other)
        return true;

    return strcmp(self->value, other->value) == 0;
}

void concat (String* self, const String* other)
{
    if (!self || !other)
    {
        return;
    }

    char* result = (char*) malloc(sizeof(char) * (strlen(self->value) + strlen(other->value) + 2));

    strcpy(result, self->value);
    strcat(result, other->value);

    self->set(self, result);
}

void freeString (String* self)
{
    free(self->value);
    free(self);
}

String* newString (char* value)
{
    String* str = (String*) malloc(sizeof(String));

    str->set = &set;
    str->equals = &equals;
    str->compareTo = &compareTo;
    str->concat = &concat;
    str->freeString = &freeString;

    str->set(str, value);

    return str;
}

这是我的main.c:

#include <stdio.h>
#include "mystring.h"

int main()
{
    String* string = newString("Hello");

    printf("%s", string->value);

    return 0;
}

当我运行此代码时,我收到上面提供的错误。但是当我在“set”中添加一个不相关的 printf 语句时,如下所示:

void set (String* self, char* value)
{
    // Only proceed if self and value exist
    if (!self || !value)
    {
        return;
    }

    printf("Debug");

    free(self->value);
    self->length = strlen(value) + 1;
    self->value = (char*) malloc(sizeof(char) * self->length);
    strcpy(self->value, value);
}

这是控制台输出: 调试你好 进程完成,退出代码 -1073741819 (0xC0000005)

谁能解释一下为什么?

C 字符串 结构 体内存泄漏 IO

评论

3赞 Fe2O3 10/20/2023
newString (char* value)调用,其第一个操作是调用 。可悲的是,因为没有初始化分配的块,所以包含传递给...解决方案:使用而不是...钥匙我会放弃这个并做其他事情......C 语言不是一种面向对象的语言。像这样的笨拙只会让代码更加臃肿和复杂......没有提供“析构函数”,所以肯定会有内存泄漏......set()free()...->valuemalloc()valuefree()calloc()malloc()
0赞 Jean-Baptiste Yunès 10/20/2023
@Fe2O3 可悲的是,由于 malloc() 没有初始化分配的块,因此 value 包含传递给 free() 的垃圾......害羞分配的内存的内容与此无关,地址管理它。
0赞 Gerhardh 10/20/2023
@Jean-BaptisteYunès 分配的内存的内容被传递给函数中调用时。这不是关于,而是关于内在的指针'freefree(self->value);setselfvalue
1赞 Jean-Baptiste Yunès 10/20/2023
哦,我明白了,原谅我(专注于功能)。问题出在必须设置为 的地方。setnewStringvalueNULL
1赞 BoP 10/20/2023
@Anton - 有关“C 不是面向对象语言”的更多信息 与C++相比,通过结构中的函数指针调用将为您提供额外的间接性。并且包含其他优化,例如如果缓冲区已经足够长,则不要重新分配缓冲区。大多数使用 C 语言的人这样做是因为他们不想要 OOP。你似乎犹豫不决。:-)std::stringset

答:

2赞 Jabberwocky 10/20/2023 #1

函数中至少有一个问题:set

  free(self->value);                 //  you free a pointer that has 
  self->length = strlen(value) + 1;  //  never been initialized
                                    

释放从未初始化的指针没有意义,并且会导致未定义的行为(大多数情况下是某种崩溃)。

您应该在以下位置初始化指向 NULL 的指针:newString

  ...
  str->freeString = &freeString;
  str->value = NULL;  // <<< add this

  str->set(str, value);
  ...

通过该修改,您的代码似乎可以工作。我没有进一步调查,因此此代码中的其他地方可能会有更多问题(错误和/或设计错误)。

评论

2赞 Jabberwocky 10/20/2023
@Fe2O3这似乎没有必要,但绝对是这里的一个选择。但无论如何,整个事情看起来或多或少像一个大的设计错误,很有可能出现内存泄漏。calloc
1赞 Fe2O3 10/20/2023 #2

OP标题:“......但是如果我添加一个不相关的打印语句,它仍然有效?和“谁能解释一下为什么?"

printf()对其功能使用动态内存分配。

引入“(无关的)打印语句”会改变环境(可能是在执行期间第一次使用动态分配)。因此,程序的非工作版本正在访问的任何字节都会被替换或重定向到包含足够 NUL 字符的其他字节,这些字节可以解释为 ,这是函数的完全合法参数。free(NULL)

教训:在堆栈上中分配变量(尤其是变量)时,请确保完全初始化变量。这可能看起来很浪费,但这种做法将为您节省数小时来追逐行为不端程序中难以捉摸的错误(并在 SO 上发布问题)。struct

此外:可能会失败。养成编写代码的习惯,这些代码会检查此类调用的返回值。假设一切正常,这被称为“在黑暗中吹口哨”。malloc()"