在C中返回字符串的正确方法

时间:2015-07-14 16:06:59

标签: c string pointers

我有以下代码:

char* get_address_string(PACKAGE* pkg){
    char *c;
    sprintf(c, "%02x:%02x:%02x:%02x:%02x:%02x", pkg->address[0], pkg->address[1], 
        pkg->address[2], pkg->address[3], pkg->address[4], pkg->address[5]);
    return c;
}

代码工作正常。但是,我知道这不是在C中返回字符串的正确方法。我收到警告“c在此函数中使用未初始化”。

在C中编写此函数的正确方法是什么?

4 个答案:

答案 0 :(得分:9)

“在C中返回字符串的正确方法”是不可能的。在C中, string 是一个字符数组(最多包括空字符),并且数组本身不能从函数返回。

一个函数可以返回指针。所以通常的方法是“将字符串”返回到:

  1. 返回一个指针。 char *foo1(...),如char *strdup()

  2. 传入指向字符数组的指针并修改其内容。 void foo2(char *,...),如int sprintf(char *dest, const char *format, ...)

  3. 结合1& 2 char *foo3(char *, ...),例如char *strcpy(char *dest, char *src)

  4. 传递指针的地址并更新它。 foo4(char **ptr),如ssize_t getline(char **lineptr, size_t *n, FILE *stream)

  5. 关键是在函数完成后,与指针关联的内存必须有效。返回指向函数的非静态内存的指针是未定义的行为。成功的方法包括在指针中传递调用代码,或者通过内存分配指向一些持久值(如全局变量或字符串常量)的函数来提供它。

      

    在C中编写此函数的正确方法是什么?

    目前的设计实践鼓励像#2&上面的#3还提供size_t size,因此该函数知道可用内存的限制。

        char *foo2(char *s, size_t size, const pkg_T *pkg) {
          int result = snprintf(s, size, "%02x:%02x:%02x:%02x:%02x:%02x", 
            pkg->address[0], pkg->address[1], pkg->address[2], 
            pkg->address[3], pkg->address[4], pkg->address[5]);
          // encoding error or not enough room
          if (result < 0 || result >= size) return NULL;
          return s;
        }
    

    另一种方法是分配内存(我赞成上面的内容)。这要求调用代码free()内存。

        #define UINT_MAX_WIDTH (sizeof(unsigned)*CHAR_BIT/3 + 3)
    
        char *foo2alloc(char *s, size_t size, const pkg_T *pkg) {
          char buf[(UINT_MAX_WIDTH+3)*6 + 1];
          int result = snprintf(buf, sizeof buf, "%02x:%02x:%02x:%02x:%02x:%02x", 
            pkg->address[0], pkg->address[1], pkg->address[2], 
            pkg->address[3], pkg->address[4], pkg->address[5]);
          // encoding error or not enough room
          if (result < 0 || result >= size) return NULL;
          return strdup(buf);
        }
    

答案 1 :(得分:4)

c是一个指针,但没有分配内存。返回值没问题,就是如何在C中完成。

但你需要分配内存。

答案 2 :(得分:0)

由于c未初始化,sprintf会写入未知的内存位置,从而导致未指定的行为。它可能会立即崩溃,它可能根本不会崩溃,或者它可能会在某些完全不相关的代码行上崩溃。

您需要通过malloc为其分配内存来初始化指针。

char* get_address_string(PACKAGE* pkg){
    char *c = malloc(20);  // enough room for output as 00:11:22:33:44:55 plus null terminator
    if (c == null) {
        perror("malloc failed");
        exit(1);
    }
    sprintf(c, "%02x:%02x:%02x:%02x:%02x:%02x", pkg->address[0], pkg->address[1], pkg->address[2], pkg->address[3], pkg->address[4], pkg->address[5]);
    return c;
}

请注意,即使您提前知道需要多少内存,也无法通过数组在编译时将其置于一旁。这是错的:

char* get_address_string(PACKAGE* pkg){
    char c[20];    // allocated on the stack, contents unspecified on return
    sprintf(c, "%02x:%02x:%02x:%02x:%02x:%02x", pkg->address[0], pkg->address[1], pkg->address[2], pkg->address[3], pkg->address[4], pkg->address[5]);
    return c;
}

就是这样:

char* get_address_string(PACKAGE* pkg){
    char c[20];    // allocated on the stack, contents unspecified on return
    char *p = c;
    sprintf(p, "%02x:%02x:%02x:%02x:%02x:%02x", pkg->address[0], pkg->address[1], pkg->address[2], pkg->address[3], pkg->address[4], pkg->address[5]);
    return p;
}

由于c已在堆栈上分配,因此get_address_string返回时内容未指定,再次导致未指定的行为。

答案 3 :(得分:-3)

我更喜欢从调用者分配堆,以便明确谁应该释放它。

#include <stdio.h>
#include <malloc.h>

bool GetString(char ** retString, size_t size)
{
    // use size to do range check
    sprintf_s(*retString, size, "blah blah blah");

    return true;
}

int _tmain(int argc, _TCHAR* argv[])
{
    size_t size = 100;
    char *data = (char *)malloc(size);

    if (data)
    {
        GetString(&data, size);

        free(data);
    }
    return 0;
}