Intereting Posts
Как сделать членов структуры частным? Как преобразовать путь Unicode в строку ac? быстрый способ проверить, равен ли массив символов нулю ошибка: ожидаемые спецификаторы декларации или «…» перед строковой константой Являются ли пустые массивы более недоступными gcc? MIPS (или SPIM): загрузка чисел с плавающей запятой Будет ли C автоматически освобождать память без указателей? Как подсчитать количество аргументов, переданных функции, которая принимает переменное количество аргументов? C макрос с выражением нежелательного результата Скомпилируйте и запустите файл .c с помощью Tiny C Compiler в Windows Неожиданное поведение при печати 4-байтового целочисленного байта байтом Что такое спецификатор формата для `long double` Что имеет более высокий приоритет в C-умножении или делении? C Для выполнения цикла преобразовать строку в формат времени в c

когда свободный указатель на C и как узнать, освобожден ли он

Я новичок в C, пытаясь разобраться в распределении памяти в C, который я немного смутил

#include  #include  typedef struct { int a; } struct1_t; int main() { funct1(); //init pointer return 1; } int funct2(struct1_t *ptr2struct) { printf("print a is %d\n",ptr2struct->a); //free(ptr2struct); printf("value of ptr in funct2 is %p\n", ptr2struct); return 1; //success } int funct1(){ struct1_t *ptr2struct = NULL; ptr2struct = malloc(sizeof(*ptr2struct)); ptr2struct->a = 5; printf("value of ptr before used is %p", ptr2struct); if (funct2(ptr2struct) == 0) { goto error; } free(ptr2struct); printf("value of ptr in funct1 after freed is is %p\n", ptr2struct); return 1; error: if(ptr2struct) free(ptr2struct); return 0; } 

У меня есть функция 1, которая вызывает функцию 2, и после использования выделенного указателя в funct1 я пытаюсь освободить указатель. И я создаю случай, где, если возвращаемое значение в funct2 не равно 1, попробуйте еще раз освободить указатель.

Мой вопрос ниже

какая практика лучше, если я должен освободить память в funct2 (после ее передачи) или в funct1 (после того, как я закончу получение возвращаемого значения funct1). Во-вторых, правильно ли это сделать ошибку goto и ошибку:

 if(ptr2struct) free(ptr2struct); 

Мой третий вопрос: как проверить, освобождено ли выделенное значение или нет? потому что после получения возвращаемого значения я освобождаю указатель, но если я его распечатаю, он показывает то же место с выделенным (так что это не нулевой указатель).

1) Должен ли я освободить его в вызывающей функции или в вызываемой функции?

Я пытаюсь сделать освобождение в той же функции, что и malloc-ing. Это сохраняет проблемы управления памятью в одном месте, а также улучшает разделение проблем, так как вызываемая функция в этом случае также может работать с указателями, которые не были изменены или используют один и тот же указатель дважды (если вы хотите сделать это ).

2) Правильно ли делать ошибку «goto»?

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

 //in the middle of the function, whenever you would have a return statement // instead do return_value = something; goto DONE; //... DONE: //resorce management code all in one spot free(stuff); return return_value; 

С другой стороны, С ++ имеет аккуратный способ сделать такое управление ресурсами. Поскольку деструкторы детерминистически называются непосредственно перед выходом функции, их можно использовать для аккуратного пакета этого короля управления ресурсами. Они называют этот метод RAII

Еще один способ, с которым другие языки должны иметь дело с этим, – это, наконец, блоки.

3) Могу ли я увидеть, был ли указатель уже освобожден?

К сожалению, вы не можете. То, что некоторые люди делают, это установить значение переменной указателя в NULL после его освобождения. Это не повредит (поскольку его старое значение не должно использоваться после его освобождения в любом случае), и у него есть свойство nice, которое освобождает нулевой указатель, указывается как no-op.

Однако делать это не является надежным. Будьте осторожны с тем, чтобы другие переменные сглаживали один и тот же указатель, поскольку они все равно будут содержать старое значение, которое теперь является опасным висячим указателем.

Вызов free () по указателю не меняет его, только отмечает память как бесплатную. Ваш указатель будет по-прежнему указывать на то же место, которое будет содержать одно и то же значение, но это значение теперь может быть перезаписано в любое время, поэтому вам не следует использовать указатель после его освобождения. Чтобы гарантировать, что это хорошая идея, всегда устанавливайте указатель на NULL после его освобождения.

Мой вопрос ниже

какая практика лучше, если я должен освободить память в funct2 (после ее передачи) или в funct1 (после того, как я закончу получение возвращаемого значения funct1)

