【问题标题】:Freeing malloc will not erase char data释放 malloc 不会擦除 char 数据
【发布时间】:2011-11-06 12:13:31
【问题描述】:

我为我的大问题做了一个小场景。我尝试做的是将一个字符串传递给一个函数,该函数将使用它生成一个新字符串。但是我遇到了一些问题。

我已将字符串定义为

typedef char string[1024];

然后我有一个函数,它接受一个字符串并创建一个新字符串,该字符串被旧字符串和一个点字符填充

string* add_sth_to_string(char* msg){
    string* newStr=malloc(sizeof(string)); // malloc a newStr
    strcpy(*newStr, msg);            // copying msg to newStr

    char buff[1024];                 // making a buffer
    strcat(buff, ".");               // adding a dot to buffer
    strcat(buff, *newStr);           // adding msg   to buffer
    strcpy(*newStr, buff);           // copying buffer to newStr
    return newStr;
}

然后在 main 中,我每次尝试使用这个函数 3 次来获取一个新字符串:

for (i=0; i<3; i++){
    string* newStr;
    newStr=add_sth_to_string("test");
    printf("str: %s\n", *newStr);
    free(newStr);
    // can even printf here
}

这是我得到的奇怪输出:

str: .test
str: .test.test
str: .test.test.test

当我期望得到:

str: .test
str: .test
str: .test

谁能指出发生了什么?我觉得奇怪的另一件事是,我可以在 释放 newStr 之后打印它的值。

【问题讨论】:

  • 顺便说一句,我不认为typedef 是个好主意。隐藏数组甚至比隐藏指针更糟糕。
  • @Matteo 正如所说,这是我更大程序的一个较小版本。在实际程序中,它是一个typedef struct request,其中包含char URN[MAX_PATH]。只是试图让问题更容易阅读(这似乎也让我被否决了)。
  • 呃,对不起,那没关系;我倾向于指出这种错误,因为它们在初学者中很常见。 (顺便说一句,我不是反对者)

标签: c string malloc concatenation


【解决方案1】:

您正在使用buff 而不对其进行初始化。试试:

char buff[1024];
buff[0] = 0;

我觉得奇怪的是我可以打印 newStr 的值 就在我释放它之后。

释放内存后访问它是未定义的行为。通常,出于性能原因,free 不会将内存归零。

这是同一问题中的 2 个未定义行为案例 + 一个非常奇怪的 typedef。继续加油!

【讨论】:

    【解决方案2】:

    尝试改变

    strcat(buff, "."); 
    

    进入

    strcpy(buff, "."); 
    

    或者在声明 buff 时将其初始化为:

     char buff[1024] = "";
    

    【讨论】:

    • +1。这是真正的问题,因为缓冲区是未初始化的,因此在其上使用strcat 可能会产生随机的东西。
    • 初始化大字符串缓冲区通常是个坏主意,因为它不只是用空终止符初始化第一个字节;它将整个缓冲区填零,当您要覆盖它时,这是浪费时间和机器代码。在定义它未初始化之后在下一个like上写buf[0]=0;会更好。
    • @R. “...它零填充 整个缓冲区 ...”这是某种标准化(以 C 标准的方式)的行为吗?
    • @alk:是的。 C 没有部分初始化的对象。一个初始化的对象总是完全初始化的。 6.7.8:“如果大括号括起来的列表中的初始化程序少于聚合的元素或成员,或者用于初始化已知大小数组的字符串文字中的字符少于数组中的元素,则聚合的其余部分应隐式初始化,与具有静态存储持续时间的对象相同。”一个有用的副作用是{0}任何类型的通用全零初始化器。
    【解决方案3】:

    您应该在每次迭代时清除buf[1024] 的内容。


    更新

    因为buf[1024] 在堆栈上分配时不会自动归零。而你选择strcat 来连接两个字符串,这将找到一个\0-terminate。因此,如果buf 包含一些默认值,则会引入混淆输出。

    使用buf[1024] = ""; 分配缓冲区将更正输出。

    【讨论】:

    • 注意不要将你的初始化程序放在热循环中,它相当于memset(buf, 0, 1024);
    • @tristopia 有什么问题吗?如果我把它放在那里怎么办?对不起,我缺乏经验,谢谢。 :)
    • 这样做的问题是,它看起来像一个简单的指令,但成本很高。我见过大量使用这种初始化程序的代码,这对应用程序的性能有很大影响。我曾经有一个案例,其中 1/3 的 CPU 时间都花在了这些初始化程序上。
    • @tristopia 感谢分享。忘记memset 是昂贵的。 :)
    【解决方案4】:
    string* newStr=malloc(sizeof(string)); // malloc a newStr
    strcpy(*newStr, msg);            // copying msg to newStr
    

    这也会崩溃。 string 是一个指针,因此它的 sizeof 将返回 4 或 8,而不是您想要做的。

    好吧,忘记我的话,你做了一个 typedef,但我在这里告诉你为什么 typedef 是一个的想法。乍一看,它混淆了它是一个数组而不是指针的事实,在一个 30 行的程序上它不是问题,但是当你必须维护一个 200 000 行的项目时(就像我一样),你会开始讨厌这些之类的东西。

    另外一点,您应该避免使用固定大小的 1024 字节字符串。 1024 并不是那么大(即使是 80 年代的家用计算机也有比这更大的屏幕),而且对于大多数相当短的字符串来说,你会白白浪费大量内存。

    【讨论】:

    • 我认为 99.99% 的 typedef 是个坏主意。他们通常在每个声明中保存 5 或 6 个字符,这对于他们施加的混淆几乎没有好处。
    • 我不会在百分比上走那么高,typedef 有合法用途。例如,创建具有整数类型语义的别名,HWNDtime_t 都是整数,但具有附加语义。不可原谅的(恕我直言)是 typedef 隐藏了对象的性质(指针、数组、函数)。 Windows 的LPSTR 就是这样的例子。这也是我不喜欢 C++ 中的引用的原因。
    猜你喜欢
    • 1970-01-01
    • 2018-09-16
    • 2011-06-19
    • 2018-10-16
    • 2012-09-14
    • 2014-01-25
    • 2021-08-29
    • 2012-02-07
    • 1970-01-01
    相关资源
    最近更新 更多