C 我的内存管理哪里出了问题?

C 我的内存管理哪里出了问题?,c,memory-management,ecobee,C,Memory Management,Ecobee,我正在用C语言制作一个小应用程序,它应该连接到ecobee API。暂时忽略这是愚蠢的/糟糕的/为什么你要使用C作为RESTAPI客户端,这是一个有趣的个人项目 我目前在内存管理方面遇到了问题。我已经用注释注释了提供的要点,并删掉了我认为不相关的代码。基本上,在我到达getTokens之前,该应用程序完全按照预期工作。然后下面的代码让人抓狂: struct authorizations getTokens(char* apiKey, char* authCode) { char* body

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

我目前在内存管理方面遇到了问题。我已经用注释注释了提供的要点,并删掉了我认为不相关的代码。基本上,在我到达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已经被释放。我想我的
realloc
s有一个坏了还是怎么了

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

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

好的,这里有一个主要问题:

s1 = realloc(s1, strlen(temp) + strlen(s2) + 1);
这可能会更改
s1
的值,一旦退出函数,该值将立即丢失。您没有将这个新指针值返回给调用者,因此它使用的是旧的、现在无效的指针值。因此,损失

您需要将更改后的
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);
好吧,你这里有个大问题:

s1 = realloc(s1, strlen(temp) + strlen(s2) + 1);
这可能会更改
s1
的值,一旦退出函数,该值将立即丢失。您没有将这个新指针值返回给调用者,因此它使用的是旧的、现在无效的指针值。因此,损失

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

正如@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
ed大小;任何添加的内存都将被取消初始化),因此您根本不需要使用
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);
}

正如@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
ed大小;任何添加的内存都将被取消初始化),因此您根本不需要使用
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);
}

“请参阅concat和concat2功能的要点。”-请参阅了解此网站的工作原理以及您希望提供的内容。简言之:如果该网站离线怎么办?
concat2
有问题。将其更改为返回类似于
body=concat2(body,“&client_id=”)的值
您需要仔细考虑
realloc
返回值这一事实。为什么需要这个值?难道我们不能只说
realloc(ptr,newsize)
就可以了吗?现在当你回答了这个问题