Другая реализация динамического массива на C

Я видел довольно много сообщений о создании динамического массива, поэтому я попытался создать свой собственный в учебных целях.

Вот:

#ifndef VECTOR_H
#define VECTOR_H

#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#ifndef VECTOR_RESERVE_SIZE 
#define VECTOR_RESERVE_SIZE 4
#endif

//Declaration

typedef struct
{
        int size_of_each;
        int reserved;
        int used;
        void* data;
} vector_t;

bool vector_init(vector_t *, int);
void vector_append(vector_t *, void *);
void* vector_get(vector_t *, int );
void vector_set(vector_t *, int, void *);
void vector_free(vector_t *);

//Implementation

bool vector_init(vector_t *p, int s)
{
        p->size_of_each = s;
        p->reserved = VECTOR_RESERVE_SIZE;
        p->used = 0;
        p->data = malloc(VECTOR_RESERVE_SIZE * p->size_of_each);

        if (p->data != NULL) { return false; }
        else { return true; }
}

void vector_append(vector_t *p, void *d)
{
        if (p->used == p->reserved)
        {
                p->data = realloc(p->data, VECTOR_RESERVE_SIZE);
                //printf("reallocated n");
                p->reserved += VECTOR_RESERVE_SIZE;
        }
        memcpy((void*)((char*)p->data + p->size_of_each * p->used),  // copy to back of the vector
                        d, p->size_of_each);

        p->used += 1;
}

void* vector_get(vector_t *p, int i)
{
        if (!(i > p->used))
        {
                return (void*)((char*)p->data + p->size_of_each * i);
        }
        else { return NULL; }
}

void vector_set(vector_t *p, int i, void *d)
{
        if (!(i > p->used))
        {
                memcpy((void*)((char*)p->data + p->size_of_each * i), d, p->size_of_each);
        }
}

void vector_free(vector_t *p)
{
        free(p->data);
}

#endif

Вот пример его использования:

#include <stdio.h>

#define VECTOR_RESERVE_SIZE 3
#include "vector.h"

int main()
{
        int a = 321;
        int b = 45;
        int c = 21;
        int d = 31;
        int e = 71;

        vector_t v;
        vector_init(&v, sizeof (int));

        vector_append(&v, &a);
        vector_append(&v, &b);
        vector_append(&v, &c);
        vector_append(&v, &d);
        vector_append(&v, &e);

        printf("1st element is  %d n", *(int*)vector_get(&v, 0));
        printf("2nd element is  %d n", *(int*)vector_get(&v, 1));
        printf("3th element is  %d n", *(int*)vector_get(&v, 2));
        printf("4th element is  %d n", *(int*)vector_get(&v, 3));
        printf("5th element is  %d nn", *(int*)vector_get(&v, 4));

        vector_set(&v, 4, &a);
        printf("4th element after setting is  %d nn", *(int*)vector_get(&v, 4));

        //contiguous
        printf("size of int is: %d n", sizeof (int));
        printf("address of 1st element: %p n", vector_get(&v, 0));
        printf("address of 2nd element: %p n", vector_get(&v, 1));
        printf("address of 3rd element: %p n", vector_get(&v, 2));
        printf("address of 4th element: %p n", vector_get(&v, 3));
        printf("address of 5th element: %p n", vector_get(&v, 4));

        vector_free(&v);
}

Я уже писал на C ++ раньше, и это был один из моих первых проектов на C.

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

Изменить: обнаружена большая проблема

это:
p->data = realloc(p->data, VECTOR_RESERVE_SIZE);

должно быть это:
p->data = realloc(p->data, (p->reserved * p->size_of_each) + VECTOR_RESERVE_SIZE);

1 ответ
1

В C, как и в C ++, указатели можно свободно преобразовывать в void*, поэтому можно удалить довольно много слепков.

Наверное удобно хранить data есть char*, так как мы его используем именно так. Мы все еще должны использовать void* как внешний интерфейс, конечно.

Это опасный антипаттерн:

            p->data = realloc(p->data, VECTOR_RESERVE_SIZE);

Если realloc() терпит неудачу, он возвращает нулевой указатель. Путем перезаписи p->data прежде чем мы проверим значение null, мы пропускаем старое значение p->data, который больше не доступен. Также отсутствует проверка на null, поэтому последующий доступ — Undefined Behavior.

По стилю, я думаю, что найду (!(i > p->used)) легче читать как (i <= p->used). И if (p->data != NULL) { return false; } else { return true; } в качестве return !p->data;.

Код примера должен служить хорошим примером и проверять возвращаемое значение vector_init() (а также vector_append() как только вы поймете, что это должно указывать на успех / неудачу) и отреагируйте соответствующим образом. В противном случае вы обнаружите, что поощряете небрежное использование.

  • Спасибо за обратную связь, у меня проблема, когда я пытаюсь реализовать эти проверки безопасности в vector_append (), я получаю segfault, когда я пытаюсь просто подтвердить перераспределенный указатель, и я не могу понять, почему

    — CheeseMan69

  • Очевидно, что утверждать неправильно, поскольку мы знаем, что указатель может быть нулевым.

    — Тоби Спейт

  • Я не понимаю, утверждая, что имею в виду следующее: assert (ptr! = NULL);

    — CheeseMan69


  • Да вот почему это неправильно. realloc() может возвращать нулевой указатель, поэтому утверждение неверно.

    — Тоби Спейт

  • Вам нужно что-то большее, например char *ptr = realloc(p->data, new_size); if (!ptr) return false; p->data = ptr; …; return true;.

    — Тоби Спейт

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

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