c - 前向声明结构体的范围

标签 c scope language-lawyer c11

想象一下我们有这个:

void f(struct s *);

通过阅读标准($6.2.1),我对标签的范围有点困惑。首先是这个:

  1. A label name is the only kind of identifier that has function scope. It can be used (in a goto statement) anywhere in the function in which it appears, and is declared implicitly by its syntactic appearance (followed by a : and a statement).

  2. Every other identifier has scope determined by the placement of its declaration (in a declarator or type specifier). If the declarator or type specifier that declares the identifier appears outside of any block or list of parameters, the identifier has file scope, which terminates at the end of the translation unit. If the declarator or type specifier that declares the identifier appears inside a block or within the list of parameter declarations in a function definition, the identifier has block scope, which terminates at the end of the associated block. If the declarator or type specifier that declares the identifier appears within the list of parameter declarations in a function prototype (not part of a function definition), the identifier has function prototype scope, which terminates at the end of the function declarator. If an identifier designates two different entities in the same name space, the scopes might overlap. If so, the scope of one entity (the inner scope) will end strictly before the scope of the other entity (the outer scope). Within the inner scope, the identifier designates the entity declared in the inner scope; the entity declared in the outer scope is hidden (and not visible) within the inner scope.

作为标识符属性,早期定义为:

  1. An identifier can denote an object; a function; a tag or a member of a structure, union, or enumeration; a typedef name; a label name; a macro name; or a macro parameter. The same identifier can denote different entities at different points in the program. A member of an enumeration is called an enumeration constant. Macro names and macro parameters are not considered further here, because prior to the semantic phase of program translation any occurrences of macro names in the source file are replaced by the preprocessing token sequences that constitute their macro definitions.

这让我得出这个结论:

作为类型说明符struct s声明标识符 s在“函数原型(prototype)中的参数声明列表”中,它具有(标识符 s )函数原型(prototype)范围。这意味着类似这样的事情:

void f2()
{ //inside **some** function block after the above declaration
    struct s { int a; } v; //new s identifier being declared

    f(&v); //not compatible types
}

但是在此之后我们有:

  1. Structure, union, and enumeration tags have scope that begins just after the appearance of the tag in a type specifier that declares the tag. Each enumeration constant has scope that begins just after the appearance of its defining enumerator in an enumerator list. Any other identifier has scope that begins just after the completion of its declarator.

这意味着完全不同的东西:

void f3()
{ //inside **some** function block after the above declaration
    struct s { int a; } v; //completing incomplete type

    f(&v); //ok
}

看起来 gcc 和 clang 遵循 p4(用编译 f 声明的警告总结):

warning: ‘struct s’ declared inside parameter list will not be visible outside of this definition or declaration
void f(struct s *);

使用 clang 的情况类似:

warning: declaration of 'struct s' will not be visible outside of this function [-Wvisibility]
void f(struct s *);

任何人都愿意解释一下确定 s 范围的正确方法是什么?函数原型(prototype)中的标识符 f

我指的是INCITS/ISO/IEC 9899-2011[2012]标准文件;使用 gcc 编译器(和 clang)使用以下标志进行编译:

-std=c11 -pedantic

请求完整的代码:

目前(通过使用 GCC 和 clang 编译):

void f(struct s {int _;});

struct s g;

将给出以下错误(由 clang 提供):

prog.c:1:15: warning: declaration of 'struct s' will not be visible outside of this function [-Wvisibility]
void f(struct s {int _;});
              ^
prog.c:3:10: error: tentative definition has type 'struct s' that is never completed
struct s g; 
         ^
prog.c:3:8: note: forward declaration of 'struct s'
struct s g; 

在这种情况下,引用标准可能是正确的行为,也可能不是正确的行为(p4 和 p7 冲突 - 或者是吗? - 我不知道)。

作者:p7 struct sg的声明中应该引用 f 函数原型(prototype)中声明的相同标识符。因此,不应该因定义不完整类型的变量而导致任何编译器错误。

但是到p4 struct sf的函数原型(prototype)中声明必须具有在函数声明符末尾终止的作用域。那么 struct s 的声明在g的声明中应该创建另一个标识符 s (是不完整结构的标签,因此错误消息)。

最佳答案

首先,您不能在参数列表中定义类。我找不到可以编译 void f(struct s {int _;}); 的编译器,所以当你问这个问题时,也许你有一个不合格的答案。您可以在标准中找到此规则: [dcl.fct]:

Types shall not be defined in return or parameter types.

其次,根据 C++03 (ISO/IEC 14882:2003) 标准一直到 C++17 (N4659),有关参数列表中类型的前向声明的规则没有改变( [basic.scope.pdecl]):

if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration

因此,您在函数的参数列表中转发声明的类型的范围位于该函数的命名空间中。您仍然必须在使用之前定义类型。从 C++03 开始​​,如下代码是合法的: Demo

void f(struct s); // forward declares s
struct s{int i;}; // defines s forward declared above

void f(s a){ // use s
   std::cout << a.i << std::endl;
}

int main()
{
   s g;
   g.i = 0;
   f(g);
}

为了更具体地回答您的问题,struct s您在函数 f 中转发声明具有命名空间范围。

关于c - 前向声明结构体的范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36238813/

相关文章:

c - 从一组映射到另一组

JavaScript:当范围已经允许访问时,为什么 checkLeaf 需要参数?

javascript - 我不断收到 "sum is not defined"错误

c++ - 如何在其定义附近使用显式长度/值构造函数初始化 vector 成员变量?

C++语法产生: class-head

c++ - 字符串化 block 中的空字 rune 字

c++ - 将函数汇编代码嵌入结构中

c - 解码这些 Valgrind 调试器内存错误在我的代码中意味着什么

c - 返回数组指针产生 "warning: return from incompatible pointer type"

scope - 虽然没有全局