警告:函数返回局部变量的地址[默认启用]

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

char *chktype(char *Buffer, int Size)
{
   char *strng = "Content-Type: ";
   int sz;
   char *found = strstr (Buffer, strng);
   char *found1 = strstr(found, "\r\n");
   sz=strlen(found)-strlen(found1);
   char type[sz];
   strncpy(type, found1, sz-1);

   return(type);
}

void main(){

   char *buffer = "HTTP/1.1 200 OK\r\nDate: Tue, 25 Jun 2013 16:27:16
   GMT\r\nExpires: -1\r\nCache-Control: private,
   max-age=0\r\nContent-Type: text/html; 
   charset=UTF-8\r\nContent-Encoding: gzip\r\nServer: 
   gws\r\nX-XSS-Protection: 1; mode=block\r\nX-Frame-Options:
   SAMEORIGIN\r\nTransfer-Encoding: chunked\r\n\r\n";

   char *extension = chktype (buffer, sizeof(buffer));
   printf("%s\r\n", extension);
}

这将产生:
warning: function returns address of local variable [enabled by 
default]

我无法确定这里有什么问题。当我运行它时,我期望的输出是text/html; charset=UTF-8,但实际上是无意义的字符。

这个警告具体是什么意思?


1
不应该从函数中返回局部数组。一种方法是声明它们为静态变量,这将使它们在整个执行期间保持存在。另一种方法是使用 malloc 分配它们。 - Nobilis
3
你究竟期望什么?你的代码太糟糕了,没人能读懂,我相信甚至你自己也看不懂。对其进行格式化。 - user529758
另外,回滚到v1。没有人想要那些“??”。我们已经有足够多了。 - user529758
4个回答

16

chktype函数在栈上为一个自动变量分配内存,然后返回该变量的地址(即指向该变量的指针)。

问题在于,在栈上分配的变量会在超出作用域时被自动销毁(即控制权跳出定义函数的花括号之外)。

这意味着您实际上是返回到无效内存位置的指针,这是不好的消息。在C语言中,这称为未定义行为。在实际应用中,它会导致错误的输出或可能甚至会崩溃。

char *chktype(char *Buffer, int Size)
{
    // This pointer variable is allocated on the stack, but that's okay because
    // it's a pointer to a string literal, which are always constant.
    // (Technically, you should add the "const" qualifier to the declaration.)
    const char *strng = "Content-Type: ";

    int sz;
    char *found = strstr (Buffer, strng);
    char *found1 = strstr(found, "\r\n");
    sz=strlen(found)-strlen(found1);

    // Like all the above variables, the one is also allocated on the stack.
    // But it's the source of your problem here, because it's the one that
    // you are returning at the end of the function.
    // Problem is, it goes away at the end of the function!
    char type[sz];
    strncpy(type, found1, sz-1);
    return(type);
}

从函数中返回char*的正确方法是使用malloc(或calloc)函数从堆上分配新的内存。这意味着该函数的调用者将负责释放返回值所使用的内存,否则您的程序将泄漏内存。
(始终将此要求记录在您的函数文档中!即使“文档”只是声明上面的注释。)

例如,将代码更改为以下形式:

char *chktype(char *Buffer, int Size)
{
    // This pointer variable is allocated on the stack, but that's okay because
    // it's a pointer to a string literal, which are always constant.
    // (Technically, you should add the "const" qualifier to the declaration.)
    const char *strng = "Content-Type: ";

    int sz;
    char *found = strstr (Buffer, strng);
    char *found1 = strstr(found, "\r\n");
    sz=strlen(found)-strlen(found1);

    char *type = malloc(sz);  // allocate memory from the heap
    strncpy(type, found1, sz-1);
    return(type);
}

现在,在 chktype 函数的调用方中,您必须确保在使用其返回值后调用 free

char *type = chktype(...);
// do something
free(type);

请注意,健壮的代码应该测试malloc函数的结果是否为null指针,以确保它未能成功分配所请求的内存。如果是这样,您需要以某种方式处理错误。出于清晰起见,上面没有展示这一点。

谢谢。我现在知道我做错了什么。我完成了那部分,现在我得到了一个内容头作为字符串的输出。这是输出结果:'Content-Type: text/html; charset=UTF-8'。现在我该如何从字符串中删除前17个字符?我想要从字符串中删除 'Content-Type: ' 或者复制从第18个字符开始的字符串。我该如何实现呢? - aDi Adam
@aDiAdam 嗯,像你以前做的那样使用 strncpy 函数。从第18个字符开始复制。你会得到一个新字符串,它不包含原始字符串的前17个字符。在 C 中不能原地删除它们。在低级语言中处理字符串非常麻烦,这就是为什么人们使用 C++,它提供了内置的 string 类。 - Cody Gray
有没有其他办法可以在原始函数的范围内保持malloc-free的责任?封装等...? - Gewure

8
快速/简单的回答(?):
做以下操作:
char type[sz];

转换为

static char type[sz];

长答案:错误非常明显,您正在返回一个变量的地址,该变量在函数返回时会很快被销毁。有几种方法可以解决这个问题。

一种简单的方法是将类型设置为static,这将通过使类型变量具有程序的生命周期来修复问题,但这意味着您不能连续调用它两次,您需要在再次调用之前打印或复制结果。

另一种方法是在函数内部为char数组分配内存,并希望您在使用完它后记得释放它。如果您没有释放,就会出现内存泄漏。这种方法不会受到上述缺点的影响。


我觉得这不可能,因为我正在使用一个可变长度的数组,不是吗? - aDi Adam
@aDiAdam 哦,是的,我没有注意到那个.. 你是对的。 - Karthik T

2

当您将type声明为char type[sz]时,这会给您一个局部变量。该内存的生命周期将在函数返回时结束。相反,您需要动态分配内存,例如使用malloc

char *type = (char *) malloc (sz * sizeof (char));

3
拜托不要这样做! - user529758
此外,“给你一个本地变量”并不是问题。KarthikT的解决方案也使用了本地变量,但它是正确的。问题在于它具有自动存储期限。 - user529758

0

你返回了一个指向在堆栈上分配并在函数 chktype() 返回后无效的数组的 type 指针。

你可能想要在堆上分配结果,像这样:

char * chktype(const char * buffer, int size)  
{
  char * strng = "Content-Type: ";
  char * found = strstr (buffer, strng);
  char * found1 = strstr(found, "\r\n");
  size_t sz = strlen(found) - strlen(found1);
  char * type = calloc(sz, sizeof(*type));
  if (type)
  {
    strncpy(type, found1, sz - 1);
  }

  return type;
}

然而,当不再需要结果时,需要使用free()释放内存。

好的,我使用了malloc函数并且它起作用了,现在type变量包含了"Content-Type: text/html; charset=UTF-8"这个字符串。我想要从这个字符串中删除前17个字符,我该怎么做呢? - aDi Adam
@aDiAdam:如果字符串长度少于17个字符,该怎么办? - alk
@aDiAdam: {char * type = chkType(...); size_t n = MIN(17, strlen(type)); memmove(type, type + n * sizeof(*type), n * sizeof(*type));} - alk
不,我需要的是一个字符串“text/html; charset=UTF-8”,这样我就可以从该字符串中复制第18个字符到另一个字符串中。或者我可以从同一字符串中删除“Content-Type: ”。很抱歉,我对字符串库函数有点生疏。您能否详细说明一下您编写的memove()部分?我将非常感激。 - aDi Adam
strlen(type) 后面我漏掉了一个 )。更多详情请参阅 man memmove - alk
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接