【问题标题】:Declaration, allocation and assignment of an array of pointers to function pointers指向函数指针的指针数组的声明、分配和赋值
【发布时间】:2011-02-24 02:58:21
【问题描述】:

这是我的第一篇文章,所以请温柔。

过去我一直在玩弄 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);  
}  

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

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

请堆栈溢出,指导我。

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

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

【问题讨论】:

  • 您必须将代码缩进 4 个空格才能正确格式化。
  • 使用编辑页面上的101010按钮将标记的文本转换为代码。
  • 你需要在 __engine_allocate_vec 中使用双重间接

标签: c


【解决方案1】:

分配函数应该是:

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

然后:

if (!vec)  
    __engine_allocate_vec(&vec);  

请注意,如果您省略了强制转换,原始分配函数中的指针类型不匹配将会被捕获。此外,不要在代码中使用包含双下划线的名称 - 它们是供实现使用的。

【讨论】:

    【解决方案2】:

    在线:

    vec[vecsize++] = func; //error here!  
    

    如果vecsize 是>= initial_vecsize 会发生什么?

    另外__engine_allocate_ve 也不起作用,因为它只修改了vec 的本地副本,您必须将签名更改为** 并使用& 传递参数。

    static void __engine_allocate_vec(engine_callback** vec)

    __engine_allocate_vec(&vec);

    【讨论】:

    • 谢谢!我相信你是第一个。有时我对 pass-by-val 和 pass-by-ptr 感到困惑。您能否详细说明为什么将指针传递给函数指针是不够的?
    • 当您通过值传递vec 时,它的副本被发送到函数中然后被修改,函数外部vec 的原始值保持不变。要解决此问题,您无需传递vec 的副本,而是传递一个指向vec 的指针。然后在函数内部取消引用这个指针来修改它指向的变量。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      您的__engine_allocate_vec 函数正在为新的engine_callbacks 创建空间,但它没有对该指针做任何有用的事情。 (它正在更改其本地版本的 vec,它是按值传递的 - 因此更改不会将其返回给调用者。并且参数的名称隐藏了全局名称,因此也没有设置。)所以当它返回时,您的指针仍然为空。

      试试这样的...

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

      然后在 register_event_callback 中,将 &vec 传递给函数而不是 vec。

      或者,使函数无效并让它自己设置全局。不,忘记我说过了。

      【讨论】:

        【解决方案5】:

        首先,不要在变量或函数名称中使用前导下划线;这些名称是为实现保留的。

        其他人都指出了初始分配向量的正确方法:

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

        请注意几件事。首先,我不投malloc的结果。没有必要(无论如何,从 C89 开始;早期版本的 C 需要强制转换,就像 C++ 一样),如果您忘记包含 stdlib.h 或没有原型,它可能会抑制编译器诊断malloc 在范围内。其次,我在表达式**vec 上调用sizeof,而不是类型;因为表达式**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;
        }
        

        【讨论】:

        • 感谢您提供 sizeof-tip!还真没有考虑到。内存重新分配和错误检查已经存在。为简洁起见,我将它们排除在外。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-23
        • 1970-01-01
        • 1970-01-01
        • 2015-12-10
        相关资源
        最近更新 更多