2010-07-20 1 views
4

При передаче символа char * в качестве аргумента функции, должна ли вызываемая функция делать свободную в этой строке? В противном случае данные будут «потеряны» правильно, и программа будет утечка данных. Или char * обрабатывается компилятором особым образом, чтобы не позволить всем делать свободное время и автоматически удаляет его из-за границы? Я передаю «строку» функции, а не экземпляр уже существующего символа char *. Или нужно использовать char [] вместо этого? Просто так глупо устанавливать фиксированный предел для ввода аргумента.char * как аргумент функции в C

+1

Хороший ответ Майкла говорит вам рассмотреть строки, хранящиеся в так называемом «статическом хранилище» - это память, инициализированная ОС оперативной памяти при загрузке образа программы. Пример Навеена и сам бесплатный/malloc, используйте «бесплатный магазин», иногда называемый «кучей». Существует также «автоматическое хранилище», которое обычно реализуется как «стек». Другие возможности хранения - это области памяти ABI с отображением OS, файлы с отображением памяти и т. Д. Примером может быть вызов ОС, возвращающий текущую строку каталога - этот символ «char *» может указывать на структуру данных в потоке ОС. –

+0

Обычно вы не должны устанавливать фиксированный предел ввода аргумента. Вместо этого, вызывающий дает вам указатель и пусть * их * скажите * вы *, сколько времени строка. – bta

ответ

15

Помните об этом простом принципе: «всегда свободная память на том же уровне что вы ее выделили». Другими словами, функция никогда не должна пытаться освобождать память, которую она сама не выделяла. Краткий пример разъяснить это:

#include "graphics.h" 

// The graphics API will get a Canvas object for us. This may be newly allocated 
// or one from a pool of pre-allocated objects. 
Canvas* canvas = graphics_get_canvas(); 

// If draw_image() frees canvas, that violates the above principle. 
// The behavior of the program will be unspecified. So, just draw the image 
// and return. 
draw_image (canvas); 

// This is also a violation. 
// free (canvas) ; 

// The right thing to do is to give back the Canvas object to the graphics API 
// so that it is freed at the same 'level' where it was allocated. 
graphics_return_canvas (canvas); 

Обратите внимание, что функция не по имени graphics_free_canvas() или что-то подобное, потому что API может выбрать, чтобы освободить его или использовать его, возвращая его в бассейн. Дело в том, что крайне плохой практикой в ​​программировании является принятие на себя права собственности на ресурс, который мы не создали, если только мы специально не сказали иначе.

+4

Как это сидеть с подобными malloc() и strcmp()? Они должны быть освобождены на другом уровне. Фактически, reductio ad absurdum, free() сам никогда не освободит память :-) – paxdiablo

+0

@paxdiablo, strcmp()? Вы имели в виду strdup()? Если это так, я не понимаю, как они должны быть освобождены на другом уровне. strcmp(), конечно, ничего не выделяет. –

+0

@ HH, я не думаю, что он действительно это понимает. Принцип, изложенный в ответе (очистка собственного беспорядка), звучит. Избегайте конструкции, используемой одним из моих бывших коллег, что его функции возвратили char * к (текстовому) ответу, и в этом случае вам пришлось освободить его или сообщить об ошибке, и в этом случае вам не нужно было его освобождать. –

7

Это звучит, как вы спрашиваете об этом использовании:

void foo(char* str); 
foo("test string"); 

Это особый случай; "test string" - это постоянная строка, хранящаяся в таблице строк в исполняемом файле, и ее не нужно освобождать. foo должны фактически взять const char* чтобы показать, что и позволяет строковых литералов, которые будут храниться в непостоянных char* с устаревшим в C++

+0

Чтобы развернуть это немного, строковый литерал «тестовая строка» существует как 12-элементный массив char (const char в C++) с * static extent *, то есть память для него выделяется при запуске программы и удерживается до тех пор, пока выходы программы. Когда вы передаете строковый литерал как аргумент функции, компилятор ** не создает ** новый экземпляр строки; вместо этого он передает указатель на существующий экземпляр. –

7

ли функция должна сделать free или нет, зависит от того, кто владеет строки. Этот код вполне допустимо и не приводит к какой-либо утечки памяти:

int main() 
{ 
    char* s = malloc(.....); 
    f(s); 
    free(s); 
} 

free может быть выполнена внутри функции f, а если он принимает право собственности на строку. Однако обратите внимание, что это опасно, поскольку вы считаете, что строка, переданная функции f, всегда выделяется в куче, используя malloc или связанные функции. Если пользователь передает указатель на строку, выделенную в стеке, ваша программа будет вести себя непредсказуемо.

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

+1

