为什么gcc允许类型为void(非指针)的extern声明?

Dav*_*son 21 c gcc

为什么gcc允许类型为void的extern声明?这是扩展还是标准C?这有可接受的用途吗?

我猜这是一个扩展,但我没有在http://gcc.gnu.org/onlinedocs/gcc-4.3.6/gcc/C-Extensions.html找到它.

$ cat extern_void.c
extern void foo; /* ok in gcc 4.3, not ok in Visual Studio 2008 */
void* get_foo_ptr(void) { return &foo; }

$ gcc -c extern_void.c # no compile error

$ gcc --version | head -n 1
gcc (Debian 4.3.2-1.1) 4.3.2
Run Code Online (Sandbox Code Playgroud)

将foo定义为void类型当然是编译错误:

$ gcc -c -Dextern= extern_void.c
extern_void.c:1: error: storage size of ‘foo’ isn’t known
Run Code Online (Sandbox Code Playgroud)

为了进行比较,Visual Studio 2008在extern声明中给出了错误:

$ cl /c extern_void.c 
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

extern_void.c
extern_void.c(1) : error C2182: 'foo' : illegal use of type 'void'
Run Code Online (Sandbox Code Playgroud)

Jer*_*fin 6

奇怪的是(或者可能不那么奇怪......)它让我觉得gcc接受这个是正确的.

如果这是声明static而不是extern,则它将具有内部链接,并且§6.9.2/ 3将适用:

如果对象的标识符声明是暂定定义并且具有内部链接,则声明的类型不应是不完整的类型.

如果它没有指定任何存储类(extern在本例中),则适用§6.7/ 7:

如果声明对象的标识符没有链接,则对象的类型应在其声明符的末尾完成,或者如果它具有初始化器,则由init-declarator的末尾完成; 在函数参数(包括原型)的情况下,需要完成的是调整后的类型(见6.7.5.3).

我这两种情况中的任何一种void都行不通,因为(§6.2.5/ 19):

void类型[...]是不完整的类型,无法完成.

但是,这些都不适用.这似乎只留下§6.7.2/ 2的要求,这似乎允许声明一个类型的名称void:

每个声明中的声明说明符中应至少给出一个类型说明符,并在每个结构声明和类型名称的说明符限定符列表中给出.每个类型说明符列表应为以下集合之一(用逗号分隔,当一行上有多个集合时); 类型说明符可以按任何顺序出现,可能与其他声明说明符混合.

  • 空虚
  • 烧焦
  • 签名的char

[...更多类型省略]

我不确定这是非常有意的 - 我怀疑void它确实是用于派生类型(例如,指向void的指针)或函数的返回类型之类的东西,但是我找不到任何直接指定该限制的东西.


小智 5

我找到了宣布的唯一合法用途

extern void foo;
Run Code Online (Sandbox Code Playgroud)

是何时foo是链接符号(由链接器定义的外部符号),表示未指定类型的对象的地址.

这实际上很有用,因为链接符号通常用于传达内存的范围; ie .text section start address,.text section length等.

因此,使用这些符号的代码通过将它们转换为适当的值来记录它们的类型是很重要的.例如,如果foo实际上是内存区域的长度:

uint32_t textLen;

textLen = ( uint32_t )foo;
Run Code Online (Sandbox Code Playgroud)

或者,如果foo是相同内存区域的起始地址:

uint8_t *textStart;

textStart = ( uint8_t * )foo;
Run Code Online (Sandbox Code Playgroud)

在我所知道的"C"中引用链接符号的唯一替代方法是将其声明为外部数组:

extern uint8_t foo[];
Run Code Online (Sandbox Code Playgroud)

我实际上更喜欢void声明,因为它清楚地表明链接器定义的符号没有内在的"类型".