在c中返回可变长度字符串的最佳实践

Mic*_*ael 9 c string function

我有一个字符串函数,它接受指向源字符串的指针并返回指向目标字符串的指针.这个功能目前有效,但我担心我没有遵循重新编写malloc,realloc和free的最佳实践.

与我的函数不同的是,目标字符串的长度与源字符串不同,因此必须在我的函数内调用realloc().我从查看文档中了解到......

http://www.cplusplus.com/reference/cstdlib/realloc/

realloc后内存地址可能会改变.这意味着我不能像C程序员那样"通过引用"传递给其他函数,我必须返回新的指针.

所以我的功能原型是:

//decode a uri encoded string
char *net_uri_to_text(char *);
Run Code Online (Sandbox Code Playgroud)

我不喜欢我这样做的方式,因为我必须在运行函数后释放指针:

char * chr_output = net_uri_to_text("testing123%5a%5b%5cabc");
printf("%s\n", chr_output); //testing123Z[\abc
free(chr_output);
Run Code Online (Sandbox Code Playgroud)

这意味着在我的函数中调用malloc()和realloc(),并在函数外部调用free().

我有高级语言的背景,(perl,plpgsql,bash)所以我的直觉是对这些东西的正确封装,但这可能不是C语言中的最佳实践.

问题:我的方式是最佳实践,还是我应该遵循更好的方法?

完整的例子

在未使用的argc和argv参数上编译并运行两个警告,您可以安全地忽略这两个警告.

example.c:

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

char *net_uri_to_text(char *);

int main(int argc, char ** argv) {
  char * chr_input = "testing123%5a%5b%5cabc";
  char * chr_output = net_uri_to_text(chr_input);
  printf("%s\n", chr_output);
  free(chr_output);
  return 0;
}

//decodes uri-encoded string
//send pointer to source string
//return pointer to destination string
//WARNING!! YOU MUST USE free(chr_result) AFTER YOU'RE DONE WITH IT OR YOU WILL GET A MEMORY LEAK!
char *net_uri_to_text(char * chr_input) {
  //define variables
  int int_length = strlen(chr_input);
  int int_new_length = int_length;
  char * chr_output = malloc(int_length);
  char * chr_output_working = chr_output;
  char * chr_input_working = chr_input;
  int int_output_working = 0;
  unsigned int uint_hex_working;
  //while not a null byte
  while(*chr_input_working != '\0') {
    //if %
    if (*chr_input_working == *"%") {
      //then put correct char in
      sscanf(chr_input_working + 1, "%02x", &uint_hex_working);
      *chr_output_working = (char)uint_hex_working;
      //printf("special char:%c, %c, %d<\n", *chr_output_working, (char)uint_hex_working, uint_hex_working);
      //realloc
      chr_input_working++;
      chr_input_working++;
      int_new_length -= 2;
      chr_output = realloc(chr_output, int_new_length);
      //output working must be the new pointer plys how many chars we've done
      chr_output_working = chr_output + int_output_working;
    } else {
      //put char in
      *chr_output_working = *chr_input_working;
    }
    //increment pointers and number of chars in output working
    chr_input_working++;
    chr_output_working++;
    int_output_working++;
  }
  //last null byte
  *chr_output_working = '\0';
  return chr_output;
}
Run Code Online (Sandbox Code Playgroud)

Fre*_*Foo 8

malloc只要您记录它们的事实,就可以从C中的函数返回'd缓冲区.很多库都这样做,即使标准库中没有函数.

如果你可以计算(一个不太悲观的上限)需要廉价地写入缓冲区的字符数,你可以提供一个功能,让用户调用它.

接受要填充的缓冲区也是可能的,但不太方便; 我见过很多像这样的库:

/*
 * Decodes uri-encoded string encoded into buf of length len (including NUL).
 * Returns the number of characters written. If that number is less than len,
 * nothing is written and you should try again with a larger buffer.
 */
size_t net_uri_to_text(char const *encoded, char *buf, size_t len)
{
    size_t space_needed = 0;

    while (decoding_needs_to_be_done()) {
        // decode characters, but only write them to buf
        // if it wouldn't overflow;
        // increment space_needed regardless
    }
    return space_needed;
}
Run Code Online (Sandbox Code Playgroud)

现在调用者负责分配,并会做类似的事情

size_t len = SOME_VALUE_THAT_IS_USUALLY_LONG_ENOUGH;
char *result = xmalloc(len);

len = net_uri_to_text(input, result, len);
if (len > SOME_VALUE_THAT_IS_USUALLY_LONG_ENOUGH) {
    // try again
    result = xrealloc(input, result, len);
}
Run Code Online (Sandbox Code Playgroud)

(这里,xmallocxrealloc是我做了跳过NULL检查"安全"的功能分配.)

  • 关于期望调用者传递缓冲区(+大小)的两个很好的方面:1.调用者可能知道前面的最大长度,因此他可以决定使用在堆栈上分配的数组.2.)内存的所有权不会被转移,即分配和解除分配都发生在调用者站点上 - 如果调用者与被调用者位于不同的DLL中,这在Windows上是至关重要的(禁止在一个DLL中分配内存)并在Windows上将其发布到另一个,因为内存管理器是按模块进行的,而不是按进程进行的. (2认同)