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'”吗? 的相关文章

  • 动态加载程序集的应用程序配置

    我正在尝试将模块动态加载到我的应用程序中 但我想为每个模块指定单独的 app config 文件 假设我的主应用程序有以下 app config 设置
  • ASP.NET MVC:这个业务逻辑应该放在哪里?

    我正在开发我的第一个真正的 MVC 应用程序 并尝试遵循一般的 OOP 最佳实践 我正在将控制器中的一些简单业务逻辑重构到我的域模型中 我最近一直在阅读一些内容 很明显我应该将逻辑放在域模型实体类中的某个位置 以避免出现 贫血域模型 反模式
  • SetCurrentDirectoryW 中的错误 206

    在我之后之前不清楚的问题 https stackoverflow com questions 44389617 long path name in setcurrentdirectoryw 我以某种方式能够创建一个具有长路径名的目录 但是
  • 如何使用 ICU 解析汉字数字字符?

    我正在编写一个使用 ICU 来解析由汉字数字字符组成的 Unicode 字符串的函数 并希望返回该字符串的整数值 五 gt 5 三十一 gt 31 五千九百七十二 gt 5972 我将区域设置设置为 Locale getJapan 并使用
  • 用于登录 .NET 的堆栈跟踪

    我编写了一个 logger exceptionfactory 模块 它使用 System Diagnostics StackTrace 从调用方法及其声明类型中获取属性 但我注意到 如果我在 Visual Studio 之外以发布模式运行代
  • OleDbDataAdapter 未填充所有行

    嘿 我正在使用 DataAdapter 读取 Excel 文件并用该数据填充数据表 这是我的查询和连接字符串 private string Query SELECT FROM Sheet1 private string ConnectStr
  • 在 ASP.NET 5 中使用 DI 调用构造函数时解决依赖关系

    Web 上似乎充斥着如何在 ASP NET 5 中使用 DI 的示例 但没有一个示例显示如何调用构造函数并解决依赖关系 以下只是众多案例之一 http social technet microsoft com wiki contents a
  • C#中如何移动PictureBox?

    我已经使用此代码来移动图片框pictureBox MouseMove event pictureBox Location new System Drawing Point e Location 但是当我尝试执行时 图片框闪烁并且无法识别确切
  • 将多个表映射到实体框架中的单个实体类

    我正在开发一个旧数据库 该数据库有 2 个具有 1 1 关系的表 目前 我为每个定义的表定义了一种类型 1Test 1Result 我想将这些特定的表合并到一个类中 当前的类型如下所示 public class Result public
  • SolrNet连接说明

    为什么 SolrNet 连接的容器保持静态 这是一个非常大的错误 因为当我们在应用程序中向应用程序发送异步请求时 SolrNet 会表现异常 在 SolrNet 中如何避免这个问题 class P static void M string
  • 垃圾收集器是否在单独的进程中运行?

    垃圾收集器是否在单独的进程中启动 例如 如果我们尝试测量某段代码所花费的进程时间 并且在此期间垃圾收集器开始收集 它会在新进程上启动还是在同一进程中启动 它的工作原理如下吗 Code Process 1 gt Garbage Collect
  • 如何查看网络连接状态是否发生变化?

    我正在编写一个应用程序 用于检查计算机是否连接到某个特定网络 并为我们的用户带来一些魔力 该应用程序将在后台运行并执行检查是否用户请求 托盘中的菜单 我还希望应用程序能够自动检查用户是否从有线更改为无线 或者断开连接并连接到新网络 并执行魔
  • 使用 x509 证书签署 json 文档或字符串

    如何使用 x509 证书签署 json 文档或字符串 public static void fund string filePath C Users VIKAS Desktop Data xml Read the file XmlDocum
  • Windows 窗体:如果文本太长,请添加新行到标签

    我正在使用 C 有时 从网络服务返回的文本 我在标签中显示 太长 并且会在表单边缘被截断 如果标签不适合表单 是否有一种简单的方法可以在标签中添加换行符 Thanks 如果您将标签设置为autosize 它会随着您输入的任何文本自动增长 为
  • 覆盖子类中的字段或属性

    我有一个抽象基类 我想声明一个字段或属性 该字段或属性在从该父类继承的每个类中具有不同的值 我想在基类中定义它 以便我可以在基类方法中引用它 例如覆盖 ToString 来表示 此对象的类型为 property field 我有三种方法可以
  • 如何在Xamarin中删除ViewTreeObserver?

    假设我需要获取并设置视图的高度 在 Android 中 众所周知 只有在绘制视图之后才能获取视图高度 如果您使用 Java 有很多答案 最著名的方法之一如下 取自这个答案 https stackoverflow com a 24035591
  • 混合 ExecutionContext.SuppressFlow 和任务时 AsyncLocal.Value 出现意外值

    在应用程序中 由于 AsyncLocal 的错误 意外值 我遇到了奇怪的行为 尽管我抑制了执行上下文的流程 但 AsyncLocal Value 属性有时不会在新生成的任务的执行范围内重置 下面我创建了一个最小的可重现示例来演示该问题 pr
  • 测试用例执行完成后,无论是否通过,如何将测试用例结果保存在变量中?

    我正在使用 NUNIT 在 Visual Studio 中使用 Selenium WebDriver 测试用例的代码是 我想在执行测试用例后立即在变量中记录测试用例通过或失败的情况 我怎样才能实现这一点 NUnit 假设您使用 NUnit
  • 是否可以在 .NET Core 中将 gRPC 与 HTTP/1.1 结合使用?

    我有两个网络服务 gRPC 客户端和 gRPC 服务器 服务器是用 NET Core编写的 然而 客户端是托管在 IIS 8 5 上的 NET Framework 4 7 2 Web 应用程序 所以它只支持HTTP 1 1 https le
  • 如何在文本框中插入图像

    有没有办法在文本框中插入图像 我正在开发一个聊天应用程序 我想用图标图像更改值 等 但我找不到如何在文本框中插入图像 Thanks 如果您使用 RichTextBox 进行聊天 请查看Paste http msdn microsoft co

随机推荐