Сбой закрыть файлы или свободную динамическую память, когда они больше не необходимы
Сбой закрыть файлы или свободную динамическую память, когда они больше не needed.[1]
Это средство проверки проверяет на эти проблемы:
Утечка памяти.
Утечка ресурсов.
Специфичная для потока утечка памяти.
Утечка памяти происходит, когда вы не освобождаете блок от памяти, выделенной через malloc
, calloc
, realloc
, или new
. Если память выделяется в функции, дефект не происходит если:
В функции вы освобождаете память с помощью free
или delete
.
Функция возвращает указатель, присвоенный malloc
, calloc
, realloc
, или new
.
Функция хранит указатель в глобальной переменной или в параметре.
Динамическое выделение памяти функционирует, такие как malloc
выделите память на куче. Если вы не выпускаете память после использования, вы уменьшаете объем памяти, доступный для другого выделения. На встраиваемых системах с ограниченной памятью вы можете закончить тем, что исчерпали доступную память кучи даже во время выполнения программы.
Определите осциллограф, где к динамически выделенной памяти получают доступ. Освободите блок памяти в конце этого осциллографа.
Чтобы освободить блок от памяти, используйте free
функция на указателе, который использовался во время выделения памяти. Например:
ptr = (int*)malloc(sizeof(int)); //... free(ptr);
Это - хорошая практика, чтобы выделить и освободить память в том же модуле на том же уровне абстракции. Например, в этом примере, func
выделяет и освобождает память на том же уровне, но func2
не делает.
void func() { ptr = (int*)malloc(sizeof(int)); { ... } free(ptr); } void func2() { { ptr = (int*)malloc(sizeof(int)); ... } free(ptr); }
#include<stdlib.h>
#include<stdio.h>
void assign_memory(void)
{
int* pi = (int*)malloc(sizeof(int));
if (pi == NULL)
{
printf("Memory allocation failed");
return;
}
*pi = 42;
/* Defect: pi is not freed */
}
В этом примере, pi
динамически выделяется malloc
. Функциональный assign_memory
не освобождает память, и при этом она не возвращает pi
.
Одна возможная коррекция должна освободить память, на которую ссылается pi
использование free
функция. free
функция должна быть вызвана перед функциональным assign_memory
завершает работу
#include<stdlib.h> #include<stdio.h> void assign_memory(void) { int* pi = (int*)malloc(sizeof(int)); if (pi == NULL) { printf("Memory allocation failed"); return; } *pi = 42; /* Fix: Free the pointer pi*/ free(pi); }
Другая возможная коррекция должна возвратить указатель pi
. Возврат pi
позволяет функцию, вызывая assign_memory
освободить блок памяти с помощью pi
.
#include<stdlib.h> #include<stdio.h> int* assign_memory(void) { int* pi = (int*)malloc(sizeof(int)); if (pi == NULL) { printf("Memory allocation failed"); return(pi); } *pi = 42; /* Fix: Return the pointer pi*/ return(pi); }
Утечка ресурсов происходит, когда вы открываете поток файла при помощи FILE
указатель, но не закрывает его прежде:
Конец осциллографа указателя.
Присвоение указателя на другой поток.
Если вы не делаете указателей файла версии явным образом как можно скорее, отказ может произойти из-за исчерпания ресурсов.
Закройте FILE
указатель перед концом его осциллографа, или прежде чем вы присвоите указатель на другой поток.
FILE
Указатель, не выпущенный перед концом осциллографа#include <stdio.h>
void func1( void ) {
FILE *fp1;
fp1 = fopen ( "data1.txt", "w" );
fprintf ( fp1, "*" );
fp1 = fopen ( "data2.txt", "w" );
fprintf ( fp1, "!" );
fclose ( fp1 );
}
В этом примере, указатель файла fp1
указывает на файл data1.txt
. Перед fp1
явным образом отделен от потока файла data1.txt
, это используется, чтобы получить доступ к другому файлу data2.txt
.
FILE
УказательОдна возможная коррекция должна явным образом отделить fp1
от потока файла data1.txt
.
#include <stdio.h> void func1( void ) { FILE *fp1; fp1 = fopen ( "data1.txt", "w" ); fprintf ( fp1, "*" ); fclose(fp1); fp1 = fopen ( "data2.txt", "w" ); fprintf ( fp1, "!" ); fclose ( fp1 ); }
Специфичная для потока утечка памяти происходит, когда вы не освобождаете специфичную для потока динамически выделенную память перед концом потока.
Чтобы создать специфичное для потока устройство хранения данных, вы обычно выполняете эти шаги:
Вы создаете ключ для специфичного для потока устройства хранения данных.
Вы создаете потоки.
В каждом потоке вы выделяете устройство хранения данных динамически и затем сопоставляете ключ с этим устройством хранения данных.
После ассоциации можно считать хранимые данные позже с помощью ключа.
Перед концом потока вы освобождаете специфичную для потока память с помощью ключа.
Средство проверки отмечает пути к выполнению в потоке, где последний шаг отсутствует.
Средство проверки работает над этими семействами функций:
tss_get
и tss_set
(C11)
pthread_getspecific
и pthread_setspecific
(POSIX)
Данные, хранимые в памяти, доступны для других процессов даже после того, как потоки закончатся (утечка памяти). Помимо уязвимостей системы обеспечения безопасности, утечки памяти могут уменьшить сумму доступной памяти и уменьшать эффективность.
Свободная динамически выделенная память перед концом потока.
Можно явным образом освободить динамически выделенную память с функциями, такими как free
.
В качестве альтернативы, когда вы создаете ключ, можно сопоставить функцию деструктора с ключом. Функция деструктора вызвана со значением ключа в качестве аргумента в конце потока. В теле функции деструктора можно освободить любую память, сопоставленную с ключом. Если вы используете этот метод, Средство поиска Ошибки все еще отмечает дефект. Проигнорируйте этот дефект с соответствующими комментариями. Смотрите Результаты Polyspace Адреса Через Исправления ошибок или Выравнивания.
#include <threads.h> #include <stdlib.h> /* Global key to the thread-specific storage */ tss_t key; enum { MAX_THREADS = 3 }; int add_data(void) { int *data = (int *)malloc(2 * sizeof(int)); if (data == NULL) { return -1; /* Report error */ } data[0] = 0; data[1] = 1; if (thrd_success != tss_set(key, (void *)data)) { /* Handle error */ } return 0; } void print_data(void) { /* Get this thread's global data from key */ int *data = tss_get(key); if (data != NULL) { /* Print data */ } } int func(void *dummy) { if (add_data() != 0) { return -1; /* Report error */ } print_data(); return 0; } int main(void) { thrd_t thread_id[MAX_THREADS]; /* Create the key before creating the threads */ if (thrd_success != tss_create(&key, NULL)) { /* Handle error */ } /* Create threads that would store specific storage */ for (size_t i = 0; i < MAX_THREADS; i++) { if (thrd_success != thrd_create(&thread_id[i], func, NULL)) { /* Handle error */ } } for (size_t i = 0; i < MAX_THREADS; i++) { if (thrd_success != thrd_join(thread_id[i], NULL)) { /* Handle error */ } } tss_delete(key); return 0; }
В этом примере, функции запуска каждого потока func
вызывает две функции:
add_data
: Эта функция выделяет устройство хранения данных динамически и сопоставляет устройство хранения данных с ключом с помощью tss_set
функция.
print_data
: Эта функция читает хранимые данные с помощью tss_get
функция.
В точках, где func
возвращается, динамически выделенное устройство хранения данных не было освобождено.
Одна возможная коррекция должна освободить динамически выделенную память явным образом прежде, чем оставить функцию запуска потока. Смотрите подсвеченное изменение в откорректированной версии.
В этой откорректированной версии дефект все еще появляется на return
оператор в разделе обработки ошибок func
. Дефект не может произойти на практике, потому что раздел обработки ошибок вводится, только если динамическое выделение памяти перестало работать. Проигнорируйте этот остающийся дефект с соответствующими комментариями. Смотрите Результаты Polyspace Адреса Через Исправления ошибок или Выравнивания.
#include <threads.h>
#include <stdlib.h>
/* Global key to the thread-specific storage */
tss_t key;
enum { MAX_THREADS = 3 };
int add_data(void) {
int *data = (int *)malloc(2 * sizeof(int));
if (data == NULL) {
return -1; /* Report error */
}
data[0] = 0;
data[1] = 1;
if (thrd_success != tss_set(key, (void *)data)) {
/* Handle error */
}
return 0;
}
void print_data(void) {
/* Get this thread's global data from key */
int *data = tss_get(key);
if (data != NULL) {
/* Print data */
}
}
int func(void *dummy) {
if (add_data() != 0) {
return -1; /* Report error */
}
print_data();
free(tss_get(key));
return 0;
}
int main(void) {
thrd_t thread_id[MAX_THREADS];
/* Create the key before creating the threads */
if (thrd_success != tss_create(&key, NULL)) {
/* Handle error */
}
/* Create threads that would store specific storage */
for (size_t i = 0; i < MAX_THREADS; i++) {
if (thrd_success != thrd_create(&thread_id[i], func, NULL)) {
/* Handle error */
}
}
for (size_t i = 0; i < MAX_THREADS; i++) {
if (thrd_success != thrd_join(thread_id[i], NULL)) {
/* Handle error */
}
}
tss_delete(key);
return 0;
}
Разрешимость: неразрешимый |
[1] Выписки из стандарта "Техническая характеристика ISO/IEC TS 17961 - 2013-11-15" воспроизводятся с соглашением о AFNOR. Только исходный и полный текст стандарта, как опубликовано Выпусками AFNOR - доступный через веб-сайт www.boutique.afnor.org - имеет нормативное значение.
1. Если смысл перевода понятен, то лучше оставьте как есть и не придирайтесь к словам, синонимам и тому подобному. О вкусах не спорим.
2. Не дополняйте перевод комментариями “от себя”. В исправлении не должно появляться дополнительных смыслов и комментариев, отсутствующих в оригинале. Такие правки не получится интегрировать в алгоритме автоматического перевода.
3. Сохраняйте структуру оригинального текста - например, не разбивайте одно предложение на два.
4. Не имеет смысла однотипное исправление перевода какого-то термина во всех предложениях. Исправляйте только в одном месте. Когда Вашу правку одобрят, это исправление будет алгоритмически распространено и на другие части документации.
5. По иным вопросам, например если надо исправить заблокированное для перевода слово, обратитесь к редакторам через форму технической поддержки.