Это вопрос «собственности». Кому принадлежит выделенная память. Как правило, это должно решаться на основе дизайна вашей программы. Например, единственной целью func1 () может быть выделение только памяти. То есть, в вашей реализации func1 () является функцией распределения памяти, а затем функция «вызова» использует память. В этом случае собственность на освобождение памяти связана с вызывающим абонентом func1 и NOT с func1 ().

Во-вторых, правильно ли это сделать ошибку goto и ошибку: использование «goto» обычно недооценивается. Это вызывает беспорядок в коде, который можно просто избежать. Однако я говорю «вообще». Бывают случаи, когда goto может быть спокойным и полезным. Например, в больших системах конфигурация системы является большим шагом. Теперь представьте, что вы вызываете одну функцию Config () для системы, которая выделяет память для своих различных структур данных в разных точках функции, например

  config() { ...some config code... if ( a specific feature is enabled) { f1 = allocateMemory(); level = 1; } ....some more code.... if ( another feature is enabled) { f2 = allocateMemory(); level = 2; } ....some more codee.... if ( another feature is enabled) { f3 = allocateMemor(); level =3; } /*some error happens */ goto level_3; level_3: free(f3); level_2: free(f2); level_1: free(f1); } 

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

Однако достаточно сказать, что в вашем примере goto легко можно избежать, и его следует избегать.

Мой третий вопрос: как проверить, освобождено ли выделенное значение или нет? потому что после получения возвращаемого значения я освобождаю указатель, но если я его распечатаю, он показывает то же место с выделенным (так что это не нулевой указатель).

Легко. Установите освобожденную память как NULL. Другим преимуществом, помимо упомянутого MK, является то, что передача NULL-указателя на свободный приведет к NOP, т. Е. Операция не выполняется. Это также поможет избежать любых проблем с двойным удалением.

То, что я собираюсь поделиться, – это мои собственные методы развития в C. Они НЕТ означают ТОЛЬКО способ организовать себя. Я просто излагаю путь не так.

Итак, во многом «С» – это свободный язык, поэтому многие дисциплины и строгости исходят от себя как разработчика. Я занимаюсь разработкой в ​​«С» более 20 лет профессионально, мне очень редко приходилось исправлять любое программное обеспечение профессионального уровня, которое я разработал. Хотя немало успехов можно отнести к опыту, справедливый кусок его коренится в последовательной практике.

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

  • Если у меня есть структура, которая используется во всем программном обеспечении, я пишу create / destroy; Функции init / done для него:

      struct foo * init_foo(); void done_foo(struct foo *); 

    и выделять и де-распределять структуру в этих функциях.

  • Если я манипулирую элементами структуры непосредственно по всей программе, тогда не набирайте ее. Я принимаю боль за использование ключевого слова struct в каждой декларации, чтобы я знал, что это структура. Этого достаточно, когда порог боли НЕ так много, что я буду раздражаться им. 🙂

  • Если я нахожу, что структура действует ОЧЕНЬ очень похожа на объект, то я решаю манипулировать элементами структуры STRICTLY через непрозрачный API; тогда я определяю его интерфейс через функции set / get type для каждого элемента, я создаю «декларацию вперед» в файле заголовка, используемом каждой другой частью программы, создавая непрозрачный typedef для указателя структуры и только объявляю фактическая структура в файле реализации API структуры.

foo.h:

 struct foo; typedef struct foo foo_t; void set_e1(foo_t f, int e1); int get_ei(foo_t f); int set_buf(foo_t f, const char *buf); char * get_buf_byref(foo_t f) char * get_buf_byval(foo_t f, char *dest, size_t *dlen); 

foo.c:

 #include  struct foo { int e1; char *buf; ... }; void set_e1(foo_t f, int e1) { f->e1 = e1; } int get_ei(foo_t f) { return f->e1; } void set_buf(foo_t f, const char *buf) { if ( f->buf ) free ( f->buf ); f->buf = strdup(buf); } char *get_buf_byref(foo_t f) { return f->buf; } char *get_buf_byval(foo_t f, char **dest, size_t *dlen) { *dlen = snprintf(*dest, (*dlen) - 1, "%s", f->buf); /* copy at most dlen-1 bytes */ return *dest; } 
  • Если связанные структуры очень сложны, вы можете даже захотеть реализовать указатели функций прямо в базовую структуру, а затем предоставить фактические манипуляторы в определенных расширениях этой структуры.

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

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

Надеюсь это поможет.

Я знаю, что это ответ, но я хотел дать свой вклад. Насколько я понимаю, когда вы вызываете функцию с такими параметрами, как здесь (указатель), параметры вставляются в стек (FILO) .


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