关于 C 中 1 元素数组的 typedef [英] Regarding typedefs of 1-element arrays in C
问题描述
有时,在 C 中,您会这样做:
Sometimes, in C, you do this:
typedef struct foo {
unsigned int some_data;
} foo; /* btw, foo_t is discouraged */
要以面向对象的方式使用这种新类型,您可能有这样的 alloc/free 对:
To use this new type in an OO-sort-of-way, you might have alloc/free pairs like these:
foo *foo_alloc(/* various "constructor" params */);
void foo_free(foo *bar);
或者,或者 init/clear 对(可能返回错误代码):
Or, alternatively init/clear pairs (perhaps returning error-codes):
int foo_init(foo *bar, /* and various "constructor" params */);
int foo_clear(foo *bar);
我已经看到使用了以下习语,特别是在 MPFR 库中:
I have seen the following idiom used, in particular in the MPFR library:
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 对现在变为:
The alloc/free and init/clear pairs now read:
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 对):
Now you can use it all like this (for instance, the init/clear pairs):
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 完成的.
Remarks: The init/clear pair seems to allow for a more generic way of initializing and clearing out objects. Compared to the alloc/free pair, the init/clear pair requires that a "shallow" object has already been constructed. The "deep" construction is done using init.
问题:1 元素数组type-idiom"是否有任何不明显的陷阱?
Question: Are there any non-obvious pitfalls of the 1-element array "type-idiom"?
推荐答案
这很聪明(但见下文).
This is very clever (but see below).
它助长了 C 函数参数可以通过引用传递的误导性想法.
It encourages the misleading idea that C function arguments can be passed by reference.
如果我在 C 程序中看到这个:
If I see this in a C program:
foo bar;
foo_init(bar);
我知道对 foo_init
的调用不会修改 bar
的值.我也知道代码在未初始化函数时将 bar
的值传递给函数,这很可能是未定义的行为.
I know that the call to foo_init
does not modify the value of bar
. I also know that the code passes the value of bar
to a function when it hasn't initialized it, which is very probably undefined behavior.
除非我碰巧知道 foo
是数组类型的 typedef.然后我突然意识到foo_init(bar)
传递的不是bar
的value,而是它的第一个元素的地址.现在每次我看到引用类型 foo
的东西,或者引用类型 foo
的对象时,我都必须考虑 foo
是如何在我理解代码之前定义为单元素数组的typedef.
Unless I happen to know that foo
is a typedef for an array type. Then I suddenly realize that foo_init(bar)
is not passing the value of bar
, but the address of its first element. And now every time I see something that refers to type foo
, or to an object of type foo
, I have to think about how foo
was defined as a typedef for a single-element array before I can understand the code.
它试图让 C 看起来像它不是的东西,与类似的东西没有什么不同:
It is an attempt to make C look like something it's not, not unlike things like:
#define BEGIN {
#define END }
等等.并且它不会导致代码更容易理解,因为它使用了 C 不直接支持的功能.它导致代码更难理解(尤其对于熟悉 C 的读者),因为您必须理解自定义声明和底层的 C 语义使整个事情正常工作.
and so forth. And it doesn't result in code that's easier to understand because it uses features that C doesn't support directly. It results in code that's harder to understand (especially to readers who know C well), because you have to understand both the customized declarations and the underlying C semantics that make the whole thing work.
如果你想传递指针,只需传递指针,并明确地传递.例如,参见
中定义的各种标准函数中 FILE*
的使用.没有试图在宏或 typedef 后面隐藏指针,C 程序员几十年来一直在使用该接口.
If you want to pass pointers around, just pass pointers around, and do it explicitly. See, for example, the use of FILE*
in the various standard functions defined in <stdio.h>
. There is no attempt to hide pointers behind macros or typedefs, and C programmers have been using that interface for decades.
如果您想编写看起来像是通过引用传递参数的代码,请定义一些类似函数的宏,并给它们全大写的名称,以便知识渊博的读者知道发生了一些奇怪的事情.
If you want to write code that looks like it's passing arguments by reference, define some function-like macros, and give them all-caps names so knowledgeable readers will know that something odd is going on.
我在上面说过这是聪明的".我想起了我第一次学习 C 语言时所做的一件事:
I said above that this is "clever". I'm reminded of something I did when I was first learning the C language:
#define EVER ;;
这让我写了一个无限循环:
which let me write an infinite loop as:
for (EVER) {
/* ... */
}
当时我觉得很聪明.
我仍然认为它很聪明.我只是不再认为这是一件好事.
I still think it's clever. I just no longer think that's a good thing.
这篇关于关于 C 中 1 元素数组的 typedef的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!