Я новичок в C и решил создать общую векторную библиотеку. Вот код:
#ifndef VECTOR_H
#define VECTOR_H
#define DEFAULT_CAPACITY 10
#define EXPAND_RATIO 1.5
#include <stdlib.h>
#include <string.h>
enum error {
NO_ERROR,
INDEX_ERROR,
KEY_ERROR,
NULL_POINTER_ERROR,
MEMORY_ERROR
};
#define VECTOR_NEW(vec_name, type)
/* a resizable array */
struct vec_name {
size_t length;
size_t capacity;
type* items;
};
/* initializes a vector with a specific capacity */
enum error init_##vec_name##_c(struct vec_name* vec, size_t capacity) {
if (!vec)
return NULL_POINTER_ERROR;
vec->length = 0;
vec->capacity = capacity;
vec->items = malloc(capacity * sizeof(*vec->items));
return (vec->items) ? NO_ERROR : MEMORY_ERROR;
}
/* initializes a vector with the default capacity */
static inline enum error init_##vec_name(struct vec_name* vec) {
return init_##vec_name##_c(vec, DEFAULT_CAPACITY);
}
/* frees the memory allocated for the vector */
static inline enum error free_##vec_name(struct vec_name* vec) {
if (!vec)
return NULL_POINTER_ERROR;
free(vec->items);
vec->items = NULL;
return NO_ERROR;
}
/* adds an item to the end of the vector */
enum error append_##vec_name(struct vec_name* vec, type item) {
if (!vec)
return NULL_POINTER_ERROR;
if (vec->length >= vec->capacity) {
vec->capacity *= EXPAND_RATIO;
vec->items =
realloc(vec->items, vec->capacity * sizeof(vec->items));
if (!vec->items)
return MEMORY_ERROR;
}
vec->items[vec->length++] = item;
return NO_ERROR;
}
/* removes and item from the vector at a specified index (-1 as an index
* is pop back)
*/
static inline enum error pop_##vec_name(struct vec_name* vec,
size_t index, type* item) {
if (!vec)
return NULL_POINTER_ERROR;
if (vec->length <= index)
return INDEX_ERROR;
if (item)
*item = vec->items[index];
const size_t block_size = (vec->length - index - 1) * sizeof(type);
memmove(&(vec->items[index]), &(vec->items[index + 1]), block_size);
--vec->length;
return NO_ERROR;
}
/* sets an existing vector index to a specifid item */
static inline enum error set_##vec_name(struct vec_name* vec,
size_t index, type item) {
if (!vec)
return NULL_POINTER_ERROR;
if (vec->length <= index)
return INDEX_ERROR;
vec->items[index] = item;
return NO_ERROR;
}
/* applies the given function pointer to every item in the vector */
static inline enum error map_##vec_name(struct vec_name* vec,
void(func)(type*)) {
if (!vec)
return NULL_POINTER_ERROR;
for (size_t i = 0; i < vec->length; ++i)
func(&vec->items[i]);
return NO_ERROR;
}
/* places the item at a specifed index in the`value` parameter */
static inline enum error get_##vec_name(struct vec_name* vec,
size_t index, type* value) {
if (!vec)
return NULL_POINTER_ERROR;
if (vec->length <= index)
return INDEX_ERROR;
*value = vec->items[index];
return NO_ERROR;
}
#endif // VECTOR_H
Вот как это используется:
#include "veclib.h"
VECTOR_NEW(vector_int, int)
int main() {
struct vector_int vec;
init_vector_int(&vec);
for (size_t i = 0; i <= 10; ++i) append_vector_int(&vec, i);
}
Я не уверен, элегантен ли мой код, быстр и т. Д. Спасибо!
1 ответ
Именование
Очень хорошие комментарии и документация. У меня было четкое представление о том, чего вы пытались достичь.
Вместо того, чтобы давать вашим пользователям возможность создавать любые vec_name
, Я бы подумал, например vec_##name
, чтобы придать моим векторным типам единообразие именования.
Возможно, что не все ваши функции используются; это обычно приводит к тому, что благонамеренные компиляторы выдают предупреждение для static
функции. Я получил этот хороший совет, отключение неиспользуемых статических функций с помощью их.
Распределение
append##vec_name
конвертируется в double
и назад. Это может сработать для DEFAULT_CAPACITY
10, но распечатка,
printf("append " #vec_name " capacity %lu -> ", vec->capacity);
vec->capacity *= EXPAND_RATIO;
printf("%lun", vec->capacity);
Если это 0 или 1, он застревает в неопределенном поведении,
append vec_foo capacity 1 -> 1
На моем компьютере код работает для одного перераспределения, а затем возникает ошибка сегментации. Легче отлаживать, когда распределение памяти находится в одной функции, а не в обеих init_##vec_name##_c
и append_##vec_name
. Предложите особенность realloc
,
Если ptr является нулевым указателем, realloc () должен быть эквивалентен malloc () для указанного размера.
Функции
В pop##vec_name
, в документации указано, что -1 — это задняя часть, что полезно, вроде Python, но я не вижу кода.
В set##vec_name
, вы используете задание. Для больших шрифтов это может означать много копирования. Было бы быстрее построить его на месте; это вроде как разница между push_back
и emplace_back
в C++
.
Значение vec
является const
в set_##vec_name
, map_##vec_name
, и get_##vec_name
. Это может помочь компилятору более агрессивно оптимизироваться и может быть важной самостоятельной документацией: это утверждение, что топология вектора не изменится.
Опечатка: уточняется.
Ошибки
Ваша абстракция ошибок и передача их пользователю великолепны. stdio.h
не нужен и даже не входит.
enum error
имеет высокую вероятность конфликтов пространства имен с другими заголовками. Возможно, вы захотите использовать возможности, указанные в errno.h
вместо того, чтобы изобретать свои собственные. Вместо того, чтобы возвращать одно из нескольких значений, одно возвращает бит (нулевое значение или флаг), предлагая вашим пользователям проверить errno
. Это также упростит передачу ошибок из стандартной библиотеки, поскольку вы можете просто передать ошибку вызывающей стороне вместо создания новой ошибки.
Это зависит от того, какой вариант использования для этого, но я бы подумал о том, чтобы сделать ошибки программирования assert
вместо динамических проверок (например NULL_POINTER_ERROR
.) Я обнаружил, что это освобождает множество случаев от тестирования и проверки.
В
set##vec_name
, вы используете задание. Это ограничивает векторы присваиваемыми типами. Я бы вообще поставил под сомнение использование этой функции. — Не могли бы вы подробнее рассказать об этом?— ксилпекс
Я отредактировал это — например а
struct
или жеunion
не будет назначен. Более общийmemset(...sizeof item...)
как это было сделано для конструктора, является более общим.— Нил
На
restrict
ключевое слово — Будетstatic inline enum error pop_##vec_name(struct vec_name* restrict vec, size_t index, type* restrict item)
быть хорошей идеей (я не использовалrestrict
перед)?— ксилпекс
Я думаю, это может помочь, см. использование ограничения, но это влияет на указатели совместимых типов.
— Нил
Я видел это, но является ли мое использование вышеупомянутым хорошей идеей?
— ксилпекс
Показать 1 больше комментариев