Не удается закрыть файлы или освободить динамическую память, если они больше не нужны
Не удается закрыть файлы или освободить динамическую память, если они больше не нужны. [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.
Кроме того, при создании ключа можно связать функцию деструктора с ключом. Функция деструктора вызывается со значением ключа в качестве аргумента в конце потока. В теле функции деструктора можно освободить любую память, связанную с ключом. При использовании этого метода функция поиска ошибок по-прежнему помечает дефект. Игнорировать этот дефект с соответствующими комментариями. См. раздел Результаты анализа пространства адресов с помощью исправлений ошибок или обоснований.
#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. Дефект не может возникнуть на практике, поскольку раздел обработки ошибок вводится только в случае сбоя динамического выделения памяти. Проигнорируйте этот оставшийся дефект с соответствующими комментариями. См. раздел Результаты анализа пространства адресов с помощью исправлений ошибок или обоснований.
#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 Technical Specification - 2013-11-15» воспроизводятся с согласия AFNOR. Нормативную ценность имеет только оригинальный и полный текст стандарта, опубликованный изданиями AFNOR - доступный через веб-сайт www.boutique.afnor.org.
1. Если смысл перевода понятен, то лучше оставьте как есть и не придирайтесь к словам, синонимам и тому подобному. О вкусах не спорим.
2. Не дополняйте перевод комментариями “от себя”. В исправлении не должно появляться дополнительных смыслов и комментариев, отсутствующих в оригинале. Такие правки не получится интегрировать в алгоритме автоматического перевода.
3. Сохраняйте структуру оригинального текста - например, не разбивайте одно предложение на два.
4. Не имеет смысла однотипное исправление перевода какого-то термина во всех предложениях. Исправляйте только в одном месте. Когда Вашу правку одобрят, это исправление будет алгоритмически распространено и на другие части документации.
5. По иным вопросам, например если надо исправить заблокированное для перевода слово, обратитесь к редакторам через форму технической поддержки.