MSVC:为什么“extern void x;”是“非法使用类型'void'”吗?

2024-01-12

为什么这段代码:

extern void x;

导致:

$ cl t555.c /std:c11 /Za
t555.c(1): error C2182: 'x': illegal use of type 'void'

这里什么是非法的?

UPD。使用案例:

$ cat t555a.c t555.p.S
#include <stdio.h>

extern void x;

int main(void)
{
    printf("%p\n", &x);
    return 0;
}

       .globl  x
x:
        .space 4

$ gcc t555a.c -std=c11 -pedantic -Wall -Wextra -c && as t555.p.S -o t555.p.o && gcc t555a.o t555.p.o && ./a.exe
t555a.c: In function ‘main’:
t555a.c:7:20: warning: taking address of expression of type ‘void’
    7 |     printf("%p\n", &x);
      |                    ^
0x1004010c0

$ clang t555a.c -std=c11 -pedantic -Wall -Wextra -c && as t555.p.S -o t555.p.o && clang t555a.o t555.p.o && ./a.exe
t555a.c:7:20: warning: ISO C forbids taking the address of an expression of type 'void' [-Wpedantic]
    printf("%p\n", &x);
                   ^~
1 warning generated.
00007FF76E051120

这是一个有趣的案例。声明标识符似乎没有违反任何约束x具有外部链接的 void 类型,但它几乎无法使用。

void“是一个无法完成的不完整对象类型”(C 2018 6.2.5 19)。当对象的标识符声明为无链接时,类型必须“在其声明符末尾处完成”(6.7 7)。但对于具有外部链接的标识符来说情况并非如此;我们可以声明extern int a[]; extern struct foo b;并定义a and b后来,甚至在另一个翻译单位。

If x没有使用,我没有看到它违反任何约束。如果程序尝试使用它,则 6.9 5 将适用:

...如果在表达式中使用通过外部链接声明的标识符(而不是作为操作数的一部分)sizeof or _Alignof其结果是整型常量的运算符),在整个程序的某处应该有一个标识符的外部定义;否则,不得超过一个。

但我们无法定义x在C代码中,因为它有一个不完整的类型,并且它的类型无法完成。只要它没有定义,我们就不能使用x在表达式中而不是作为操作数sizeof or _Alignof,由于上面的段落,我们也不能将它与sizeof or _Alignof,因为这些运算符需要完整的类型。

我们可以想象x在 C 外部定义并与此 C 代码链接。因此某些汇编模块可能会提供以下定义xC 代码不知道这一点。当然,如果没有类型的定义,C 代码就无法使用对象的值。但它可以使用地址x。例如,它可以充当指针值的哨兵或其他标记。E.g.,我们可以将指针列表的列表作为指针列表传递给另一个例程,其中子列表由&x整个列表的末尾由空指针标记。 (所以两个子列表(&a, &b, &c) and (&d, &e, &f) 将被传递为(void *[]) { &a, &b, &c, &x, &d, &e, &f, NULL };.)

然而,编译printf("%p\n", &x);与 Clang 并使用-pedantic产生错误消息“ISO C 禁止获取‘void’类型表达式的地址”。其核心原因似乎是 6.3.2.1 1 排除了 的对象void类型不再是左值:

  • An lvalue是一个表达式(对象类型不是void) 可能指定一个对象;...

6.5.3.2 1 要求操作数为一元&成为左值:

  • 一元的操作数&运算符应是函数指示符、函数的结果[]或一元*运算符,或指定对象的左值...

这可能是 C 标准设计不完整的部分,因为它并不排除const void不再是左值,并且 Clang 编译extern const void x; printf("%p\n", &x);没有抱怨,但似乎没有理由以标准来对待const void and void在这方面有所不同。

一方面,微软可能已经得出结论,没有办法使用这个x因此,一旦出现问题,我们就会立即对其进行诊断extern void x被发现而不是让代码尝试使用它时发生错误x。然而,虽然编译器可以自由地发出额外的诊断消息,但它应该接受符合要求的程序。也就是说,对于符合C标准的编译器,诊断可能是警告,但可能不是阻止编译的错误。

补充说明

注意到一元的约束&允许“结果[]或一元*运算符”,我对此进行了测试:

static void foo(void *p)
{
    printf("%p\n", &*p);
}

