2015-07-14 3 views
5

У меня есть следующий код:Правильный способ вернуть строку в C

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?

+0

Прежде всего это ** должно ** сбой, потому что вы пишете нераспределенную память, отменяя неинициализированный указатель. Надлежащим образом? Трудно дать правило * общего *. Как и вы, вы оставляете ответственность за освобождение памяти от вызывающего (но вы ее выделяете), она * может * быть склонной к ошибкам и причиной утечек. Другой способ - иметь выходной буфер в качестве входного аргумента. Часто он имеет лучшую производительность (буферы могут быть повторно использованы или даже оставаться в стеке), он менее подвержен ошибкам, но он также менее * естественный * и более подробный. –

+0

«Код работает нормально». - Это не реально. –

+0

Во-первых, он 100% не падает. Однако наличие выходного буфера в качестве входного файла должно работать для того, что мне нужно. – mstagg

ответ

4

c - указатель, но память не выделяется. Возвращаемое значение в порядке, вот как это можно сделать в C.

Но вам нужно выделить память.

+2

Возвращаемое значение не подходит, если c - локальная переменная типа array, например char [128] –

+0

@Giorgi Не уверен, что я понимаю. Не могли бы вы уточнить, пожалуйста? Пример будет полезен. – Elyasin

+2

вы написали «return value is ok» - было бы не так, если c был объявлен как «char c [128] =« test »;' –

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

Почему двойной указатель? Передача простого символа 'char *' была бы идеальной. – alk

+0

Также код, который вы показываете не Standard-C. Там нет ничего похожего на '_tmain()' в C. Я не делал downvote, кстати. – alk

+1

В чем смысл возврата типа bool, и он всегда возвращает true? –

9

"Правильный способ вернуть строку в C" не является действительно возможно. В C строка представляет собой массив символов (вплоть до нулевого символа и включая нулевой символ), а сами массивы не могут быть возвращены из функции.

Функция может возвращать указатели. Таким образом, обычный метод «вернуть строку» это:

  1. Верните указатель. char *foo1(...)char *strdup()

  2. Передайте указатель на массив символов и измените его содержимое. void foo2(char *,...) как int sprintf(char *dest, const char *format, ...)

  3. Объединить 1 как char *strcpy(char *dest, char *src)

  4. Пропускают адрес указателя и обновлять это. foo4(char **ptr) как ssize_t getline(char **lineptr, size_t *n, FILE *stream)

Ключ в том, что память, связанная с указателем должен быть действительным после того, как функция завершена. Возврат указателя к нестатической памяти функции - это неопределенное поведение. Успешные методы включают в себя передачу кода вызова в указатель или функцию, обеспечивающую его посредством выделения памяти указателем на некоторое постоянное значение, такое как глобальная переменная или строковая константа.

Каков правильный способ записи этой функции в C?

Текущая практика дизайна стимулирует функции, такие как # 2 & # 3 выше, также поставить size_t size поэтому функция knowns ограничения доступной памяти.

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); 
    } 
+0

Я бы сказал, что в этом случае есть определенные преимущества для распределения памяти в функции, поскольку OP явно предполагает, что длина строки будет постоянной. – Mints97

+0

@ Mints97 Если вызывающий код или эта функция выделяют память, часто является стратегией кодирования более высокого уровня - оба имеют преимущество.Для вашего «функционального» предпочтения, имея эту функцию, у нее есть преимущества справиться с обычной надоедливой проблемой печати 'char address [6]; адрес [0] = 255' и получить неожиданный '' FFFFFFFF'', когда это должен был быть либо 'unsigned char address [6];' или '"% 02hhx: '. – chux

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 возвращает содержимое не указано, что снова приводит к неустановленному поведению.

Смежные вопросы