我偶然发现了C的一个非常奇怪的特性:你可以在另一个结构中声明一个命名结构,只要同时声明该类型的成员变量:
struct Robot_st {
int pos_x;
int pos_y;
struct BatteryStatus_st { /* <- named struct */
int capacity;
int load;
} battery;
};
然后内部结构在结构外部可以像任何其他类型一样使用,使得像这样的奇怪代码完全有效:
struct Robot_st my_robot = {2, 3, {200, 50}};
struct BatteryStatus_st battery_snapshot; /* <- use of inner struct */
memcpy(
&battery_snapshot,
&my_robot.battery,
sizeof(struct BatteryStatus_st)
);
printf("robot position: %d,%d\n", my_robot.pos_x, my_robot.pos_y);
printf("battery load: %d%%\n", battery_snapshot.load);
嵌套未命名的结构感觉是正确的,因为您以后无法访问该类型,因此不会混淆该类型的范围。上面的代码在C ++中也是无效的,虽然嵌套声明是,因为C ++将它理解为外部结构的命名空间中的类型,所以你需要使用
来访问它。struct Robot_st::BatteryStatus_st battery_snapshot;
虽然同时宣称一个类型和一个成员感到奇怪,但更有意义。
那么为什么这个结构在C中有效?背后有历史/原因吗?这样的构造是否有用例? (我的错误导致失败,因此问题。)
答案 0 :(得分:2)
除了其他两个答案之外,这里还有一个真正的用例,它需要这样一个命名的内部结构:
struct LinkedList {
//data members stored once per list
struct LinkedListNode {
//data members for each entry of the list
struct LinkedListNode *next;
} *head, *tail;
};
很难为链表的结构写一个更简洁的定义。
一个未命名的内部结构不会做:在链表中插入内容的代码可能必须使用节点指针声明局部变量。在链表结构之外声明节点结构毫无意义。
答案 1 :(得分:2)
这样的构造最初是在C中允许的,因为结构名称占用了一个宇宙,它们从来没有应用任何类型的作用域规则[struct 成员名称也是如此,顺便说一句,这是为什么旧标准库中的某些结构类型在其成员上有前缀]。由于某些代码以与范围不一致的方式使用结构名称,因此无法更改标准以禁止此类使用而不破坏现有代码。虽然有时候破坏现有代码是值得的(例如,为了摆脱被称为gets
的憎恶的C),但这真的不是其中之一。
答案 2 :(得分:1)
我们说我有这样的结构:
struct OuterStruct
{
int a;
struct InnerStruct
{
int i;
int j;
} b;
} s;
现在我可以访问s.a
,我甚至可以将其保存在变量中,以便更好地处理它,将其传递给函数,...
int sa = s.a;
好吧,现在我想对s.b
做同样的事情,为此,我需要InnerStruct
来命名!
???? sb = s.b; // here I have to use InnerStruct, otherwise sb would have no valid type!
替代方法是在InnerStruct
之外声明OuterStruct
,然后将其实例作为OuterStruct
的成员放入其中。但这会隐藏InnerStruct
属于OuterStruct
的事实,使您的意图不那么明确。
答案 3 :(得分:1)
嗯,这在C:
是合法的struct BatteryStatus_st {
int capacity;
int load;
};
struct Robot_st {
int pos_x;
int pos_y;
struct BatteryStatus_st battery;
};
并且,正如您所指出的,它实际上与您发布的代码相同(因为C没有C ++中引入的命名空间/作用域规则)。
如果在内部移动“内部”类型并没有改变任何东西,但有时可能会澄清意图,禁止它似乎很奇怪。