Не только строка, выделенная стеком. Если вы передаете 'f()' строковый литерал, а 'f()' пытается его освободить, вы также столкнетесь с проблемами. – ptomato

0

Когда-то API ожидает выделенного буфера и его до объекта функции, которая вызывает это api, чтобы освободить его.

myFunc() 
{ 
char *error = malloc(<max size of error string>); 
foo(error); 
//Free the pointer here 
free(error); 

} 

Некоторый API, как GLIB API, ожидать указатель на адрес объявленной переменной

myFunc() 
{ 
GError *error; 

glib_api(&error); 
if (error) 
{ 
printf("Error %s", error-> message); 
// can use glib API to free if error is NON NULL but message is allocated by GLIB API 
g_error_free(error); 
} 
} 

Таким образом, даже если вы не выделенная память для переменных вам нужно сделать освобождение при использовании стандартных библиотек.

Выделенная часть памяти, если она не освобождена, приведет к уменьшению объема памяти в многопроцессорной среде, что ухудшит производительность системы.

1

Кажется, что вы привыкли к стилю ООП.Мне не нравится ООП, и для меня было бы странно, если бы я получил копию объекта после назначения. В этом случае строка находится где-то в памяти, и ее адрес отправляется как char *, а не целая строка. Кроме того, будьте осторожны, вы можете освободить() только указатели, возвращаемые malloc(), и только один раз.

0

С равным char * я бы рекомендовал всегда писать код с политикой, согласно которой вызывающий «владеет» строкой и отвечает за ее освобождение, если она была получена malloc. С другой стороны, можно было бы предусмотреть «строчные объекты« псевдопроход по значению »в C, реализованные как структура, где политика требует, чтобы вы отказались от владения строкой (или дублировали ее сначала и передали дубликат) при передаче строк как аргументы. Это могло бы работать особенно хорошо, если в реализации использовалось хранилище с подсчетом ссылок для строк, в которых переданный объект был просто ссылкой на хранилище, так что операция «дублировать» была бы просто приращением счетчика ссылок плюс тривиальное распределение структуры-оболочки (или даже структура передачи по значению).

0

Указатель на char как функциональную переменную является адресом для той же переменной, за исключением случаев, когда он был заменен как постоянная строка. Ваш вопрос не может быть объяснен простым руководством да/нет; это зависит от контекста. В приведенном ниже коде структура, выделенная для кучи и стека отдельно, передается по ссылке, а также строка char *, и данные вставляются в структуру. Обратите внимание на то, как маллоки отличаются друг от друга, когда они используются, но функция работает одинаково.

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

// struct with all data within 
typedef struct d 
{ 
int number; 
char name[50]; 
}data; 
// struct with dynamic char * 
typedef struct d2 
{ 
int number; 
char *name; 
}dynamic_data; 

// generic function placing data into struct 
void InsertData (data * out, int a, char * b) 
{ 
    out->number = a; 
    strcpy(out->name, b); 
} 

// generic function placing data into second struct 
void InsertData2 (dynamic_data * out, int a, char * b) 
{ 
    out->number = a; 
    strcpy(out->name, b); 
} 


int main (void) 
{ 
    char * text = "some string\0"; 
    int n = 20; 
    // allocated struct 
    data stuff; 

    dynamic_data stuff2; 

    dynamic_data * stuff3; 
    // need to allocate pointer within struct only 
    stuff2.name = (char *) malloc(50 * sizeof(char)); 

    // heap allocated struct 
    stuff3 = (dynamic_data *) malloc(50 * sizeof(dynamic_data)); 
    // heap allocated sub element char * 
    stuff3->name = (char *) malloc(50 * sizeof(char)); 


    // this is the data 
    printf ("Pre insertion data\n"); 
    printf ("s=[%s]\n", text); 
    printf ("n=%d\n", n); 

    // this is the function insertting 
    InsertData (&stuff, n, text); 
    printf ("Post insertion data\n"); 
    printf ("stuff.name=[%s]\n", stuff.name); 
    printf ("stuff.number=%d\n", stuff.number); 

    // this is the function inserting 
    InsertData2 (&stuff2, n, text); 
    printf ("Post insertion data\n"); 
    printf ("stuff.name=[%s]\n", stuff2.name); 
    printf ("stuff.number=%d\n", stuff2.number);  

// 
// This is the segfault version - if nothing was allocated for pointers into 
// this function scope, it would crash 


    // this is the function insertting under a heap allocated 
    InsertData2 (stuff3, n, text); 
    printf ("Post insertion data - dynamic version\n"); 
    printf ("stuff3->name=[%s]\n", stuff3->name); 
    printf ("stuff3->number=%d\n", stuff3->number); 

    // free in reverse order 
    free(stuff3->name); 
    free(stuff3); 
    free(stuff2.name); 
    return 0; 
}