Умный / динамический массив в C

Я попытался реализовать интеллектуальный / динамический массив на C, который должен быть гибридом вектора / стека / очереди C ++.

Вот что я придумал:

Заголовок (smart_array.h):

#pragma once

#include <stddef.h>

typedef struct smart_array smart_array_t;

void smart_array_alloc(smart_array_t** array, size_t capacity);
void smart_array_free(smart_array_t** array);
int  smart_array_resize(smart_array_t* array, size_t capacity);

void smart_array_capacity(smart_array_t* array, size_t* capacity);
void smart_array_total(smart_array_t* array, size_t* total);
void smart_array_empty(smart_array_t* array, int* empty);

int  smart_array_set(smart_array_t* array, size_t index, void* item);
int  smart_array_set_back(smart_array_t* array, void* item);
int  smart_array_set_top(smart_array_t* array, void* item);

void smart_array_get(smart_array_t* array, size_t index, void** item);
void smart_array_get_back(smart_array_t* array, void** item);
void smart_array_get_top(smart_array_t* array, void** item);

int  smart_array_push_back(smart_array_t* array, void* item);
int  smart_array_push_top(smart_array_t* array, void* item);

int  smart_array_pop(smart_array_t* array, size_t index);
int  smart_array_pop_back(smart_array_t* array);
int  smart_array_pop_top(smart_array_t* array);
int  smart_array_clear(smart_array_t* array);

Исходный файл: (smart_array.c):

#include "smart_array.h"
#include <stdlib.h>

struct smart_array {
    void** items;
    size_t total;
    size_t capacity;
};

void smart_array_alloc(smart_array_t** array, size_t capacity) {
    if(!(*array = malloc(sizeof(struct smart_array))))
        return;
    if(!((*array)->items = malloc(sizeof(void*) * capacity))) {
        free(*array);
        *array = 0;
        return;
    }
    (*array)->capacity = capacity;
    (*array)->total = 0;
}

void smart_array_free(smart_array_t** array) {
    if(array && *array) {
        free((*array)->items);
        free(*array);
        *array = 0;
    }
}

int smart_array_resize(smart_array_t* array, size_t capacity) {
    if(array) {
        void **items = realloc(array->items, sizeof(void*) * capacity);
        if(items) {
            array->items = items;
            array->capacity = capacity;
            return 0;
        }
    }
    return 1;
}

void smart_array_capacity(smart_array_t* array, size_t* capacity) {
    *capacity = !array ? 0 : array->capacity;
}

void smart_array_total(smart_array_t* array, size_t* total) {
    *total = !array ? 0 : array->total;
}

void smart_array_empty(smart_array_t* array, int* empty) {
    *empty = array->total == 0;
}

int smart_array_set(smart_array_t* array, size_t index, void* item) {
    if(array) {
        if (index >= 0 && index < array->total) {
            array->items[index] = item;
            return 0;
        }
    }
    return 1;
}

int smart_array_set_back(smart_array_t* array, void* item) {
    size_t total;
    smart_array_total(array, &total);
    return smart_array_set(array, total - 1, item);
}

int smart_array_set_top(smart_array_t* array, void* item) {
    return smart_array_set(array, 0, item);
}

void smart_array_get(smart_array_t* array, size_t index, void** item) {
    if(array && item) {
        if (index >= 0 && index < array->total)
            *item = array->items[index];
        else 
            *item = 0;
    }
}

void smart_array_get_back(smart_array_t* array, void** item) {
    size_t total;
    smart_array_total(array, &total);
    smart_array_get(array, total - 1, item);
}

void smart_array_get_top(smart_array_t* array, void** item) {
    smart_array_get(array, 0, item);
}

int smart_array_push_back(smart_array_t* array, void* item) {
    if(array) {
        if(array->capacity == array->total) {
            if(smart_array_resize(array, array->capacity * 2))
                return 1;
        }
        array->items[array->total++] = item;
        return 0;
    }
    return 1;
}

int smart_array_push_top(smart_array_t* array, void* item) {
    if(array) {
        if(array->capacity == array->total) {
            if(smart_array_resize(array, array->capacity * 2))
                return 1;
        }
        array->total++;
        for(size_t s = array->total; s > 0; --s) 
            array->items[s] = array->items[s - 1];
        array->items[0] = item;
        return 0;
    }
    return 1;
}

int smart_array_pop(smart_array_t* array, size_t index) {
    if(array && (index >= 0) && (index < array->total)) {
        array->items[index] = 0;
        for (int i = index; i < array->total - 1; ++i) {
            array->items[i] = array->items[i + 1];
            array->items[i + 1] = NULL;
        }
        array->total--;
        if ((array->total > 0) && ((array->total) == (array->capacity / 4))) {
            if(smart_array_resize(array, array->capacity / 2))
                return 0;
        }
    }
    return 1;
}

