【问题标题】:Combine designated initializers and malloc in C99+?在 C99+ 中结合指定的初始化程序和 malloc?
【发布时间】:2023-03-12 04:30:01
【问题描述】:

有没有一种很好的方法可以将 C99 中的指定初始化程序与 malloc 的结果结合起来?

以下似乎有不必要的重复:

typedef struct {
   int a, b, c;
} Type;

Type *t = malloc(sizeof *t);
*t = (Type) {
    .a = 2,
    .b = 3,
    .c = 5,
};

上述代码中Type*t的使用能否去掉?

【问题讨论】:

  • 如果可以,如果malloc返回NULL会发生什么?
  • 看起来你真的想要 C++。
  • 有趣的问题,但我认为没有什么好的答案。就我个人而言,我只会使用calloc,然后是t->a=2; t->b=3; ...(calloc 只是在你想省略任何成员的情况下,所以如果你知道你会明确设置它们,你可以使用@987654330 @也一样)

标签: c malloc c99 designated-initializer


【解决方案1】:

既然你问了 ;) C 中有一个工具可以避免显式重复代码,即宏。也就是说,我看不到至少不重复类型名称的方法。但在 C++ 中它们也不能,所以 C 至少一样好:)

我看到的最简单的是

#define DESIGNATE_NEW(T, ...)       \
  memcpy(malloc(sizeof(T)),         \
         &(T const){ __VA_ARGS__ }, \
         sizeof(T))

这会给

Type *t = DESIGNATE_NEW(Type,
    .a = 2,
    .b = 3,
    .c = 5,
);

这有几个优点。

  • 它正确地初始化所有成员,即使在没有非 0 的标准表示,用于浮点类型或指针。
  • 除了 Keith 的版本之外,它是可接受的“编码风格”,因为它只是一个看起来像初始化的表达式,任何人都应该立即直观地捕捉到第二个代码片段应该做什么。

注意:观察宏中的const,如果编译器认为这是相关的,这允许折叠复合文字的多个实例。还有一种方法是有一个变体,其中指示符列表是可选的,请参见下面的 P99。

缺点是memcpy,我会更乐意分配。其次,在使用结果之前没有检查malloc 是否失败,但是可能会遇到一些奇怪的情况,以便让代码很好地退出。

P99 中,我采取了稍微不同的方式。我们总是有一个类型的初始化函数,比如

inline
Type* Type_init(Type* t, int a, int b, int c) {
  if (t) {
    *t = (Type const){ .a = a, .b = b, .c = c };
  }
  return t;
}

如果它们被省略,可以通过宏魔法为abc 提供默认参数。然后你可以简单地使用类似的东西

Type *t = P99_NEW(Type, 1, 2, 3);

在您的应用程序代码中。这更好,因为它避免了在调用 malloc 失败时取消引用指针。另一方面,这给初始化器重新引入了一个顺序,所以也不完美。

【讨论】:

  • 您的 DESIGNATE_NEW 宏最接近惯用的解决方案。
  • 在宏声明中缺少, ...
  • @DanO,确实,在 10 多年前的答案中发现了一个错误;-)
【解决方案2】:

您可以使用可变参数宏。我不会声称这是一个好主意,但它确实有效:

#include <stdlib.h>
#include <stdio.h>

#define CREATE(type, ptr, ...) \
    type *ptr = malloc(sizeof *ptr); \
    if (ptr) *ptr = (type){__VA_ARGS__}

int main(void)
{
    typedef struct {
        int a, b, c;
    } Type;
    CREATE(Type, t, .a = 2, .b = 3, .c = 5);
    printf("t->a = %d, t->b = %d, t->c = %d\n", t->a, t->b, t->c);
    return 0;
}

请注意,我无法使用通常的 do { ... } while (0) 宏定义技巧(它会创建一个新范围,而 t 将不可见),因此您必须小心上下文您在其中使用它。

就个人而言,我认为我对不必要的重复更满意。

【讨论】:

    【解决方案3】:

    不,这是使用指定初始化程序的唯一方法。如果没有 (Type){},编译器将不知道如何验证内容。

    【讨论】:

      猜你喜欢
      • 2021-01-02
      • 2014-10-12
      • 2013-05-29
      • 1970-01-01
      • 2021-03-25
      • 1970-01-01
      • 1970-01-01
      • 2018-04-06
      • 2015-10-30
      相关资源
      最近更新 更多