【问题标题】:Variadic function in C99 to deallocate several arrays?C99 中的可变参数函数释放多个数组?
【发布时间】:2014-02-19 09:50:28
【问题描述】:

目前,我有一个非常简单的函数可以在我的程序中释放双精度数组:

void deallocate(double** array)
{
    free(*array);
}

我希望这个函数是可变的,以便获取多个数组,并一个接一个地释放它们。我从来没有写过可变参数函数,因为可能存在指针技巧,我想知道如何做到这一点。

【问题讨论】:

  • 没有#define 是不可能做到的?
  • 当然,您只需要每次输入第一个和最后一个参数即可。在这种情况下,我宁愿创建一个数组并传递它,而不是使用可变参数 f.
  • @self 你能在我将验证的编译答案中说明它吗?

标签: c c99 variadic-functions variadic-macros


【解决方案1】:

不要使用可变参数函数,这个概念应该被淘汰。特别是对于接收所有相同类型的参数void* 的东西来说根本没有意义。

只要有一个简单的函数,首先接收一个指针数组

void free_arrays(void* a[]) {
  for (size_t i = 0; a[i]; ++i) free(a[i]);
}

然后你可以用这样的宏来包装它

#define FREE_ARRAYS(...) free_arrays((void*[]){ __VA_ARGS__, 0 })

这假设您的指针都不是0,因为处理将在该点停止。

如果即使某些指针是0,您也需要让它工作,您必须将元素的数量作为第一个参数传递给您的函数。这有点繁琐,但也可以在宏中确定。

void free_arrays0(size_t n, void* a[]) {
  for (size_t i = 0; i < n; ++i) free(a[i]);
}

#define FREE_ARRAYS0(...)                                    \
  free_arrays(                                               \
              sizeof((void*[]){ __VA_ARGS__})/sizeof(void*), \
              (void*[]){ __VA_ARGS__}                        \
             )

【讨论】:

  • “有点乏味”? sizeof((void*[]){ __VA_ARGS__ }) / sizeof(void*) 应该可以,对吧?
  • @hvd,是的,当然,请参阅我的编辑。很多人可能会认为这样的代码难以辨认。
  • 啊,好吧,我不是其中之一,但是为了提高可读性,您可以将其包装为#define LENGTHOF(x) (sizeof(x)/sizeof(*(x))(在其他情况下通常已经有用),然后执行LENGTHOF((void*[]){ __VA_ARGS__ })跨度>
  • @JensGustedt ...可变参数函数,这个概念应该被淘汰... 需要详细说明吗?在使用直接 C 时,这对我来说是最有用的功能之一。(或者您只是指这个特定的应用程序?)
  • 因为参数提升规则,并且因为忘记参数的所有内容并动态重建它只是多余的。如果所有参数都属于同一类型,您可以按照我在此答案中描述的方式执行类似的操作。有关更多信息,请参阅 gustedt.wordpress.com/2010/08/04/va_arg-functions-and-macrosgustedt.wordpress.com/2011/07/10/avoid-writing-va_arg-functions
【解决方案2】:

你可以这样做:

void deallocate(double *p, ...)
{
    va_list ap;

    va_start(ap, p);
    do {
        free(p);
        p = va_arg(ap, double *);
    } while (p);
    va_end(ap);
}

致电deallocate(p1, p2, p3, (double *)NULL)。您需要NULL(或其他值)作为标记来表示参数列表的结束;其他指针都不应该是NULL,否则循环将过早停止。

不过,我并不是说这是个好主意:可变参数函数有它们的用例,但它们很容易使用指针出错,因为某些隐式转换不会发生(因为编译器不知道除了第一个参数的类型)。

【讨论】:

  • 如果您在deallocate 中将double * 读取为double *,则实际上需要传递(double *) NULL
  • @hvd 不,您不必这样做。 void* 与任何指针兼容,您永远不会取消引用它。
  • @self。是的你是。当通过... 传递时,编译器不知道如何将NULL 隐式转换为任何其他类型。它何时中断的简单示例:NULL 定义为0int 有 32 位。 double * 有 64 位。你认为会发生什么? (另外,void * 不需要与其他指针类型兼容,它只需要支持与其他指针类型之间的转换。但是 that 中断的实现要少得多。)
  • @hvd NULL 定义为 (( void*)0)
  • @self。如何定义 NULL 是实现定义的。 ((void *) 0) 是一个有效的定义。 0 是另一个有效的定义,它也被用于现实世界的实现。
猜你喜欢
  • 1970-01-01
  • 2011-04-04
  • 1970-01-01
  • 1970-01-01
  • 2014-08-16
  • 2013-09-20
  • 2015-01-18
  • 2014-07-23
  • 1970-01-01
相关资源
最近更新 更多