int smart_array_pop_back(smart_array_t* array) {
    size_t total;
    smart_array_total(array, &total);
    return smart_array_pop(array, total - 1);
}

int smart_array_pop_top(smart_array_t* array) {
    return smart_array_pop(array, 0);
}

int smart_array_clear(smart_array_t* array) {
    int empty;
    smart_array_empty(array, &empty);
    while(!empty) {
        smart_array_pop_top(array);
        smart_array_empty(array, &empty);
    }
    return 0;
}

Тест (main.c):

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

void print_smart_array(smart_array_t* array) {
    size_t total, capacity;
    
    smart_array_total(array, &total);
    smart_array_capacity(array, &capacity);
    
    printf("Array total: %in", total);
    printf("Array capacity: %in", capacity);
    
    printf("Array items:n");
    for(size_t i = 0; i < total; ++i) {
        int* val;
        smart_array_get(array, i, (void**)&val);
        printf("t%in", *val);
    }
}

int main(void) {
    smart_array_t* a;
    smart_array_alloc(&a, 4);
    
    int* x = malloc(sizeof(int));
    int* y = malloc(sizeof(int));
    int* z = malloc(sizeof(int));
    int* w = malloc(sizeof(int));
    
    *x = 3;
    *y = 56;
    *z = 100;
    *w = 87;
    
    smart_array_push_back(a, x);
    smart_array_push_back(a, x);
    smart_array_push_top(a, w);
    smart_array_push_back(a, w);
    smart_array_push_top(a, y);
    
    print_smart_array(a);
    
    smart_array_clear(a);
    
    print_smart_array(a);
    
    free(x);
    free(y);
    free(z);
    free(w);
    smart_array_free(&a);
}
``` 

1 ответ
1

Файловая структура

Код хорошо организован по тестированию, реализации и заголовкам. Включая stddef.h уместно в вашем заголовке, так как вы используете size_t.

Последовательность

Подумайте о том, чтобы убедиться, что указатели действительны, например, с помощью assertили, по крайней мере, в документации, прежде чем писать им. Пользователи этого кода могут не понять, и для них не рекомендуется тщательно анализировать ваш код, чтобы найти проблему. По крайней мере, пусть он будет последовательным; в smart_array_set он проверяет значение null, когда другие этого не делают.

typedef struct smart_array smart_array_t потенциально проблематично. См. Обсуждение type, за которым следует _t, представляют и Руководство по стилю ядра Linux. Я думаю, что пользователи должны иметь право выражать это как typedef, поэтому я бы просто придерживался struct smart_array; C и C ++ — разные языки.

Возвращаемые значения

Возвращение void из smart_array_alloc проблематично, потому что есть два разных пути; нужно будет запросить указатель, чтобы узнать, удалось ли это. Его должно быть легко использовать должным образом, и в нынешнем виде использование этого без проверки является (недокументированной) ошибкой.

Я ожидал smart_array_get(_*) вернуть товар. В его нынешнем виде у одного есть указатель на переданный элемент, и он возвращает void. Это может раздражать, когда желателен функциональный состав. То же самое и с большинством других функций. По крайней мере, кратко задокументируйте их, объясняя переход от C ++.

Я ожидаю какой-то ошибки или обратной связи при доступе за пределы, но это ничего не дает. Возможно, это не лучший выбор.

Упрощение конструктора

Я ожидал smart_array_alloc будет вызываться с конструктором по умолчанию большую часть времени, поэтому я бы подумал, что он не включает capacity который вызывает тот, который делает.

Два распределения должным образом обрабатываются smart_array_alloc, но их можно упростить, вызвав smart_array_free вместо. Не нужно два выделения, так как он уже выровнен, было бы проще выделить его как одно.

if(!(*array = malloc(sizeof struct smart_array + sizeof(void*) * capacity)
    return (fail);
(*array)->items = (void *)(*array + 1);

Фактически, есть несколько распределений памяти, которые могут вызывать smart_array_resize.

Если ptr является нулевым указателем, realloc () должен быть эквивалентен malloc () для указанного размера.

Я бы рассмотрел возможность разоблачения struct smart_pointer в заголовке для выделения в стеке и еще большего упрощения, но это дизайнерское решение не всегда уместно.

Нулевые значения

smart_array_set_back и smart_array_set_top будет иметь неопределенное поведение, когда пусто или емкость равна нулю.

Использование частных членов

Я думаю это нормально использовать total в качестве члена данных в этом частном коде вместо того, чтобы передавать его через общедоступные внешние smart_array_total. smart_array_clear, с доступом к закрытым членам, должно быть очень простым.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *