【问题标题】:Where am I going wrong with my memory management?我的内存管理哪里出了问题?
【发布时间】:2017-09-30 11:30:12
【问题描述】:

https://gist.github.com/macarr/b49c0097666a613f639c4eab931f31d4

我正在用 C 语言制作一个小应用程序,它应该连接到 ecobee API。暂时忽略这是愚蠢的/可怕的/为什么上帝使用 C 作为 REST API 客户端,这是一个有趣的个人项目。

我目前遇到了内存管理问题。我已经用 cmets 注释了提供的要点,并剪掉了我认为不相关的代码。基本上,在我到达 getTokens 之前,该应用程序完全按预期工作。然后下面的代码吓坏了:

struct authorizations getTokens(char* apiKey, char* authCode) {
  char* body = "grant_type=ecobeePin&code=";
  body = concat(body, authCode);
  printf("%s!!!!!!!!!!\n", body); //as expected
  concat2(body, "&client_id=");
  printf("%s!!!!!!!!!!\n", body); //as expected
  concat2(body, apiKey);
  printf("%s!!!!!!!!!!\n", body); //garbage
  ...

concat 和 concat2 的功能见要点。

char* concat(const char *s1, const char *s2)
{
    char* result;
    result = malloc(strlen(s1)+strlen(s2)+1);//+1 for the zero-terminator
    if(!result) {
        exit(1);
    }
    strcpy(result, s1);
    strcat(result, s2);
    printf("CONCAT1: %s\n", result);
    return result;
}

void concat2(char *s1, const char *s2) {
    char temp[strlen(s1)];
    strcpy(temp, s1);
    printf("%s:%s\n", temp, s2);
    s1 = realloc(s1, strlen(temp) + strlen(s2) + 1);//+1 for the null terminator
    if(!s1) {
        exit(1);
    }
    strcpy(s1, temp);
    strcat(s1, s2);
    printf("CONCAT2: %s\n", s1);
}

在我的函数结束时,我 free(body),这会杀死应用程序,因为显然 body 已被释放。我猜我的一个reallocs 不工作还是什么?

最让我困惑的是,当我两天前处理不良数据时(对 api 进行无效调用,只是从错误中提取信息以填充未来的请求 - 当时我没有设置登录),一切正常。一旦我开始获取真实数据,应用程序就开始“堆栈粉碎”。这是我昨晚能推的。

此外,任何关于我在字符串连接和指针操作方面出错的一般建议都将受到欢迎。假设我已经听说过为什么我不应该使用 C 作为 REST API 客户端

【问题讨论】:

  • "concat 和 concat2 的功能见要点。" - 请参阅How to Ask,了解本网站的运作方式以及您需要提供的内容。简而言之:如果该网站离线怎么办?
  • concat2 有问题。将其更改为返回类似 body = concat2(body, "&client_id="); 的值
  • 您需要仔细考虑 realloc 返回值这一事实。为什么需要这个值?难道我们不能直接说realloc(ptr, newsize) 就可以了?现在,当您回答完这个问题后,请查看concat2
  • 请在您的问题中发布concatconcat2 的正文 - 我无法访问提供的链接。
  • 还有char temp[strlen(s1)]; off-by-one 错误 --> char temp[strlen(s1)+1]; 注意:realloc 保存原始内容。

标签: c memory-management ecobee


【解决方案1】:

正如@n.m 所指出的。还有@BLUEPIXY,concat2 存在严重问题。假设您不想再花时间自己弄清楚,剧透如下......

主要问题是concat2 尽职尽责地获取s1 指向的缓冲区,重新分配它以确保它足够大以进行连接,并将指向新调整大小的缓冲区的指针存储在s1 中。然后它将字符串连接到该新缓冲区中,然后 - 当函数结束时 - 它会将 s1 中最重要的新指针值扔掉。在第一次调用concat2 之后,你的getTokens 函数被错误地认为缓冲区仍然位于body,但位置很可能已经改变了!

(取决于分配器在您的特定平台上的工作方式,它可能会或很多不会实际上改变,具体取决于新旧大小和分配器详细信息,但您需要假设它可能已经改变.) 所以,如果你将concat2 重写为:

char* concat2(char *s1, const char *s2) {
    char temp[strlen(s1)];
    strcpy(temp, s1);
    s1 = realloc(s1, strlen(temp) + strlen(s2) + 1);//+1 for the null terminator
    if(!s1) {
        exit(1);
    }
    strcpy(s1, temp);
    strcat(s1, s2);
    return(s1);  /* IMPORTANT: new buffer location! */ 
}

并修改您的 concat2 调用,使其看起来像:

body = concat2(body, "&client_id=");
...
body = concat2(body, apiKey);

你会发现事情做得更好。

还有一点需要注意:realloc已经将缓冲区的先前内容复制到新缓冲区(直到其最初的 malloced 大小;任何添加的内存都将被取消初始化),所以您根本不需要使用temp 缓冲区和额外的复制,concat2 可以简化为:

char* concat2(char *s1, const char *s2) {
    s1 = realloc(s1, strlen(s1) + strlen(s2) + 1);//+1 for the null terminator
    if(!s1) {
        exit(1);
    }
    /* now s1 points to the original string at the beginning
     * of a sufficiently large buffer
     */
    strcat(s1, s2);
    return(s1);
}

【讨论】:

  • char temp[strlen(s1)]; strcpy(temp, s1); 是 UB,因为temp[] 太小了。
  • 谢谢,效果很好!我想我现在总体上对指针有了更好的处理,非常感谢。
【解决方案2】:

好的,你有一个主要问题:

s1 = realloc(s1, strlen(temp) + strlen(s2) + 1);

这可能会更改s1 的值,在您退出函数后会立即丢失。您不会将此新指针值返回给调用者,因此它使用旧的,现在 invalid 指针值。因此损失了。

您需要使s1 的更改值可供调用者使用,这意味着您必须传递一个指向它的指针:

void concat2(char **s1, const char *s2) {
    /**
     * You do not need to preserve the contents of s1 here - if successful,
     * realloc will copy the contents of s1 to the new memory; if not, 
     * it will leave the existing contents in place.  
     * 
     * Because realloc can return NULL on failure, you should *not*
     * assign the result back to the original pointer, but instead
     * assign it to a temporary; that way, if realloc does fail, you
     * don't lose the reference to the previously allocated memory
     */
    char *tmp = realloc(*s1, strlen(*s1) + strlen(s2) + 1);//+1 for the null terminator
    if(!tmp) {
        // handle realloc error
    }
    *s1 = tmp;
    strcat(*s1, s2);
    printf("CONCAT2: %s\n", *s1);
}

然后你可以称之为

concat2(&body, "&client_id=");
...
concat2(&body, apiKey);

【讨论】:

    猜你喜欢
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    • 2016-09-18
    • 1970-01-01
    相关资源
    最近更新 更多