Подтвердить что ты не робот

Как заставить gcc предупреждать о возврате адреса локальных переменных?

С gcc 4.4.5 у меня есть предупреждение со следующим кодом.

char *f(void)
{
    char c;
    return &c;
}

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

char *f(void)
{
    char c;
    char *p = &c;
    return p;
}

Я слышал, что анализ указателей затруднен в C, но может ли gcc предупредить о таком коде?

4b9b3361

Ответ 1

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

Macmade рекомендует Clang в комментариях, рекомендацию, которую я могу сделать второй. Обратите внимание, что Clang по-прежнему направлен на то, чтобы быть полезным для большинства разработчиков, минимизируя ложные срабатывания. Это означает, что у него есть ложные негативы или, другими словами, что он пропускает некоторые реальные проблемы (когда вы не уверены, что есть проблема, он может оставаться безмолвным, а не рисковать тратить время разработчика с ложным положительным результатом).


Обратите внимание, что даже в случае вашей проблемы существует проблема в функции f(). Функция h() ниже четко выглядит, хотя вызывающий код не должен использовать p после его возврата:

char *p;

void h(void)
{
    char c;
    p = &c;
}

Еще один статический анализатор, который я могу порекомендовать, это анализ значений Frama-C (я один из разработчиков). Это не оставляет ложных негативов для некоторых семейств ошибок (включая оборванные указатели) при использовании в контролируемых условиях.

char *f(void)
{
    char c;
    return &c;
}

char *g(void)
{
    char c;
    char *p = &c;
    return p;
}

$ frama-c -val -lib-entry -main g r.c
...
r.c:11:[value] warning: locals {c} escaping the scope of g through \result
...
$ frama-c -val -lib-entry -main f r.c
...
r.c:4:[value] warning: locals {c} escaping the scope of f through \result
... 

Выше приведены только информационные сообщения, они не означают, что функция обязательно неверна. Для моей функции h() есть и одна:

h.c:7:[value] warning: locals {c} escaping the scope of h through p

Реальная ошибка, характеризуемая словом "assert" в выводе Frama-C, заключается в том, что функция вызывает h(), а затем использует p:

void caller(void)
{
  char d;
  h();
  d = *p;
}

$ frama-c -val -lib-entry -main caller h.c
...
h.c:7:[value] warning: locals {c} escaping the scope of h through p
...
h.c:13:[kernel] warning: accessing left-value p that contains escaping addresses; assert(Ook)
h.c:13:[kernel] warning: completely undefined value in {{ p -> {0} }} (size:<32>).

Анализ значения Frama-C называется контекстно-зависимым. Он анализирует функцию h() для каждого вызова со значениями, которые фактически передаются ему. Он также анализирует код, который приходит после вызова h() в функции caller() со значениями, которые действительно могут быть возвращены h(). Это дороже, чем анализ, нечувствительный к контексту, который обычно делает Clang или GCC, но более точно.

Ответ 2

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

Хотя gcc не будет жаловаться здесь, он будет предупреждать с использованием указателя следующим образом:

char *f(const char *x)
{
  char *y = x;
  ...
}

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

Еще одна утилита, которая обнаружит эту проблему, - это splint (http://splint.org).