Что такое «определенно потерянная» память?
Прежде всего, давайте обсудим, что Valgrind сообщает как «определенно потерянный»: Valgrind сообщит о выделенной памяти как «определенно потерянной», если все ссылки на выделенную память будут потеряны до окончания программы. Другими словами, если ваша программа достигает состояния, в котором есть выделенная память, которая не может быть освобождена из-за отсутствия действительных указателей на нее, это будет считаться «определенно потерянным».
Это означает, что такая программа :
int main(void) {
char *buf = malloc(10);
// ...
exit(0);
}
вызовет без ошибок от Valgrind, в то время как программа, подобная этой:
void func(void) {
char *buf = malloc(10);
// ...
} // memory is definitely lost here
int main(void) {
func();
exit(0);
}
, вызовет "определенно потерянную" ошибку.
Почему первая версия подходит для Valgrind? Это потому, что память всегда освобождается системой при выходе из программы. Если вы продолжаете использовать выделенный кусок памяти до конца вашей программы, на самом деле нет необходимости явно вызывать free()
для него, и это можно рассматривать как просто трата времени. По этой причине, если вы не освобождаете какой-то выделенный блок, сохраняя при этом ссылку на него, Valgrind предполагает, что вы сделали это, чтобы избежать «бесполезного» * 1018 *, потому что вы умны и знаете, что ОС позаботится об этом. об этом все равно.
Если, однако, вы забыли что-то free()
и потеряли все ссылки на него, то Valgrind предупредит вас, потому что у вас должно быть free()
d памяти. Если вы этого не сделаете, и программа продолжит работу, то каждый раз, когда вводится ошибочный блок, происходит то же самое, и вы теряете память. Это то, что называется «утечка памяти». Очень простым примером является следующий:
void func(void) {
char *buf = malloc(10);
// ...
} // memory is definitely lost here
int main(void) {
while (1) {
func();
}
exit(0);
}
Эта программа заставит вашу машину исчерпать память и в конечном итоге может привести к уничтожению или зависанию вашей системы (предупреждение: не проверяйте это, если не хотите рискнуть заморозить свой P C). Если вместо этого вы правильно вызовете free(buf)
до конца func
, то программа продолжит работать без проблем.
Что происходит в вашей программе
Теперь давайте посмотрим, где вы размещаете память и где переменные, содержащие ссылки, объявлены. Единственная часть программы, которая выделяет память, находится внутри блока if (rc == 0)
, до strdup
, здесь:
char *myargs[3];
myargs[0] = strdup("wc"); // program: "wc" (word count)
myargs[1] = strdup("p4.c"); // argument: file to count
Два вызова strdup()
дублируют строку и выделяют новую память для выполнения так. Затем вы сохраняете ссылку на вновь выделенную память в массиве myargs
, который объявлен внутри блока if
. Если ваша программа выходит из блока без освобождения выделенной памяти, эти ссылки будут потеряны, и ваша программа не сможет освободить память.
С execvp()
: ваш дочерний процесс заменяется новым процессом (wc p4.c
), а пространство памяти родительского процесса выбрасывается операционной системой (для Valgrind это точно так же, как завершение программы). Эта память не считается потерянной Valgrind, потому что ссылки на выделенную память все еще присутствуют при вызове execvp()
. ПРИМЕЧАНИЕ: это , а не , потому что вы передаете указатели на выделенную память для execvp()
, это потому, что исходная программа эффективно завершается и память сохраняется ОС.
Без execvp()
: ваш дочерний процесс продолжает выполнение и сразу после выхода из блока кода, в котором определено myargs
, он теряет любую ссылку на выделенную память (так как myargs[0]
и myargs[1]
были единственные ссылки). Затем Valgrind правильно сообщает об этом как о «определенно потерянных», 8 байтов (3 для "wc"
и 5 для "p4.c"
) в 2 блоках (2 выделения). То же самое происходит, если по какой-либо причине не удается вызвать execvp()
.
Дополнительные соображения
Если быть честным, в программе, которую вы показываете, нет необходимости вызывать strdup()
. Это не так, как эти строки должны быть скопированы, потому что они используются где-то еще (или что-то в этом роде). Код мог бы быть просто:
myargs[0] = "wc"; // program: "wc" (word count)
myargs[1] = "p4.c"; // argument: file to count
В любом случае, при использовании семейства функций exec*()
рекомендуется ставить exit()
непосредственно после него, чтобы программа не продолжала работать в случае сбоя exec*()
. Примерно так:
execvp(myargs[0], myargs);
perror("execvp failed");
exit(1);