我的内存管理在哪里出错了?

时间:2017-05-02 20:09:14

标签: c memory-management ecobee

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

我在C中创建了一个应用程序,它应该连接到ecobee API。暂时忽略这是愚蠢/可怕/为什么上帝你使用C作为REST API客户端,它是个人项目的乐趣。

我目前遇到内存管理问题。我已经用提供的评论注释了所提供的要点,并删除了我认为不相关的代码。基本上,应用程序按预期工作完全,直到我到达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),它杀死了应用程序,因为显然已经释放了身体。我想我的realloc之一还没有工作?

最令我困惑的是,当我两天前处理坏数据时(对api进行无效调用,只是从错误中提取信息以填充未来的请求 - 我没有登录集那时候,一切都很好。一旦我开始获得真实数据,应用程序就开始了#34;堆栈粉碎"。这是我昨晚能够推动的。

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

2 个答案:

答案 0 :(得分:3)

正如@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 已经将缓冲区的先前内容复制到新缓冲区(最大为malloc个大小;任何添加的内存都将是未初始化),因此您根本不需要使用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);
}

答案 1 :(得分:2)

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

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

这可能会改变s1的值,一旦退出函数就会立即丢失。{em>您不会将此新指针值返回给调用者,因此它使用旧的,现在无效指针值。因此造成了损失。

您需要让调用者可以使用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);