Не удается закрыть файлы или освободить динамическую память, когда они больше не нужны
Не удается закрыть файлы или освободить динамическую память, когда они больше не нужны.[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
функция. The 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
.
Кроме того, когда вы создаете ключ, можно связать функцию деструктора с клавишей. Функция деструктора вызывается со значением ключа в качестве аргумента в конце потока. В теле функции деструктора можно освободить любую память, связанную с клавишей. Если вы используете этот метод, Bug Finder все еще помечает дефект. Игнорируйте этот дефект с соответствующими комментариями. Смотрите Адрес Результаты 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;
}
Решимость: Undecidable |
[1] Выдержки из стандарта «Техническая спецификация ISO/IEC TS 17961 - 2013-11-15» воспроизводятся с согласия АФНОР. Только оригинальный и полный текст стандарта, опубликованный AFNOR Editions - доступный через веб-сайт www.boutique.afnor.org - имеет нормативное значение.
1. Если смысл перевода понятен, то лучше оставьте как есть и не придирайтесь к словам, синонимам и тому подобному. О вкусах не спорим.
2. Не дополняйте перевод комментариями “от себя”. В исправлении не должно появляться дополнительных смыслов и комментариев, отсутствующих в оригинале. Такие правки не получится интегрировать в алгоритме автоматического перевода.
3. Сохраняйте структуру оригинального текста - например, не разбивайте одно предложение на два.
4. Не имеет смысла однотипное исправление перевода какого-то термина во всех предложениях. Исправляйте только в одном месте. Когда Вашу правку одобрят, это исправление будет алгоритмически распространено и на другие части документации.
5. По иным вопросам, например если надо исправить заблокированное для перевода слово, обратитесь к редакторам через форму технической поддержки.