我有一个大字符串,我想要使用它的一部分,但我不想复制它们,所以我想我可以制作一个结构,标记来自的有用块的开始和长度大字符串,然后创建一个读取它的函数。
struct descriptor {
int start;
int length;
};
到目前为止一切顺利,但是当我开始编写这个函数时,我意识到我无法在没有复制到内存的情况下返回这个块...
char* getSegment(char* string, struct descriptor d) {
char* chunk = malloc(d.length + 1);
strncpy(chunk, string + d.start, d.length);
chunk[d.length] = '\0';
return chunk;
}
所以我的问题是:
getSegment
?答案 0 :(得分:1)
回答你的两个问题:
char* getSegment(const char* string, const char *buff, struct descriptor *d)
答案 1 :(得分:1)
- 有没有办法可以在不复制的情况下返回该字符串
你是对的,如果你想将这些块与任何期望使用空终止字符数组的C函数结合使用,那么你必须制作副本。否则,添加终结符会修改原始字符串。
但是,如果你准备将块作为固定长度的 un 终止的数组来处理,那么你就可以代表它们而不需要复制为指向第一个字符的指针和一个长度。一些标准库函数使用用户指定的字符串长度,从而支持对这些段的操作而不会出现空终止。但是,你需要非常小心它们。
如果采用这种方法,我建议将指针和长度放在结构中。例如,
struct string_segment {
char *start;
size_t length;
};
您可以声明此类型的变量,传递和返回此类型的对象,并创建此类型的复合文字而无需任何动态内存分配,从而避免打开内存泄漏的任何途径。
- 如果没有,我怎么能处理这个内存泄漏,因为副本在堆内存中,我无法控制谁将调用getSegment?
返回动态分配的对象不会自动创建内存泄漏 - 它只是赋予调用者释放已分配内存的责任。当调用者无法满足该职责或将其传递给发生内存泄漏的其他代码时。几个标准库函数确实返回动态分配的对象,并且在第三方库中并不常见。规范示例(malloc()
本身除外)可能是POSIX标准strdup()
函数。
如果你的函数返回一个指向动态分配对象的指针 - 无论是复制的字符串还是块定义结构 - 那么它应该记录免费的责任落在调用者身上。当您从自己的代码中调用它时,您必须确保您履行义务,但是如果明确记录了该功能的行为,则您不能对其他呼叫者因未能履行其义务而可能犯的错误承担责任。
答案 2 :(得分:1)
有没有办法可以在不复制的情况下返回该字符串
字符串包含终止空字符,因此除非部分代码需要的是尾部,指向“字符串”的指针仍然是字符串,是不可能的。
我如何处理这个内存泄漏,因为副本在堆内存中,我无法控制谁将调用getSegment?
使用可变长度数组创建临时空间(因为C11中支持C99和可选项)。好到块结束。此时,内存被释放,不应再使用。
char* getSegment(char* string, struct descriptor d, char *dest) {
// form result in `dest`
return dest;
}
用法
char *t;
{
struct descriptor des = bar();
char *large_string = foo();
char sub[des.length + 1u]; //VLA
t = getSegment(large_string, des, sub);
puts(t); // use sub or t;
}
// do not use `t` here, invalid pointer.
召回尺寸值得关注。如果代码返回大的子字符串,最好malloc()
一个缓冲区并强制调用代码在完成后释放它。