Как используется атрибут malloc gcc?

Я не совсем уверен, что правильно понимаю атрибут gccmalloc.

Например, взгляните на эту программу:

#include <stdio.h>
#include <stdlib.h>

__attribute__((returns_nonnull, malloc, malloc(free, 1)))
void *safe_malloc(size_t n)
{
    void *memory = malloc(n);
    if (memory == NULL)
    {
        fprintf(stderr, "failed: malloc(%zu)\n", n);
        exit(EXIT_FAILURE);
    }

    return memory;
}

typedef struct
{
    size_t n, capacity;
    int *elements;
} array_list;

__attribute__((nonnull))
void free_array_list(array_list *lst)
{
    if (lst->elements != NULL)
    {
        free(lst->elements);
    }
    free(lst);
}


__attribute__((returns_nonnull, malloc, malloc(free_array_list, 1)))
array_list *new_array_list()
{
    array_list *lst = safe_malloc(sizeof(array_list));
    lst->elements = NULL;
    lst->capacity = 0;
    lst->n = 0;

    return lst;
}

int main(void)
{
    array_list *lst = new_array_list();
    free_array_list(lst);
    
    return EXIT_SUCCESS;
}

Насколько я понял __attribute__((..., malloc, malloc(free_array_list, 1))), это было так

  • malloc означало, что new_array_list возвращает новую память, аналогичную malloc, и
  • malloc(free_array_list, 1) означало, что память, возвращенная new_array_list, должна быть освобождена free_array_list.

Подобно инициализатору и деструктору, new_array_list возвращает что-то, что позже должно быть очищено free_array_list. Но если вы скомпилируете этот фрагмент кода с помощью -fanalyzer (я пробовал версии gcc 11.3.0 и 12.3.0), появится предупреждение о том, что память, выделенная в new_array_list с помощью safe_malloc, утечка.

Что я делаю не так?

Обратите внимание, что тест if (lst->elements != NULL) не нужен для free(lst->elements);, но if (lst != NULL) полезен перед free(lst->elements);, чтобы избежать UB, если lst == NULL.

chux - Reinstate Monica 28.07.2024 05:08

@chux-ReinstateMonica Хорошая мысль, не знаю, почему я это сюда поместил 😅 Интересно, что clangd на самом деле помечается if (lst != NULL) из-за __attribute__((nonnull)).

Knogger 28.07.2024 05:15

На информационной странице говорится: «Чтобы указать, что функция распределения одновременно удовлетворяет свойству отсутствия псевдонимов и имеет связанный с ней механизм освобождения, необходимо использовать как простую форму атрибута, так и форму с аргументом DEALLOCATOR».

Allan Wind 28.07.2024 05:52

Я вручную встроил safe_malloc(), чтобы упростить код, но получил странные предупреждения: утечка lst, использование после free_array_list из lst и lst должно было быть освобождено с помощью free, но было освобождено с помощью free_array_list. Последние предполагают, что gcc не глючит malloc (free_array_list, 1). cpp сообщает мне, что (stdlib) malloc() имеет атрибуты __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) __attribute__ ((__alloc_size__ (1))), возможно, без Dealloc из-за realloc()?

Allan Wind 28.07.2024 06:50
gcc.gnu.org/bugzilla/show_bug.cgi?id=105530
Allan Wind 28.07.2024 07:04
gcc.gnu.org/bugzilla/show_bug.cgi?id=98992 предложите регрессию атрибута malloc с указанным освободителем.
Allan Wind 28.07.2024 07:09
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
6
89
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваше понимание правильное и соответствует документации, поэтому я думаю, что вы обнаружили одну или обе эти ошибки в анализаторе 98992 (примечание: регрессия из gcc 11.3) и 105530. В качестве обходного пути, возможно, подойдет версия без malloc(free_array_list, 1))? Рассмотрите возможность использования calloc() вместо malloc() с последующей инициализацией вручную.

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    size_t capacity;
    size_t n;
    int *elements;
} array_list;

__attribute__ ((nonnull)) void free_array_list(array_list *lst);
__attribute__ ((returns_nonnull, malloc)) array_list *new_array_list();

array_list *new_array_list() {
    array_list *lst = malloc(sizeof *lst);
    if (!lst) {
        fprintf(stderr, "failed: malloc(%zu)\n", sizeof *lst);
        exit(EXIT_FAILURE);
    }
    lst->elements = NULL;
    lst->capacity = 0;
    lst->n = 0;
    return lst;
}

void free_array_list(array_list *lst) {
    // safe if allocated with new_array_list otherwise you need
    // an if (lst) guard.  free(NULL) is a safe no-op so
    // if (lst->elements) isn't needed.
    free(lst->elements);
    free(lst);
}

int main(void) {
    array_list *lst = new_array_list();
    free_array_list(lst);
}

и пример компиляции:

$ gcc -fanalyzer -Wall -Wextra your_program.c
$

Спасибо за исследование! Это действительно удивительно, судя по тому, что я нашел, статический анализ в gcc уже довольно зрелый.

Knogger 28.07.2024 07:28
gcc.gnu.org/wiki/StaticAnalyzer — это полезная ссылка, позволяющая следить за развитием статического анализа.
Allan Wind 28.07.2024 07:31

Другие вопросы по теме

Класс времени компиляции C++ с переменной-членом std::vector и использование ее данных во время выполнения
Gcc и clang не согласны с использованием шаблонов псевдонимов в качестве аргумента шаблона шаблона
Настройка libstdc++ для данной версии gcc
Почему я получаю сообщение «объявление for не относится к классу, шаблону класса или частичной специализации шаблона класса» в Clang, а не в GCC
GCC 14 «возможно, висячая ссылка на временное» предупреждение или нет, в зависимости от аргумента функции
Почему исключение не может быть перехвачено в Windows с помощью msvc, а в Linux - с помощью GCC
Ограничение встроенной сборки gnu `i` для адреса памяти
Могу ли я перезаписать malloc, вызываемый библиотечными функциями?
Есть ли способ явно указать компилятору остановить оптимизацию?
Неоднозначное разрешение шаблонов функций-членов и функций, не являющихся членами C++, в GCC 14, но не в предыдущих версиях GCC