【问题标题】:Where's my pointer error?我的指针错误在哪里?
【发布时间】:2016-05-22 12:14:17
【问题描述】:

我浏览了以下代码,但找不到问题所在。函数getsxnremem() 使用fgets() 获取最多为len 字符的字符串,用空终止符覆盖换行符(如果有的话),然后重新调整内存大小以适应字符串。反正就是这个想法。

以下代码有时有效,有时会崩溃。过去我已经发生过很多次这种情况,我通常会发现问题,但这次我花了太长时间。

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

unsigned getsxnremem(char **str, unsigned len){
    unsigned l, flag = 1;
    free(*str);
    char *buff;
    if ((*str = malloc(len)) == NULL) return 0;
    if(fgets(*str, len, stdin) == NULL) { free(*str); return 0; }
    l = strlen(*str);
    if (l && ((*str)[l-1] == '\n')) { *(str)[l-1] = '\0'; flag = 0; }
    if ((buff = realloc(*str, l + flag)) == NULL){ free(*str); return 0; }
    *str = buff;
    return (l - 1);
}


int main(void){
    char *buff = NULL;
    unsigned l = getsxnremem(&buff, 256);
    printf("%s\n%u chars long.", buff, l);
}

【问题讨论】:

  • 您应该检查来自fgets() 的返回值。如果它得到 EOF,则可能没有有效的字符串来运行 strlen()malloc() 返回的内存不能保证是一个以 null 结尾的字符串。这是对不从realloc() 收集返回值的问题的改进,尽管当正在使用的空间缩小时重新分配分配将是一个不寻常的实现。
  • 如果字符串没有以新行结尾,你重新分配到它的字符串长度。那是空终止符短 1 个字节,将返回错误的长度,并且 printf 可能会读取分配的内存并输出垃圾!添加else ++l;修复
  • @king_nak 你是对的。我更新了代码来处理这个问题。主要错误仍然存​​在。谢谢!
  • @JonathanLeffler 谢谢。我更新了代码以检查 fgets() 的返回值并使用 realloc() 的返回值。不幸的是,问题仍然存在。

标签: c realloc


【解决方案1】:

问题是,你没有收集到那里realloc()的返回值。

根据C11 标准,第 7.22.3.5 章

#include &lt;stdlib.h&gt;
void *realloc(void *ptr, size_t size);

realloc 函数释放ptr 指向的旧对象并返回一个 指向具有size 指定大小的新对象的指针。 [...]

realloc() 调整内存大小并返回一个指向新内存的指针。旧记忆是free()d,考虑到realloc()是成功的。

所以,

  1. 您需要收集并检查realloc() 的返回值,并针对NULL 进行测试以确保成功。然后,将其重新分配给*str

    注意:请不要使用p = realloc(p, newsize); 这样的形式,因为如果realloc() 失败,您最终也会丢失实际的指针。

  2. 如果realloc() 成功,则不能free() old 指针。在已经 free()-d 内存上调用 free() 会调用 undefined behavior

之后,正如dbushother answer 中正确提到的那样,用法

 { *(str)[l-1] = '\0'; flag = 0; }

也是错误的。您所需的字符串*str 表示,而不是str。根据operator precedence,数组下标运算符 ([]) 的优先级高于取消引用 (*) 运算符,因此基本上您的代码看起来像

{ * ((str)[l-1]) = '\0'; flag = 0; }

这不是你想要的。所以,为了尊重operator precedence,你应该像这样修改它

{ (*str)[l-1] = '\0'; flag = 0; }

也就是说,在使用目标缓冲区之前,您还应该检查fgets() 的返回值以确保成功。由于malloc() 返回未初始化的内存,如果fgets() 失败,您最终将读取未初始化的内存,这将再次导致 UB。

【讨论】:

  • 这可能看起来微不足道,但“调整大小”可能涉及移动分配。提及移动的想法可能有助于 OP 理解和想象为什么涉及不同的地址。
  • 我已经根据您的建议更新了代码:现在,如果 realloc 成功,我将 realloc() 的返回值分配给字符串。我还添加了对 fgets() 的返回值的检查。不幸的是,问题仍然存在。
  • @KorganRivera 我已经更新了我的答案来解决这个问题。也感谢 dbush。 :)
【解决方案2】:

对于您最近的更新,您的括号放错了位置。

这个:

if (l && ((*str)[l-1] == '\n')) { *(str)[l-1] = '\0'; flag = 0; }

应该是:

if (l && ((*str)[l-1] == '\n')) { (*str)[l-1] = '\0'; flag = 0; }
                                   ^---- here

【讨论】:

    猜你喜欢
    • 2020-05-02
    • 2013-09-06
    • 1970-01-01
    • 2015-03-11
    • 2019-08-03
    • 2014-05-04
    • 1970-01-01
    • 1970-01-01
    • 2020-07-14
    相关资源
    最近更新 更多