char * в качестве аргумента функции в C - PullRequest
4 голосов
/ 20 июля 2010

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

Ответы [ 7 ]

15 голосов
/ 20 июля 2010

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

#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 может решить освободить ее или использовать повторно, вернув в пул. Суть в том, что практика владения ресурсом, который мы не создали, является крайне плохой практикой программирования, если нам не указано иное.

7 голосов
/ 20 июля 2010

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

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

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

В общем, компилятор не выполняет какой-либо специальной обработки для управления строками в памяти.С точки зрения компилятора это просто набор символов.

7 голосов
/ 20 июля 2010

Похоже, вы спрашиваете об этом использовании:

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

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

1 голос
/ 20 июля 2010

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

0 голосов
/ 07 июля 2016

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

#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;
}
0 голосов
/ 20 июля 2010

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

0 голосов
/ 20 июля 2010

Иногда API ожидает выделенный буфер и его право на владение функцией, которая вызывает этот API, чтобы освободить его.

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

}

Некоторые API, такие как GLIB API 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);
}
}

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

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

...