- 已关闭**。此问题需要超过focused。当前不接受答案。
- 想要改进此问题吗?**更新此问题,使其仅关注editing this post的一个问题。
7天前关闭。
Improve this question
在C语言中最痛苦的工作之一就是找出最好和最有效的处理字符串的方法。我已经搜索和尝试了将近一天了。不管怎么说,基本上,我试图创建一个动态字符串构建器函数,所以我得到了以下结果:
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
void *alloc(void **ptr, size_t len) {
free(*ptr);
void *mem = malloc(len);
if (mem == NULL) {
fprintf(stderr, "err: alloc failed");
exit(EXIT_FAILURE);
}
return mem;
}
char *str(char **src, const char *fmt, ...) {
if (fmt == NULL) {
return NULL;
}
va_list args;
va_start(args, fmt);
size_t len = vsnprintf(NULL, 0, fmt, args);
if (len < 0) {
return NULL;
}
*src = alloc((void **) src, sizeof(char) * (len + 1));
vsnprintf(*src, len + 1, fmt, args);
va_end(args);
return *src;
}
以下是我目前的使用方法:
int main(int argc, char *argv[]) {
char *s = str(&s, "FOUND!");
puts(s);
puts(str(&s, "NOT %s", s));
puts(s);
return 0;
}
当它确实起作用时,我在想这样的事情:
- 可能的内存泄漏。
- 以及我在动态内存分配方面做错的任何事情。
- 以及如何用
str(&pointer, "value")
初始化char *
(传递未初始化的&pointer
是好还是坏)。
代码是否有任何错误以及可能的修复/改进?
编辑
也许我一开始就不该这么做XD
根据答案/建议修复:
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
void *alloc(void *ptr, size_t len) {
void *mem = realloc(ptr, len);
if (mem == NULL) {
free(ptr);
perror(NULL);
}
return mem;
}
char *str(char *src, const char *fmt, ...) {
if (fmt == NULL) {
return NULL;
}
va_list args1, args2;
va_copy(args2, args1);
va_start(args1, fmt);
int len = vsnprintf(NULL, 0, fmt, args1);
va_end(args1);
if (len < 0) {
return NULL;
}
char *val = alloc(src, len + 1);
va_start(args2, fmt);
int res = vsnprintf(val, len + 1, fmt, args2);
va_end(args2);
free(src);
if (res < 0) {
free(val);
return NULL;
}
return val;
}
int main(int argc, char *argv[]) {
char *s = str(NULL, "FOUND!");
puts(s);
free(s);
s = str(s, "NOT %s", s);
puts(s);
free(s);
return 0;
}
变更内容(以及注解(Ns),供将来参考):
- N**:传递未初始化的
&pointer
是错误的,实际上是伪装的错误。
- N**:传递未初始化的
- N**:使用
realloc
代替malloc
,后者处理NULL
初始值并释放先前分配的内存。
- N**:使用
- 将
**
参数更改为*
。**
背后的最初想法是能够直接修改函数中*src
指针的值(在原始代码中以某种方式工作)。 - 初始声明更改为
char *s = str(NULL, "value")
,更新值需要重新分配s = str(s, "updated: %s", s)
。 - 对于
vsnprintf
的返回值,将size_t len
更改为int
。size_t
是我最初用来匹配malloc
的size_t
参数的值。 - N**:第二次
vsnprintf
调用可能在第一次调用后返回不同的结果,因此使用va_copy
复制第一个va_list
用于第二次调用。
- N**:第二次
free
旧字符串,然后返回实际值。
2条答案
按热度按时间jq6vz3qz1#
虽然它确实有效
不,它在某些微妙的方面失败了。
str(&s, "NOT %s", s)
执行s
的free()
,然后尝试读取s
:alloc()
(及其free()
)出现在第二个vsnprintf()
之前。这是基本设计问题。代码因
char *s = str(&s, "FOUND!");
而失败,因为str()
最终尝试free(*ptr);
,并且*ptr
的值不确定。va_end()
**代码应始终在
va_start()
之后调用va_end()
。malloc(0)
**这里使用的
alloc()
从不调用malloc(0)
。然而,作为一个通用函数,alloc(..., 0)
是可能的。在这种情况下,malloc(0)
,返回NULL
不是一个问题,代码应该不会出错。vsnprintf()
可能失败**在多线程应用程序中,第二个
vsnprintf()
可能返回与第一个不同的值。代码也可以检查第二个返回值,但我们遇到了一些深层次的问题。size_t len = vsnprintf(NULL, 0, fmt, args); if (len < 0)
永远不为true。请通过保存为带符号类型来检查vsnprintf()
的返回值。迂腐:
vsnprintf()
返回一个int
。在极少数实现中,size_t
的范围比int
窄。您是否应该处理这样的问题:改进:
alloc()
对free(*str)
是陌生的,然后不更新*str
。OP希望执行
str(&s, "NOT %s", s)
,这要求s
在整个打印阶段保持稳定。代码然后必须为新字符串分配,打印,然后释放旧字符串。
'alloc()'看起来几乎像'realloc()'。
相反,返回一个错误状态并使用
strdup()
(或处理NULL
的类似函数)进行初始化。替代未测试代码:
使用
str()
时,即使fmt == NULL
或vsnprintf()
出现故障,也要考虑更新*src
-可能是free(*src); *src = NULL;
?huwehgph2#
你的函数
alloc()
和str()
看起来不错。你也许应该考虑使用realloc()
,它可以给予你一个小的内存使用/速度改进。你必须确保你从来没有传递一个未初始化的指针到
str()
,否则你的应用程序将崩溃。任何新的字符串指针应该在调用函数之前初始化为零。你为什么要传递一个指针到函数的指针?毕竟,你是要得到结果字符串,按值传递初始指针将有助于缓解这个问题,例如:你担心内存泄漏是对的。你用
str()
创建的每一个字符串都必须用free()
释放。