这是我的第一篇文章,所以请保持温和。
我过去一直在玩C语言。现在我已经开始了我开始一个真正的项目(使用SDL的2D图形引擎,但这与问题无关),能够说我有一些真正的C体验。昨天,在处理事件系统时,我遇到了一个我无法解决的问题。
有这种typedef,
//the void parameter is really an SDL_Event*.
//but that is irrelevant for this question.
typedef void (*event_callback)(void);
指定要在引擎事件上调用的函数的签名。
我希望能够支持多个event_callbacks,所以这些回调的数组是一个想法,但不想限制回调量,所以我需要某种动态分配。这就是问题出现的地方。我的第一次尝试是这样的:
//initial size of callback vector
static const int initial_vecsize = 32;
//our event callback vector
static event_callback* vec = 0;
//size
static unsigned int vecsize = 0;
void register_event_callback(event_callback func) {
if (!vec)
__engine_allocate_vec(vec);
vec[vecsize++] = func; //error here!
}
static void __engine_allocate_vec(engine_callback* vec) {
vec = (engine_callback*) malloc(sizeof(engine_callback*) * initial_vecsize);
}
首先,我省略了一些错误检查以及当回调数量超过矢量大小时重新分配回调向量的代码。
但是,当我运行此代码时,程序会崩溃,如代码中所述。我猜测分段错误但我不能确定,因为没有给出输出。我也猜测错误来自对如何声明和分配指向函数指针的指针数组的一些有缺陷的理解。
请Stack Overflow,指导我。
编辑:我似乎无法掌握如何缩进代码块。那几乎有点尴尬...... 编辑:没关系。检查了其他一些帖子的页面来源=)。答案 0 :(得分:3)
分配功能应该是:
static void __engine_allocate_vec(engine_callback** vec) {
*vec = malloc(sizeof(engine_callback) * initial_vecsize);
}
然后:
if (!vec)
__engine_allocate_vec(&vec);
请注意,如果省略了强制转换,则原始分配函数中的指针类型不匹配将被捕获。另外,不要在代码中使用包含双下划线的名称 - 它们仅供实现使用。
答案 1 :(得分:1)
在这一行:
vec[vecsize++] = func; //error here!
如果vecsize
为>= initial_vecsize
会怎样?
同样__engine_allocate_ve
不起作用,因为它只修改vec
的本地副本,您必须将签名更改为**
并将参数传递给&
static void __engine_allocate_vec(engine_callback** vec)
__engine_allocate_vec(&vec);
答案 2 :(得分:0)
您似乎malloc
- 基于sizeof(engine_callback*)
而不是sizeof(engine_callback)
......
答案 3 :(得分:0)
你的__engine_allocate_vec
函数正在为新的engine_callback
创建空间,但它没有对该指针做任何有用的事情。 (它正在改变它的本地版本的vec,它是通过值传递的 - 因此更改不会将其返回给调用者。并且参数的名称隐藏了全局的名称,因此也没有设置。)所以当它返回时,你的指针仍为空。
尝试这样的事情......
static void __engine_allocate_vec(engine_callback** vec) {
*vec = (engine_callback*) malloc(sizeof(engine_callback) * initial_vecsize);
}
然后在register_event_callback中,将&vec
传递给函数而不是vec
。
或者,使函数为void并让它设置全局本身。不,我忘记了。
答案 4 :(得分:0)
首先,不使用前导下划线表示变量或函数名称;这些名称是为实施而保留的。
其他人都指出了最初分配矢量的正确方法:
static void engine_allocate_vec(event_callback **vec)
{
*vec = malloc(sizeof **vec * initial_vecsize);
}
注意几件事。首先,我没有投射malloc
的结果。它没有必要(从C89开始,无论如何;早期版本的C需要强制转换,C ++也是如此),如果您忘记包含stdlib.h
或者没有包含malloc
,它可能会抑制编译器诊断范围内**vec
的原型。其次,我在表达式**vec
上调用sizeof,而不是类型;由于表达式event_callback
的类型为sizeof **vec
,因此sizeof (event_callback)
返回与sizeof
相同的结果。这有助于减少视觉混乱,并且它避免了当某人更改变量类型时会出现的一些常见错误,但是没有将该更改传递到malloc调用中的double *f; /* was originally declared as float, later changed to double */
...
f = malloc(sizeof (float) * size); /* type change not carried through */
表达式,例如
sizeof
请注意initial_vecsize
不评估其操作数(除非它是VLA),因此您不必担心在未初始化的指针表达式上调用它。
这有助于您创建初始向量。但是,您希望在注册超过static int engine_allocate_vec(event_callback **vec,
size_t *currentSize,
size_t extent)
{
int success = 0;
/**
* Assign the result of realloc to a temporary; realloc returns NULL
* on failure, and we don't want to risk losing our pointer to the
* previously allocated memory. Similarly, we don't update *currentSize
* unless the call succeeds. Note that realloc(NULL, size) is equivalent
* to malloc(size).
*/
event_callback *tmp = realloc(*vec, sizeof *tmp * (*currentSize + extent));
if (tmp != NULL)
{
*vec = tmp;
*currentSize += extent;
success = 1;
}
return success;
}
个回调时能够根据需要扩展向量,对吗?如果是这样,请允许我建议如下:
/**
* Adding vector_count variable to keep track of the number
* of items in the vector as opposed to the physical size
* of the vector.
*/
static size_t vector_count = 0;
void register_callback_event(event_callback func)
{
if (!vec)
{
/**
* Initial vector allocation
*/
if (!engine_allocate_vec(&vec, &vecsize, initial_vecsize))
{
/* allocation failed; treating this as a fatal error */
exit(0);
}
}
else if (vector_count == vecsize)
{
/**
* Need to extend the vector to accomodate
* the new callback. Double the vector size (i.e.,
* extend it by the current vector size)
*/
if (!engine_allocate_vec(&vec, &vecsize, vecsize))
{
/* extension failed - treating this as a fatal error*/
free(vec);
exit(0);
}
}
vec[vector_count++] = func;
}
然后您的注册功能变为:
{{1}}