ISO/IEC TS 17961 [fileclose]

Сбой закрыть файлы или свободную динамическую память, когда они больше не необходимы

Описание

Управляйте определением

Сбой закрыть файлы или свободную динамическую память, когда они больше не необходимы. [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);
}
См. правило MEM00-C CERT-C.

Пример - динамическая память, не выпущенная перед концом функции

#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);                   
}

Пример - утечка памяти с новым/удаляют

#define NULL '\0'

void initialize_arr1(void)
{
    int *p_scalar = new int(5);
}

void initialize_arr2(void)
{
    int *p_array = new int[5];
}

В этом примере функции создают две переменные, p_scalar и p_array, с помощью ключевого слова new. Однако функции заканчиваются, не очищая память для этих указателей. Поскольку функции использовали new, чтобы создать эти переменные, необходимо очистить их память путем вызова delete в конце каждой функции.

Исправление — добавляет, удаляют

Чтобы исправить эту ошибку, добавьте оператор delete для каждой инициализации new. Если бы вы использовали скобки [], чтобы инстанцировать переменной, необходимо вызвать, удаляют со скобками также.

#define NULL '\0'

void initialize_arrs(void)
{
    int *p_scalar = new int(5); 
    int *p_array = new int[5];  

    delete p_scalar;
    p_scalar = NULL;

    delete[] p_array;
    p_scalar = NULL;
}

Описание

Утечка ресурсов происходит, когда вы открываете поток файла при помощи указателя 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 );
}

Описание

Специфичная для потока утечка памяти происходит, когда вы не освобождаете специфичную для потока динамически выделенную память перед концом потока.

Чтобы создать специфичное для потока устройство хранения данных, вы обычно выполняете эти шаги:

  1. Вы создаете ключ для специфичного для потока устройства хранения данных.

  2. Вы создаете потоки.

  3. В каждом потоке вы выделяете устройство хранения данных динамически и затем сопоставляете ключ с этим устройством хранения данных.

    После ассоциации можно считать хранимые данные позже с помощью ключа.

  4. Перед концом потока вы освобождаете специфичную для потока память с помощью ключа.

Средство проверки отмечает пути к выполнению в потоке, где последний шаг отсутствует.

Средство проверки работает над этими семействами функций:

  • 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;
}

Проверяйте информацию

Разрешимость: неразрешимый

Введенный в R2019a


[1]  Выписки из стандарта "Техническая характеристика ISO/IEC TS 17961 - 2013-11-15" воспроизводятся с соглашением о AFNOR. Только исходный и полный текст стандарта, как опубликовано Выпусками AFNOR - доступный через веб-сайт www.boutique.afnor.org - имеет нормативное значение.