这个[1]在结构体声明的末尾有什么用途?

100

我在查阅我的MSP430微控制器的头文件时,偶然发现了在<setjmp.h>中的以下内容:

/* r3 does not have to be saved */
typedef struct
{
    uint32_t __j_pc; /* return address */
    uint32_t __j_sp; /* r1 stack pointer */
    uint32_t __j_sr; /* r2 status register */
    uint32_t __j_r4;
    uint32_t __j_r5;
    uint32_t __j_r6;
    uint32_t __j_r7;
    uint32_t __j_r8;
    uint32_t __j_r9;
    uint32_t __j_r10;
    uint32_t __j_r11;
} jmp_buf[1]; /* size = 20 bytes */

我明白它声明了一个匿名结构体并将其typedef为jmp_buf,但我无法弄清楚[1]是用来做什么的。 我知道它声明了jmp_buf是一个只有一个成员(来自此匿名结构体)的数组,但我想不出它有什么用。 有什么想法吗?


5
也许与指针衰变有关? - Elazar
3
最终评论似乎完全是错的... - R.. GitHub STOP HELPING ICE
1个回答

122
这是在C语言中制造“引用类型”的常见技巧,将其作为函数参数使用时,单个元素数组会降级为指向其第一个元素的指针,而程序员无需显式地使用&运算符来获取其地址。声明时,它是一个真正的堆栈类型(不需要动态分配内存),但当作为参数传递时,被调用的函数接收到的是指向它的指针,而非副本,因此传递成本低廉(如果未标记为const,则可由被调用的函数改变)。
GMP使用同样的技巧来处理其mpz_t类型,这在那里至关重要,因为该结构体管理指向动态分配内存的指针;mpz_init函数依赖于获取指向结构体的指针,而不是其副本,否则它无法初始化它。同样,许多操作可以调整动态分配的内存大小,如果它们不能改变调用者的结构,则无法正常工作。

13
它还可以通过防止使用“=”进行复制。 - melpomene
12
太恶心了。等最短时间过后我会接受这个答案。谢谢你的帮助! - Alexander
3
@Alexander: 如果使用 typedef 这样的封装方式,它就不会像这样粗糙了。如果是临时处理,那么做起来可能有点可怕,但如果你具有稍微模糊的不透明类型,在这种类型中 API 用户永远不需要考虑引用与非引用语义(它应该始终通过引用传递),那么这是一种合理的方式,可以向否则缺乏此功能的语言中添加自动引用语义。即使用户编写自己的 API 来接收该类型,它也能正常工作,因为在 C 中,声明接受一个数组作为参数实际上意味着你接受一个指针;一切“都正常工作”。 - ShadowRanger
4
这是一个巧妙的技巧,但“... otherwise lacks it”是其不足之处。这不是解决方法本身的问题,而是C语言的局限性所致。 - Alexander
36
个人看法,这很恶心。第一次使用 GMP 时,由于数字似乎是按值传递的,我无法理解它是如何工作的。我不得不深入研究 GMP 的头文件来了解它的奥秘。这与那些已经了解 C 语言的人完全相反。然后你必须在脑海中跟踪哪些参数是按值传递的,哪些是引用传递的,而不是只在代码中查找 * - M.M
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接