2010-05-26 132 views
1

这是我的第一篇文章,所以请温和。声明,分配和分配指向函数指针的指针数组

我以前一直在玩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); 
} 

首先,我省略了一些错误检查,以及为重新分配回调矢量时回调的数量超过矢量大小不同的代码。

但是,当我运行此代码时,程序崩溃,如代码中所述。我猜分段错误,但我不能确定,因为没有输出。我也猜测这个错误来自于对如何声明和分配一个指向函数指针的指针数组有点有缺陷的理解。

请堆栈溢出,指导我。

编辑:我似乎无法掌握如何缩进代码块。这几乎是一个尴尬...

编辑:没关系的。检查了其他一些帖子的页面源代码=)。

+0

你必须通过4个空格缩进的代码,它被正确格式化。 – Artefacto 2010-05-26 10:43:53

+1

使用编辑页面上的'101010'按钮将标记文本转换为代码。 – sbi 2010-05-26 10:44:11

+1

在__engine_allocate_vec中需要双重间接 – Artefacto 2010-05-26 10:46:34

回答

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);

+0

谢谢!我相信你是第一个。有时我会被pass-by-val和pass-by-ptr困惑。你可能会详细说明为什么传递一个指向函数指针的指针是不够的? – manneorama 2010-05-27 05:54:16

+0

当您通过值传递'vec'时,它的副本被发送到函数中,然后被修改,函数外的'vec'原始值保持不变。为了解决这个问题,不是传递'vec'的副本,而是传递一个指向'vec'的指针。在函数内部,您取消引用这个指针来修改它指向的变量。 – 2010-05-27 07:03:15

3

分配功能应该是:

static void __engine_allocate_vec(engine_callback** vec) { 
    *vec = malloc(sizeof(engine_callback) * initial_vecsize); 
} 

然后:

if (!vec) 
    __engine_allocate_vec(&vec); 

注意,在你原来的分配函数指针类型不匹配将有如果你被抓住了已经省略了演员。另外,不要在代码中使用包含双下划线的名称 - 它们是为了实现的使用。

0

你似乎malloc -ing基于sizeof(engine_callback*),而不是sizeof(engine_callback)是...

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并让它设置全局本身。不,我忘记了。

0

首先,不要使用前导下划线表示变量名或函数名;这样的名字被保留用于执行。

其他人指出最初分配矢量的正确方法:

static void engine_allocate_vec(event_callback **vec) 
{ 
    *vec = malloc(sizeof **vec * initial_vecsize); 
} 

注意两件事情。首先,我没有投下malloc的结果。它不是必需的(无论如何,从C89开始; C的早期版本需要强制转换,C++也如此),并且如果忘记包含stdlib.h或者没有malloc的原型,则可能会抑制编译器诊断范围。其次,我打电话给sizeof表示**vec,而不是类型;由于表达式**vec的类型是event_callback,sizeof **vec返回与sizeof (event_callback)相同的结果。这有助于减少视觉混乱,并且它避免了某些常见错误,当某人更改变量的类型时,它会悄悄进入,但不会在malloc调用中将该变化传递给sizeof表达式,例如

double *f; /* was originally declared as float, later changed to double */ 
... 
f = malloc(sizeof (float) * size); /* type change not carried through */ 

请注意,sizeof不会评估其操作数(除非它是VLA),因此您不必担心在未初始化的指针表达式上调用它。

这可以帮助您创建初始向量。不过,您希望能够在注册超过initial_vecsize的回调时根据需要扩展向量,对吧?如果是这样,我建议如下:

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; 
} 
+0

感谢您提供sizeof-tip!没有真正考虑过这一点。内存重新分配和错误检查已经存在。为了简洁,我将它们排除在外。 – manneorama 2010-05-27 05:51:12