【发布时间】:2015-01-11 18:17:49
【问题描述】:
我是一名同时了解 C 和 C++ 的程序员。我在自己的项目中使用过这两种语言,但我不知道我更喜欢哪一种。
当我用 C 编程时,我最怀念 C++ 的功能是来自 STL(标准模板库)的 std::vector
我还没有弄清楚我应该如何在 C 中表示不断增长的数组。到目前为止,我在项目中的所有地方都复制了我的内存分配代码。我不喜欢代码重复,我知道这是不好的做法,所以这对我来说似乎不是一个很好的解决方案。
我前段时间考虑过这个问题,并想出了使用预处理器宏实现泛型向量的想法。
这是实现的样子:
#ifndef VECTOR_H_
#define VECTOR_H_
#include <stdlib.h>
#include <stdio.h>
/* Declare a vector of type `TYPE`. */
#define VECTOR_OF(TYPE) struct { \
TYPE *data; \
size_t size; \
size_t capacity; \
}
/* Initialize `VEC` with `N` capacity. */
#define VECTOR_INIT_CAPACITY(VEC, N) do { \
(VEC).data = malloc((N) * sizeof(*(VEC).data)); \
if (!(VEC).data) { \
fputs("malloc failed!\n", stderr); \
abort(); \
} \
(VEC).size = 0; \
(VEC).capacity = (N); \
} while (0)
/* Initialize `VEC` with zero elements. */
#define VECTOR_INIT(VEC) VECTOR_INIT_CAPACITY(VEC, 1)
/* Get the amount of elements in `VEC`. */
#define VECTOR_SIZE(VEC) (VEC).size
/* Get the amount of elements that are allocated for `VEC`. */
#define VECTOR_CAPACITY(VEC) (VEC).capacity
/* Test if `VEC` is empty. */
#define VECTOR_EMPTY(VEC) ((VEC).size == 0)
/* Push `VAL` at the back of the vector. This function will reallocate the buffer if
necessary. */
#define VECTOR_PUSH_BACK(VEC, VAL) do { \
if ((VEC).size + 1 > (VEC).capacity) { \
size_t n = (VEC).capacity * 2; \
void *p = realloc((VEC).data, n * sizeof(*(VEC).data)); \
if (!p) { \
fputs("realloc failed!\n", stderr); \
abort(); \
} \
(VEC).data = p; \
(VEC).capacity = n; \
} \
(VEC).data[VECTOR_SIZE(VEC)] = (VAL); \
(VEC).size += 1; \
} while (0)
/* Get the value of `VEC` at `INDEX`. */
#define VECTOR_AT(VEC, INDEX) (VEC).data[INDEX]
/* Get the value at the front of `VEC`. */
#define VECTOR_FRONT(VEC) (VEC).data[0]
/* Get the value at the back of `VEC`. */
#define VECTOR_BACK(VEC) (VEC).data[VECTOR_SIZE(VEC) - 1]
#define VECTOR_FREE(VEC) do { \
(VEC).size = 0; \
(VEC).capacity = 0; \
free((VEC).data); \
} while(0)
#endif /* !defined VECTOR_H_ */
此代码位于名为 "vector.h" 的头文件中。
请注意,它确实缺少一些功能(例如 VECTOR_INSERT 和 VECTOR_ERASE),但我认为它足以展示我的概念。
向量的使用是这样的:
int main()
{
VECTOR_OF(int) int_vec;
VECTOR_OF(double) dbl_vec;
int i;
VECTOR_INIT(int_vec);
VECTOR_INIT(dbl_vec);
for (i = 0; i < 100000000; ++i) {
VECTOR_PUSH_BACK(int_vec, i);
VECTOR_PUSH_BACK(dbl_vec, i);
}
for (i = 0; i < 100; ++i) {
printf("int_vec[%d] = %d\n", i, VECTOR_AT(int_vec, i));
printf("dbl_vec[%d] = %f\n", i, VECTOR_AT(dbl_vec, i));
}
VECTOR_FREE(int_vec);
VECTOR_FREE(dbl_vec);
return 0;
}
它使用与std::vector 相同的分配规则(大小从1 开始,然后每次需要时加倍)。
令我惊讶的是,我发现这段代码的运行速度是使用 std::vector 编写的相同代码的两倍以上 并且 生成的可执行文件更小! (使用 GCC 和 G++ 编译,在两种情况下都使用 -O3)。
我的问题是:
- 这种方法是否存在严重缺陷?
- 您是否建议在严肃的项目中使用它?
- 如果不是,那么我希望您解释一下为什么,并告诉我有什么更好的选择。
【问题讨论】:
-
如果不知道如何您的宏是如何实现的以及您的预期用例是什么,就不可能回答您的问题。
-
你为什么不写函数呢?使用宏并没有真正的优势,除非在非常有限的情况下。
-
我不使用函数的原因是因为函数在 C 中不能是“通用的”。包含源代码是个好主意。我会这样做的,请稍等!
-
I [...]know both C and C++. [...] I do not know which one I prefer.如果您可以在C和C++之间进行选择,请使用C++对此没有任何疑问。 -
您的
VECTOR_BACK宏包含一个小而重要的缺陷。
标签: c vector data-structures macros c-preprocessor