Я видел довольно много сообщений о создании динамического массива, поэтому я попытался создать свой собственный в учебных целях.
Вот:
#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 ответ
В 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;
.— Тоби Спейт