关于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 */
要在OO排序中使用这种新类型,您可能需要像下面这样的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);
或者,也可以使用初始化/清除对(可能返回错误代码):
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 */
分配/空闲和初始化/清除对现在显示为:
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. */
}
备注:初始化/清除对似乎允许使用更通用的方式初始化和清除对象.与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.
如果要传递指针,只需传递指针,然后显式执行即可.例如,请参见在<stdio.h>
中定义的各种标准功能中使用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) {
/* ... */
}
当时,我认为它很聪明.
At the time, I thought it was clever.
我仍然认为这很聪明.我只是不再认为那是一件好事.
I still think it's clever. I just no longer think that's a good thing.
这篇关于关于C中1元素数组的typedef的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!