关于C中1元素数组的typedef

Ole*_*uus 9 c arrays

有时候,在C中,你这样做:

typedef struct foo {
   unsigned int some_data;
} foo; /* btw, foo_t is discouraged */
Run Code Online (Sandbox Code Playgroud)

要在OO-sort-way中使用这种新类型,你可能有这样的alloc/free对:

foo *foo_alloc(/* various "constructor" params */);
void foo_free(foo *bar);
Run Code Online (Sandbox Code Playgroud)

或者,或者init/clear对(可能返回错误代码):

int foo_init(foo *bar, /* and various "constructor" params */);
int foo_clear(foo *bar);
Run Code Online (Sandbox Code Playgroud)

我已经看到使用了以下习语,特别是在MPFR库中:

struct foo {
   unsigned int some_data;
};
typedef struct foo foo[1]; /* <- notice, 1-element array */
typedef struct foo *foo_ptr; /* let's create a ptr-type */
Run Code Online (Sandbox Code Playgroud)

alloc/free和init/clear对现在读取:

foo_ptr foo_alloc(/* various "constructor" params */);
void foo_free(foo_ptr bar);
int foo_init(foo_ptr bar, /* and various "constructor" params */);
int foo_clear(foo_ptr bar);
Run Code Online (Sandbox Code Playgroud)

现在你可以像这样使用它(例如,init/clear对):

int main()
{  
   foo bar; /* constructed but NOT initialized yet */
   foo_init(bar); /* initialize bar object, alloc stuff on heap, etc. */
   /* use bar */
   foo_clear(bar); /* clear bar object, free stuff on heap, etc. */
}
Run Code Online (Sandbox Code Playgroud)

备注:init/clear对似乎允许更通用的方式初始化和清除对象.与alloc/free对相比,init/clear对要求已经构造了"浅"对象."深度"构造使用init完成.

问题:1元素数组"type-idiom"是否有任何非明显的缺陷?

Kei*_*son 11

这非常聪明(但见下文).

它鼓励误导的观点,即C函数参数可以通过引用传递.

如果我在C程序中看到这个:

foo bar;
foo_init(bar);
Run Code Online (Sandbox Code Playgroud)

我知道调用foo_init不会修改值bar.我也知道代码在bar没有初始化时将值传递给函数,这很可能是未定义的行为.

除非我碰巧知道这foo是一个数组类型的typedef.这时我突然意识到,foo_init(bar)不流通价值bar,但是它的第一个元素的地址.现在,每当我看到引用类型foo或类型对象的内容时foo,我都要考虑foo在理解代码之前如何定义为单元素数组的typedef.

这是一种让C看起来不像它的东西的尝试,与以下内容不同:

#define BEGIN {
#define END }
Run Code Online (Sandbox Code Playgroud)

等等.并且它不会导致代码更容易理解,因为它使用C不直接支持的功能.这会导致代码是很难理解(尤其是给谁知道的C以及读者),因为你必须要了解双方的定制声明底层的C语义使整个事情的工作.

如果你想传递指针,只需传递指针,并明确地做.例如,参见FILE*在中定义的各种标准函数中的使用<stdio.h>.没有尝试隐藏宏或typedef背后的指针,C程序员已经使用该接口数十年了.

如果你想编写看起来像是通过引用传递参数的代码,定义一些类似函数的宏,并给它们全部大写名称,这样知识渊博的读者就会知道奇怪的事情正在发生.

我在上面说过,这是"聪明的".我想起了我第一次学习C语言时所做的事情:

#define EVER ;;
Run Code Online (Sandbox Code Playgroud)

这让我写了一个无限循环:

for (EVER) {
    /* ... */
}
Run Code Online (Sandbox Code Playgroud)

那时,我觉得它很聪明.

我仍然觉得它很聪明.我不再认为这是件好事.

  • 好的,基思!阅读你的答案非常愉快.所以需要更多的答案,比如这个,具有天赋,风格和额外内容,如有趣的个人记忆,给读者带来的不仅仅是答案. (2认同)