关于 C 中 1 元素数组的 typedef [英] Regarding typedefs of 1-element arrays in C

查看:37
本文介绍了关于 C 中 1 元素数组的 typedef的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有时,在 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)传递的不是barvalue,而是它的第一个元素的地址.现在每次我看到引用类型 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