在 C99 中组合指定的初始值设定项和 malloc?

发布于 2024-12-02 13:07:30 字数 297 浏览 3 评论 0原文

有没有一种好的方法可以将 C99 中的指定初始值设定项与 malloc 的结果结合起来?

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

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

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

可以从上面的代码中删除 Type*t 的使用吗?

Is there a nice way to combine designated initializers from C99, with the result of a malloc?

The following seems to have needless duplication:

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

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

Can the use of Type, and *t be removed from the above code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

戏剧牡丹亭 2024-12-09 13:07:30

既然你问了;)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 失败时取消引用指针。另一方面,这重新向初始化器引入了顺序,因此也不完美。

Since you asked ;) there is one tool in C to avoid explicit duplication of code, macros. That said I don't see a way not to repeat at least the name of the type. But in C++ they can't either, so C is at least as good :)

The easiest I see is

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

which would give

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

this has several advantages.

  • It initializes all members correctly, even on architectures with non
    standard representations of the 0 for float types or pointers.
  • Other than Keith' version it is "coding style" acceptable since it is just an expression that looks like an initialization and anybody should immediately capture visually what the second code snipset is supposed to do.

NB: Observe the const in the macro, this allows several instances of the compound literal to be folded, if the compiler decides this to be relevant. Also there are means to have a variant where the list of designators is optional, see P99 below.

The disadvantage is the memcpy and I would be happier with an assignment. Second there is no check for failure of malloc before using the result, but one could probably come across with some weirdness to have the code exit nicely.

In P99 I go a slightly different way. There we always have an initialization function for a type, something like

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

which by macro magic can be made to provide default arguments for a, b and c if they are omitted. Then you can simply use something like

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

in your application code. This is better, since it avoids dereferrencing the pointer when the call to malloc failed. On the other hand this reintroduces an order to the initializers, so not perfect either.

梦途 2024-12-09 13:07:30

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

#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 将不可见),因此您必须小心使用它的上下文。

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

You can with a variadic macro. I'm not going to claim that this is a good idea, but it works:

#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;
}

Note that I wasn't able to use the usual do { ... } while (0) macro definition trick (it would create a new scope, and t wouldn't be visible), so you'd have to be careful about the context in which you use this.

Personally, I think I'm happier with the needless duplication.

诗酒趁年少 2024-12-09 13:07:30

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

No, that's the only way to use designated initializers. Without the (Type){}, the compiler doesn't know how to validate the contents.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文