为什么允许命名嵌套结构?
Why are named nested structure allowed?
我偶然发现了 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 中有效?后面有没有history/reason?这种结构有用例吗? (我是一个导致失败的错误,因此是这个问题。)
假设我有这个结构:
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!
另一种方法是在 OuterStruct
之外声明 InnerStruct
,然后将其实例作为 OuterStruct
的成员放入其中。但这会掩盖 InnerStruct
属于 OuterStruct
的事实,使您的意图不那么明确。
嗯,这在 C:
中是合法的
struct BatteryStatus_st {
int capacity;
int load;
};
struct Robot_st {
int pos_x;
int pos_y;
struct BatteryStatus_st battery;
};
而且,正如您所指出的,它实际上与您发布的代码相同(因为 C 没有在 C++ 中引入的 namespace/scoping 规则)。
如果在内部移动 "inner" 类型不会改变任何东西,但有时可能会澄清意图,那么禁止它似乎很奇怪。
除了其他两个答案之外,这里还有一个真实的用例,它需要这样一个命名的内部结构:
struct LinkedList {
//data members stored once per list
struct LinkedListNode {
//data members for each entry of the list
struct LinkedListNode *next;
} *head, *tail;
};
很难为链表的结构写出更简洁的定义。
未命名的内部结构将不起作用:将某些内容插入链表的代码可能必须声明带有节点指针的局部变量。而且在链表结构之外声明节点结构也没有意义。
C 最初允许这样的构造,因为结构名称占据了一个完全属于自己的宇宙,从未应用任何类型的范围规则 [struct member names 也这样做了,通过方式,这就是为什么旧标准库中的某些结构类型在其成员上有前缀]。由于某些代码以与作用域不一致的方式使用结构名称,因此无法在不破坏现有代码的情况下更改标准以禁止此类使用。虽然有时值得打破现有代码(例如,摆脱 C 的可憎之物 gets
),但这确实不是其中之一。
我偶然发现了 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 中有效?后面有没有history/reason?这种结构有用例吗? (我是一个导致失败的错误,因此是这个问题。)
假设我有这个结构:
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!
另一种方法是在 OuterStruct
之外声明 InnerStruct
,然后将其实例作为 OuterStruct
的成员放入其中。但这会掩盖 InnerStruct
属于 OuterStruct
的事实,使您的意图不那么明确。
嗯,这在 C:
中是合法的struct BatteryStatus_st {
int capacity;
int load;
};
struct Robot_st {
int pos_x;
int pos_y;
struct BatteryStatus_st battery;
};
而且,正如您所指出的,它实际上与您发布的代码相同(因为 C 没有在 C++ 中引入的 namespace/scoping 规则)。
如果在内部移动 "inner" 类型不会改变任何东西,但有时可能会澄清意图,那么禁止它似乎很奇怪。
除了其他两个答案之外,这里还有一个真实的用例,它需要这样一个命名的内部结构:
struct LinkedList {
//data members stored once per list
struct LinkedListNode {
//data members for each entry of the list
struct LinkedListNode *next;
} *head, *tail;
};
很难为链表的结构写出更简洁的定义。
未命名的内部结构将不起作用:将某些内容插入链表的代码可能必须声明带有节点指针的局部变量。而且在链表结构之外声明节点结构也没有意义。
C 最初允许这样的构造,因为结构名称占据了一个完全属于自己的宇宙,从未应用任何类型的范围规则 [struct member names 也这样做了,通过方式,这就是为什么旧标准库中的某些结构类型在其成员上有前缀]。由于某些代码以与作用域不一致的方式使用结构名称,因此无法在不破坏现有代码的情况下更改标准以禁止此类使用。虽然有时值得打破现有代码(例如,摆脱 C 的可憎之物 gets
),但这确实不是其中之一。