首页 > 解决方案 > 如果范围是不同的结构定义,结构声明是否存在于声明它的范围之后?

问题描述

因此,在对代码进行了一些更改后,我收到了以下警告:

warning: declaration of 'struct Thing' will not be visible
      outside of this function

这个出色的答案解决了这个问题。

然后我对别的事情感到困惑。事实证明,以下是我在 bug 之前的程序:

struct OtherThing {
    struct Thing* t; // <-- struct Thing declared here
};

void func(struct Thing* t);

int main(void) {
  return 0;
}

这是引发警告的程序:

struct OtherThing {
    int t; // <-- struct Thing no-longer declared here
};

void func(struct Thing* t);

int main(void) {
  return 0;
}

如果我理解正确 - 如链接答案中所述,struct在函数签名中声明的声明与普通变量具有相同的范围规则。

但是-在不同定义中struct声明的声明确实在结尾 '}' 之后继续存在struct

我的理解正确吗?如果是这样 - 那么相同的范围规则适用于struct声明和普通变量(与链接答案所做的声明相反)是不正确的。这种设计背后的逻辑是什么?

标签: cstructsyntaxdeclaration

解决方案


如果范围是不同的结构定义,结构声明是否存在于声明它的范围之后?

这个问题有一个错误的前提。结构定义不是范围,因此结构声明的范围不能是封闭结构声明。根据 C 2018 6.2.1,结构声明的范围必须是文件范围、函数原型范围或块范围。(还有一个函数作用域,但只有标签 [for gotostatements] 具有函数作用域。)如果声明在任何函数之外,则它具有函数作用域。如果它在函数原型(不是定义)的参数内,则它具有函数原型范围。否则,它具有块范围。

C中的一个块是:

  • 复合语句 (C 2018 6.8.2 2)。
  • 选择语句 ( ifif…elseswitch) (C 2018 6.8.4 3)。
  • 选择语句的每个关联子语句(同上)。
  • 迭代语句 ( while, do, for) (C 2018 6.8.5 5)。
  • 选择语句的循环体(同上)。

结构声明不是其中之一,也不构成范围——绑定结构声明的大括号不是与范围关联的块。结构声明中的大括号和声明不构成复合语句,因为仅具有这种形式是不够的。根据 C 2018 6.8 中的 C 语法,复合语句还必须出现在可能出现语句的程序中 1. 根据 C 2018 6.7,结构声明中的大括号和声明是语法中struct-declaration-list的一部分.2.1 1.

因此,结构声明中的结构声明与未封闭结构声明的行为类似——为结构声明的标记具有块范围,而块位于封闭结构声明之外,因此标记在封闭结构之外可见。

考虑一下:

struct Outer
{
    struct Inner *Member;
};

这将 Outer 和 Inner 都声明为结构标记。它们都具有相同的范围(除了起点略有不同;每个范围都从其名称出现的位置开始)。如果这些出现在任何函数之外,则它们具有文件范围。如果它们出现在内,则它们具有块范围。如果它们出现在函数函数原型(不是定义)的参数声明中,则它们具有函数参数范围(但这通常对结构没有用)。

人们可能会有这样的印象,即作用域仅限于结构声明,因为结构成员名称,如Member,在结构之外是不可用的。但这是出于不同的原因:结构成员名称位于不同的名称空间中。每个结构都有自己的名称空间,其成员名称在其名称空间中。相反,所有结构/联合/枚举标签只有一个名称空间(所有普通标识符都有一个名称空间,所有goto标签都有一个名称空间)。

这种设计背后的逻辑是什么?

最初在 C 中,成员名称没有自己的名称空间——在不同的结构中不能有相同的成员名称,至少在从结构开始的不同偏移量中不能有相同的成员名称。为每个结构提供自己的名称空间是一项有价值的改进,因为不同结构的声明不会相互作用,程序员不必担心它们包含的头文件声明了哪些成员名称。而且这种变化并没有改变语法上的语言:成员名称总是在结构的上下文中使用,所以成员名称是哪个结构的一部分总是很清楚,旧代码继续使用这种变化。

关联结构标记(或类似地,类型定义)需要更改语言。您需要一种方法来指定标签所指的结构。C++ 用它的::语法来做到这一点。我无法谈论是否为 C 考虑过此类更改的历史,但我们至少可以看到它会带来一些成本,并且可能会破坏现有代码——任何引用在另一个结构中声明的结构标记的代码都会有必须更改以包含它所指的结构的新规范。


推荐阅读