C: Предупреждения, связанные с шинами. Что они значат?
Следующий код компилируется и выполняется без каких-либо предупреждений или ошибок. Тем не менее, когда я использую шину для анализа кода, он показывает 4 предупреждения (показано под кодом).
Большинство примеров, которые я видел (с такими предупреждениями), использует malloc и free. Поскольку этот код не использует malloc, можем ли мы сказать, что безопасно игнорировать эти предупреждения? Какой должен быть правильный способ кодирования без удаления указателей?
#include <stdio.h>
typedef struct
{
void (*Doit) ( void );
} func;
typedef struct
{
func f;
int val;
} obj;
typedef struct
{
obj *o;
} world;
static void Read( void ) {
printf( "Read\n");
}
static void Init( world *w ) {
obj pc;
w->o = &pc; //(1)
w->o->val = 10;
w->o->f.Doit = Read;
w->o->f.Doit();
}
int main() {
world w;
Init( &w ); //(2)
return 0; //(3)
}
(1): 28: 5: неявно только хранилище w->o (тип obj *), не освобожденное до назначения: w->o = &pc . Утечка памяти обнаружена. Только квалифицированное хранилище не освобождается до потери последней ссылки на него.
(1): 28: 5: Непосредственный адрес и ПК, назначенные только для неявного: w->o = &pc . Немедленный адрес (результат оператора &) передается непоследовательно.
(2): 33: 11: Переменная w использовалась до определения. Используется значение r, которое не может быть инициализировано значением в некотором пути выполнения.
(3): 34: 14: Только хранилище wo (тип obj *), полученное из переменной, объявленной в этой области, не освобождается (утечка памяти). Предполагается утечка памяти из-за неполного освобождения структуры или глубокого указателя. Нераспределенное хранилище, доступное по ссылке, которая была освобождена, еще не было освобождено. Splint предполагает, что когда объект передается как единственный пустой указатель, внешний объект будет освобожден, а внутренние объекты - нет.
Этот код является просто тестом чего-то еще, чего я хочу достичь, но, поскольку я не обладаю достаточными знаниями в C, я хотел бы понять риск утечек памяти, используя описанный выше подход.
Заранее спасибо.
2 ответа
Я нахожу splint
вывод будет совсем нечитаемым. В зависимости от вашей платформы, вы также можете попробовать valgrind
,
Несмотря на это, главная проблема с вашим кодом заключается в том, что pc
является локальной переменной Становится недействительным в конце Init()
, так как он (вероятно, с каждым компилятором) расположен в стеке.
Вы, вероятно, захотите сделать
w->o = (obj *) malloc(sizeof(obj));
вместо.
Альтернативный способ сделать это - изменить тип o в w на obj
вместо obj *
,
Примечание по управлению памятью в C
(дальнейшее чтение настоятельно рекомендуется):
Аргументы функций и локальные переменные обычно лежат на так называемых stack
, Они становятся недействительными, как только функция возвращается. Размер стековых объектов всегда должен быть известен компилятору.
Объекты, которые выделяются через malloc()
лежать в куче. malloc()
может использоваться для объектов неизвестного размера. Объекты, выделенные malloc()
оставаться в силе, пока их указатели не будут переданы realloc()
или же free()
, Если вы забыли пройти malloc
указатель на free
, у вас есть утечка памяти. Если вы получите доступ к объекту после того, как он стал недействительным, вы получите неопределенное поведение (это место в куче может быть использовано кем-то еще сейчас, но, возможно, данные все еще там, кто знает?). В худшем случае вы можете получить ошибку сегментации или повреждение кучи. Если вы передаете неверный указатель на объект free()
или же realloc()
вы, вероятно, получите кучу коррупции.
Глобальные переменные и члены статических функций лежат где-то еще (tm) и действительны в течение всего выполнения программы (по крайней мере, от ввода main до возврата из нее, как локальной переменной main).
malloc()
это сложная функция, которая внутренне управляет доступными блоками памяти в сложной структуре данных, называемой кучей. Повреждение кучи означает, что вы повредите эту структуру данных. Ошибка сегментации означает, что вы пишете / читаете где-то за пределами допустимого блока памяти.
Обратите внимание, что C
Стандарт не гарантирует, если это. Это не гарантирует, что что-то вроде стека вообще существует. Однако все известные мне компиляторы делают это так. Для некоторых встроенных платформ, хотя, malloc()
довольно простая функция, которая просто увеличивает указатель каждый раз, когда что-то выделяется, и free()
вообще ничего не делает
В Init ()
static void Init( world *w ) {
obj pc;
w->o = &pc; //(1)
w->o->val = 10;
w->o->f.Doit = Read;
w->o->f.Doit();
}
obj pc; является автоматической переменной в функции Init ().
Память, на которую он указывает, будет недействительной после повторной установки из Init (). Так что в main () после Init() w->o будет недействительным.
вы используете malloc
w->o = (obj *) malloc(sizeof(obj));
Вы можете сделать это в Init () или main ().
Примечание: вы должны освободить это с помощью free (), иначе произойдет утечка памяти.