2017-07-28 194 views
0

我有一个大字符串,我想使用它的一些片段,但我不想一定要复制它们,所以我想我可以制作一个标记有用块的开始和长度的结构从大字符串开始,然后创建一个读取它的函数。字符串的返回子字符串

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; 
} 

所以问题我有分别是:

  • 有什么办法,我可以返回一条绳子但没有照搬
  • 如果没有,我该怎么处理这个内存泄漏,因为我复制s在堆内存中,我无法控制谁会拨打getSegment
+1

你不能只是返回'字符串+ d.start'?唯一的问题是如果有人写入内存。其他问题将是空终止符,所以你可能需要通过传递新的子字符串 –

+2

来管理它,而不会损坏原始字符串,什么都不做。你必须将没有空终止的子字符串复制到其他空格中以终止它。你无法处理泄漏(使用该界面);你必须依靠调用者正确处理分配的内存。你可以定义'char * getSegment(const char * source,struct descriptor d,char * output)',其中调用者在'output'参数中提供的空间必须至少为d.length + 1'字节的内存。当然,调用者也对该内存负责。 –

+0

@JonathanLeffler什么是一个适当的接口,被调用者不需要处理内存管理? –

回答

1

回答您的两个问题:

  1. 没有
  2. 调用方应为被复制字符串提供缓冲
  3. 我会亲自传递指针descrpiptor

char* getSegment(const char* string, const char *buff, struct descriptor *d)

+0

如果我像我一样传递结构我的例子是在内存中复制它?也许我对这样的事实感到困惑,在动态语言中,对象总是通过引用传递的,我将结构看作是对象的等价物。 –

+0

@php_nub_qq:是的,结构被复制;不,当结构中只有两个整数时,成本不是一个主要问题。就我个人而言,我可能会传递结构,而不是指向它的指针。如果结构变得更大(例如,也包含指针),那么我会考虑将指针传递给结构。我也会考虑运行性能测量。“ –

1
  • 有什么办法,我可以返回一条绳子但没有照搬

你说的没错,如果你想与任何的意想不到的许多C函数使用块要使用以null结尾的字符数组,则必须进行复制。否则,添加终止符会修改原始字符串。

但是,如果您准备将句块作为固定长度来处理,那么您可以将它们表示为不作为指向第一个字符和长度的指针的组合来复制。某些标准库函数使用用户指定的字符串长度,因此支持在没有空终止的情况下对这些段进行操作。但是,您需要非常小心。

如果你采取这种方法,我会建议colocating指针和结构的长度。例如,

struct string_segment { 
    char *start; 
    size_t length; 
}; 

可以声明这种类型的变量,传递和返回此类型的对象,并创建该类型的复合文字而没有任何动态存储器分配,从而避免打开任何途径内存泄漏。

  • 如果没有,我该怎么处理这个内存泄漏,因为副本是堆内存和我没有对谁将会调用getSegment控制?

返回动态分配的对象不会自动创建一个内存泄漏 - 它只是赋予呼叫者责任以释放分配的内存。当调用者未能满足该责任或将其传递给发生内存泄漏的其他代码时。几个标准库函数确实会返回动态分配的对象,在第三方库中并不常见。规范示例(malloc()本身除外)可能是POSIX标准的strdup()函数。

如果你的函数返回一个指向动态分配对象的指针 - 无论是复制的字符串还是块定义结构 - 那么它应该释放它的责任落在调用者上。当您从自己的代码中调用该功能时,您必须确保您履行义务,但是如果已明确记录该功能的行为,则不能对其他呼叫者未履行义务时可能犯的错误承担责任。

1

有什么办法,我可以返回一条绳子,而不将其复制

一个包括终止空字符,所以除非部分代码想是尾巴,指向“一段字符串”的指针仍然是一个字符串,是不可能的。


我该怎么处理这个内存泄漏,因为副本是堆内存和我没有对谁将会调用getSegment控制?

使用可变长度数组(由于C99和C11支持可选)创建临时空间。好,直到块的结束。在这一点上,内存被释放,不应该被进一步使用。

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()缓冲区,并在完成时强制调用代码释放它。