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),这会终止应用程序,因为显然body已经被释放。我想我的一个电脑坏了?
最让我困惑的是,当我两天前处理坏数据时(对api进行无效调用并从错误中提取信息来填充请求——当时我没有设置登录),一切都正常。一旦我开始获取真实数据,应用程序就开始“堆栈崩溃”。这是我昨晚能推到的极限。
此外,任何关于我在字符串连接和指针操作方面哪里出错的一般建议都是受欢迎的假设我已经听说了为什么不应该使用C作为REST API客户端

最佳答案

正如@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);
}

关于c - 我的内存管理哪里出问题了?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43746393/

10-11 21:24