在函数中返回 char*
Posted
技术标签:
【中文标题】在函数中返回 char*【英文标题】:Returning char* in function 【发布时间】:2011-01-27 09:17:07 【问题描述】:我有功能:
char *zap(char *ar)
char pie[100] = "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '";
char dru[] = "' )";
strcat(pie, ar);
strcat(pie, dru);
return pie;
主要有:
printf("%s", zap( argv[1] ) );
编译时出现警告:
test.c: In function ‘zap’:
test.c:17: warning: function returns address of local variable
我应该如何正确返回 char*?
【问题讨论】:
您似乎正在与数据库进行交互。你必须使用纯C吗?此外,大多数数据库都有 API 来安全地构造 SQL 语句。你能用这些吗? 谢谢,是的 - 我使用 mysql C API,但我必须创建一个字符串用作查询,因为我不能在查询本身中使用变量。 【参考方案1】:用malloc
为pie
分配内存
【讨论】:
正如你提到的malloc,你能帮我如何为这个函数写一个合适的malloc吗?【参考方案2】:您最好的选择可能是根本不返回它 - 而是将要填充的缓冲区作为参数传递给函数。
void zap(char * pie, const char *ar)
strcpy( pie, "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '");
char dru[] = "' )";
strcat(pie, ar);
strcat(pie, dru);
然后这样称呼它:
char pie[100];
zap( pie, "foo" );
要对这个函数进行防弹,你还需要传入缓冲区的长度,然后在每次要添加新的查询元素时进行检查。
【讨论】:
缓冲区溢出问好 如果这段代码中存在缓冲区溢出,那么原始代码中也会存在。我试图说明一个概念,而不是编写完美的代码。 很公平。你仍然可以使用 strncpy 编写更好的代码。顺便说一句,你在第 2 行缺少一个括号;)【参考方案3】:char pie[100];
void zap(char* pie, char *ar)
char pies[100] = "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '";
char dru[] = "' )";
strcpy(pie, pies);
strcat(pie, ar);
strcat(pie, dru);
zap(pie, argv[1]);
printf("%s", pie );
【讨论】:
【参考方案4】:我强烈建议更改此函数,让用户同时传递一个缓冲区和一个长度,并改用该缓冲区。或者,您可以分配返回值的新实例,即使用malloc
,但请确保给用户留下评论以再次释放它。
【讨论】:
【参考方案5】:声明你的 char 数组是静态的
static char pie[100] = "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '";
【讨论】:
多线程冲突问好,缓冲区溢出的可能性还是存在的。【参考方案6】:发布的解决方案都有效,但只是为了回答您为什么会收到警告的问题:
当您在函数中将 pie 声明为缓冲区时,您并没有分配堆内存,而是在堆栈中创建变量。该内存内容仅在该功能范围内得到保证。一旦你离开函数(在返回之后),内存可以被重用于任何事情,你可以发现你指向的内存地址随时被覆盖。因此,您会被警告您正在返回一个指向内存的指针,该指针不能保证保留。
如果你想在一个 c 函数中分配持久内存,你可以在该函数之外引用,你需要使用 malloc(或其他风格的堆内存分配函数)。这将为堆上的该变量分配内存,并且它将一直存在,直到使用 free 函数释放内存为止。如果您不清楚堆栈与堆内存的区别,您可能需要 google 一下,这将使您的 C 体验更加流畅。
【讨论】:
这是对stack vs heap的一个很好的回答【参考方案7】: #include #include【讨论】:
没关系,但你通常不想使用 assert() 进行运行时错误检查... 我知道...但是很简单... :-/【参考方案8】:略有不同的方法:
void zap(char **stmt, char *argument, size_t *stmtBufLen)
char *fmt="INSERT INTO test(nazwa, liczba) VALUES ('nowy wpis', '%s')";
/**
* Is our current buffer size (stmtBufLen) big enough to hold the result string?
*/
size_t newStmtLen = strlen(fmt) + strlen(argument) - 2;
if (*stmtBufLen < newStmtLen)
/**
* No. Extend the buffer to accomodate the new statement length.
*/
char *tmp = realloc(*stmt, newStmtLen + 1);
if (tmp)
*stmt = tmp;
*stmtLen = newStmtLen+1;
else
/**
* For now, just write an error message to stderr; the statement
* buffer and statement length are left unchanged.
*/
fprintf(stderr, "realloc failed; stmt was not modified\n");
return;
/**
* Write statement with argument to buffer.
*/
sprintf(*stmt, fmt, argument);
int main(void)
char *stmtBuffer = NULL;
size_t stmtBufferLen = 0;
...
zap(&stmtBuffer, "foo", &stmtBufferLen);
...
zap(&stmtBuffer, "blurga", &stmtBufferLen);
...
zap(&stmtBuffer, "AReallyLongArgumentName", &stmtBufferLen);
...
zap(&stmtBuffer, "AnEvenLongerRidiculouslyLongArgumentName", &stmtBufferLen);
...
free(stmtBuffer);
return 0;
此版本使用动态内存分配来根据需要调整缓冲区大小,从 NULL 缓冲区指针 (realloc(NULL, size) == malloc(size)) 开始。这样您就不必担心从“足够大”的缓冲区开始。唯一的缺点是你需要记住在完成后释放缓冲区(我通常不喜欢像这样在调用者和被调用者之间划分内存管理职责;如果我考虑了超过 10 分钟,我d 想出更好的东西)。
【讨论】:
【参考方案9】:我进行此类操作的方式是使本地缓冲区成为静态线程特定变量:
const int max_pie_cnt = 100;
const char *zap(char *ar)
static __declspec(thread) char pie[max_pie_cnt]; // use TLS to store buffer
strcpy(pie, "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '");
char dru[] = "' )";
strcat(pie, ar);
strcat(pie, dru);
return pie;
我对专家的cmets很好奇。
顺便说一句,让我们暂时忘记缓冲区溢出问题。
【讨论】:
以上是关于在函数中返回 char*的主要内容,如果未能解决你的问题,请参考以下文章
Char* 在函数中使用 malloc 创建,编译器说地址在堆栈上,无法返回
为啥 char** 不能成为 C++ 中以下函数的返回类型?