有时,在 C 中,您会这样做:
typedef struct foo {
unsigned int some_data;
} foo; /* btw, foo_t is discouraged */
要以面向对象的方式使用这种新类型,您可能需要这样的分配/释放对:
foo *foo_alloc(/* various "constructor" params */);
void foo_free(foo *bar);
或者,或者 init/clear 对(可能返回错误代码):
int foo_init(foo *bar, /* and various "constructor" params */);
int foo_clear(foo *bar);
我看到使用了以下习语,特别是在 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 */
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);
现在您可以像这样使用它(例如,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. */
}
备注:init/clear 对似乎允许使用更通用的方式来初始化和清除对象。与 alloc/free 对相比,init/clear 对要求已经构造了一个“浅层”对象。 “深层”构造是使用 init 完成的。
问题:1 元素数组“type-idiom”是否存在任何不明显的缺陷?
最佳答案
这非常聪明(但见下文)。
它助长了 C 函数参数可以通过引用传递的误导性想法。
如果我在 C 程序中看到这个:
foo bar;
foo_init(bar);
我知道调用 foo_init
不修改 bar
的值.我也知道代码传递了 bar
的值在函数尚未初始化时将其传递给函数,这很可能是未定义的行为。
除非我碰巧知道 foo
是数组类型的 typedef。然后我突然意识到 foo_init(bar)
未传递 bar
的值 , 但它的第一个元素的地址。现在每次我看到引用类型 foo
的内容时, 或 foo
类型的对象,我得想想怎么办foo
在我理解代码之前被定义为单元素数组的 typedef。
它试图让 C 看起来像它不是的东西,就像这样:
#define BEGIN {
#define END }
等等。而且它不会导致代码更容易理解,因为它使用了 C 不直接支持的功能。它会导致代码更难理解(尤其是对于非常了解 C 的读者),因为您必须同时理解自定义声明和使整个事情正常工作的底层 C 语义。
如果你想传递指针,就传递指针,然后显式地传递。例如,参见 FILE*
的使用在 <stdio.h>
中定义的各种标准函数中.没有人试图将指针隐藏在宏或 typedef 后面,C 程序员几十年来一直在使用该接口(interface)。
如果您想编写看起来像通过引用传递参数的代码,请定义一些类似函数的宏,并为它们提供全部大写的名称,以便知识渊博的读者知道发生了一些奇怪的事情。
上面我说这是“聪明”。让我想起了我刚开始学习C语言时做过的一件事:
#define EVER ;;
这让我写了一个无限循环:
for (EVER) {
/* ... */
}
当时觉得挺聪明的。
我仍然认为它很聪明。我只是不再认为这是一件好事。
关于c - 关于 C 中 1 元素数组的类型定义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18560660/