Here, *p其本身是类型的左值void,并且这是允许的&因为约束明确允许它,而&x看起来是一个非常相似的表达式,取一个地址void,但约束不允许,因为x既不是左值也不是结果*。好奇的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MSVC:为什么“extern void x;”是“非法使用类型'void'”吗? 的相关文章

  • 类型转换 sockaddr 结构

    我正在尝试学习网络编程 并在这个过程中学习C 我对结构感到困惑sockaddr这是一个通用地址 并且sockaddr in 我的书里是这么说的 因此 我们可以填写 sockaddr in 的字段 然后强制转换 a 指向 它指向 指向 soc
  • 将数组从 C# 编组到 C++ 并返回:PInvokeStackImbalance

    我有一个 C 函数 我想从 C 访问它 问题是我不断收到 PInvokeStackImbalance 异常 但我不知道为什么 当检查异常被关闭时 一切都运行良好并且符合预期 我的 C 函数的签名是 extern C double solve
  • 如何使用Task.WhenAny并实现重试

    我有一个创建多个基于 I O 的任务的解决方案 我正在使用Task WhenAny 来管理这些任务 但通常许多任务会由于网络问题或请求限制等原因而失败 我似乎找不到一个解决方案 使我能够在使用时成功重试失败的任务Task WhenAny 方
  • C# - 如何将 IntPtr 缓冲区数据保存到文件(最快的方法)?

    我使用此代码将非托管代码中的 IntPtr 缓冲区中的字节保存到文件中 这是一个简单的回调函数 private void callback IntPtr buffer int length byte bytes new byte lengt
  • 用 C++ 解密文件,该文件使用 openssl -aes-128-cbc 加密

    我正在尝试用 C 解密文件 该文件使用以下命令加密 openssl enc nosalt aes 128 cbc pass pass test in test txt out test enc txt p 控制台显示key 098F6BCD
  • WIX 自动生成 GUID *?

    假设我生成产品 ID 为 的 WIX XML 文件 另外 对于每个组件 GUID 我都使用
  • 尝试将元素推入向量

    在头文件 我没有编写 中 已经定义了一个结构体 如下所示 struct MemoryMessage public boost counted base public FastAlloc explicit MemoryMessage Memo
  • 使用 Thread.Sleep() 时,异步编程如何与线程一起工作?

    假设 前言 在之前的问题中 我们注意到Thread Sleep阻塞线程参见 什么时候使用Task Delay 什么时候使用Thread Sleep https stackoverflow com questions 20082221 whe
  • c++11 中的 std::thread 问题

    我在尝试从标准模板库编译具有多线程的程序时遇到一些麻烦 当我尝试编译以下程序时 它返回一个晦涩的错误 include
  • 我应该使用函数还是无状态函子?

    这两段代码做同样的事情 如您所见 它将用于排序函数 哪个更好 我通常写后一种 但我看到一些程序员像以前那样做 struct val lessthan binary function
  • NHibernate 中具有不同类型答案的问题

    我正在尝试找到一个问卷问题的简洁解决方案 假设我有一个Questionnaire类有一个集合Answers e g public class Questionnaire public virtual ISet
  • 让 WIX 在项目中包含引用

    我对 WiX 和设置自定义安装程序完全陌生 所以我对问题的主题表示歉意 我有一个内部业务应用程序 日记 它构建并运行良好 因此我按照教程 官方文档添加 WiX 项目并引用日记的 csproj 然后构建并运行这个最基本版本的 WiX 安装程序
  • Boost async_write问题

    我将展示一些代码 void wh const boost system error code ec std size t bytes transferred std cout lt lt test int main int argc cha
  • 在 C# 中加密并在 Flex 中解密

    我需要解密 Flex 中的一些数据 这些数据是用 C 加密并写入文件的 为了简单起见 我选择使用 as3crypto As3 库和 Bruce Schneier C 库 AS3 as3加密链接 http code google com p
  • 无论表单上的焦点控件如何,如何捕获 Keys.F1?

    我使用了 KeyDown 事件和一些简单的代码 例如if e KeyCode Keys F1 捕获在表单上按下 F1 但如果表单上有一些文本框 或者表单上有一些带有 Dock Fill 的电子表格 则上面的代码将毫无用处并且不执行任何操作
  • 为什么 OOP 中静态类的最佳实践有所不同?

    我目前正在阅读有关 Java 最佳实践的内容 我发现根据这本书 https rads stackoverflow com amzn click com 0321356683我们必须优先选择静态类而不是非静态类 我记得在 C 最佳实践中 我们
  • 在同一条线上铸造两次

    我在项目中看到了这段代码 b的类型是void void b int a int unsigned long b 这条线毫无意义吗 我的意思是 这与a int b在所有情况下 这可能会避免 64 位 Unix 系统上的编译器警告unsigne
  • #define, #ifdef #undef #endif

    我有以下代码 define PROC ADD void main void while 1 ifdef PROC ADD Do this code here then undefined it to run the code in the
  • lambda 表达式是多线程的吗?

    lambda 表达式是多线程的吗 假设当你将数学公式编写为 lambda 方法时 当你将其传递给另一个方法时 它会是多线程的吗 不是100 清楚你问的是什么 您是否想问 lambda 是否自然地在不同的线程上运行 如果是这样 则它们只是 S
  • 你将如何开始自动化我的工作? - 第2部分

    后续这个问题 https stackoverflow com questions 2796128 how would you start automating my job 在经历了第一波进货 9 小时的复制 粘贴 后 我现在相信我已经满足

随机推荐