Пытаясь понять, когда звонить бесплатно в C - PullRequest
2 голосов
/ 03 октября 2011

edit Похоже, что это просто неверный пример кода. Спасибо за разъяснение, ТАК.

Глядя на следующий код / ​​цитату из http://staff.um.edu.mt/csta1/courses/lectures/csa2060/c8a.html

//f.c    
#include <stdio.h>
#include <stdlib.h>

char *foo(char *);

main() {
    char *a = NULL;
    char *b = NULL;

    a = foo("Hi there, Chris");
    free(a);

    b = foo("Goodbye");
    free(b); 

    printf("From main: %s %s\n", a, b);
}

char *foo(char *p) {
    char *q = (char *)malloc(strlen(p)+1);
    strcpy(q, p);
    printf("From foo: the string is %s\n", q);    
    return q;
}

Если free (b) опущено, то можно сказать, что «До свидания» записано в месте «Привет, Крис».

Я не понимаю, почему вы должны вызывать free, прежде чем использовать переменные free'd в операторе printf() (на самом деле, на мой взгляд, кажется, что сначала освобождение памяти может привести к сбою).

Извините, если это повторение, но, посмотрев / прочитав то, что я смог найти, я все еще в неведении. Код и цитата здесь: http://staff.um.edu.mt/csta1/courses/lectures/csa2060/c8a.html

edit Похоже, что это просто случай неправильного примера кода. Спасибо за разъяснение, ТАК.

Ответы [ 5 ]

3 голосов
/ 03 октября 2011

Вы звоните free(), когда вам больше не нужно использовать память.

Ваш printf() приходит после того, как вы освободили обе строки, поэтому вы вызываете «неопределенное поведение» (UB), когда пытаетесь распечатать строки. Существует небольшая вероятность того, что вы получите один и тот же адрес для a и b в main(), и в этом случае вы, конечно, можете хранить только одну из двух строк в пространстве. Но это все еще UB, и все может случиться.

Вы должны звонить free() только после printf() в main().

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

char *foo(char *);

int main(void)
{
    char *a = NULL;
    char *b = NULL;

    a = foo("Hi there, Chris");
    b = foo("Goodbye");

    printf("From main: %s %s\n", a, b);

    free(a);    // Now it is safe to free the memory
    free(b); 
    return 0;
}

char *foo(char *p)
{
    char *q = (char *)malloc(strlen(p)+1);
    strcpy(q, p);
    printf("From foo: the string is %s\n", q);    
    return q;
}
2 голосов
/ 03 октября 2011

Когда вы вызываете free(a), вы указываете среде выполнения освободить память, указанную переменной указателя a. К тому времени, как вы доберетесь до printf, a больше не будет указывать на действительную память. Случайно, b выделяется той же памяти, которая когда-то была a. (Тогда b тоже освобождается, поэтому ни один указатель не действителен.)

Печать строк в двух недопустимых указателях - неопределенное поведение. Случайно, память содержит содержимое строк, которые вы скопировали туда ранее.

1 голос
/ 03 октября 2011

Вы должны получить вывод, подобный:

From foo: the string is Hi there, Chris
From foo: the string is Goodbye
From main: 

Это имеет смысл в соответствии с вашим кодом, поскольку вы освободили переменную, которую собираетесь использовать в последнем выражении printf.

Я изменил ваш код на:

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

char *foo(char *);

main() {
  char *a = NULL;
  char *b = NULL;

  a = foo("Hi there, Chris");

  b = foo("Goodbye");

  printf("From main: %s %s\n", a, b);
  free(a);
  free(b);
}

char *foo(char *p) {
  char *q = (char *)malloc(strlen(p)+1);
  strcpy(q, p);
  printf("From foo: the string is %s\n", q);
  return q;
}

Вывод вышеуказанной программы:

From foo: the string is Hi there, Chris
From foo: the string is Goodbye
From main: Hi there, Chris Goodbye
1 голос
/ 03 октября 2011

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

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

0 голосов
/ 03 октября 2011

Это неправильно на многих уровнях.В foo вы динамически выделяете пространство для строки символов и заполняете ее - это хорошо.

Дело в том, что теперь у вас есть контейнер (ну, блок памяти) для каждой строки, a и b, так какВы назвали Foo на обоих.Вам нужен этот контейнер для хранения строки в течение срока, который вы хотите использовать.

Таким образом, вы не можете звонить бесплатно на a или b, пока не закончите их использовать.Вы вызываете это слишком рано (до вашего printf ()), поэтому вы вызываете неопределенное поведение.Насколько вы знаете, компьютер повторно использовал области памяти для a и b, прежде чем ваш printf даже напечатает их содержимое.Это может привести к возникновению ошибки или к чему-то противному.

Позвоните бесплатно обоим после printf, когда закончите.

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

...