微软如何能说WinAPI中的word大小为16位?

7
我刚开始学习WinAPI。在MSDN中,WORD数据类型的解释如下:
WORD 一个16位无符号整数。范围是0到65535十进制。 此类型在WinDef.h中声明如下: typedef unsigned short WORD;
很简单,并且它与我用于学习的其他资源相匹配,但是怎么能确定它是16位呢?维基百科上的C数据类型页面指定:
short / short int / signed short / signed short int 短有符号整数类型。 能够包含至少[-32767,+32767]范围;因此, 它至少16位大小。
因此,根据C标准,short的大小可能为32位。但是谁决定要使用哪些位大小呢?我在这里找到了一个实用解释。具体来说,是这一行:

...它取决于处理器(更具体地说,是ISA,指令集架构,例如x86和x86-64)和编译器,包括编程模型。

所以应该是ISA决定了,这很有道理。这就是我迷失方向的地方。查看维基百科上的Windows页面,我在侧边栏中看到了这个:

平台 ARM、IA-32、Itanium、x86-64、DEC Alpha、MIPS、PowerPC

我不太清楚这些是什么,但我认为这些是处理器,每个处理器都有一个ISA。也许Windows支持这些平台是因为它们都保证使用16位来表示无符号短整型?这听起来不太对,但我对这方面的了解还不足以进一步研究。
回到我的问题:Windows API如何可以typedef unsigned short WORD;然后说WORD是一个16位无符号整数,而C标准本身并不保证short始终是16位?

10
标准规定了 short 类型的大小至少为 16 位,具体大小由实现决定。微软是这个实现者,并且他们选择了 16 位。 - Mysticial
4
如果微软为其平台定义了 ABI,其中 short 始终是 16 位长,则在微软平台上,short 始终是 16 位长。这是他们的决定。 - EOF
1
据我所知,类型WORDDWORD早于uint16_tuint32_t - Weather Vane
1
@WeatherVane:我想知道“DWORD”和“uint32_t”是否有任何保证是别名兼容的?如果一个具有32位“int”和“long”的平台使用“unsigned int”作为“uint32_t”的类型,并使用“unsigned long”作为“DWORD”的类型,那么现代版本的gcc将假定通过“DWORD *”写入永远不会修改“uint32_t”,并且通过“uint32_t *”写入永远不会修改“DWORD”。 - supercat
1
微软表示WORD是16位的,但并没有说明short的大小。 - Ajay
显示剩余2条评论
6个回答

9
简单来说,一个WORD始终是16位。
由于WORD始终是16位,但unsigned short不是,所以WORD并不总是unsigned short
对于Windows SDK支持的每个平台,windows头文件包含#ifdef风格的宏,可以检测编译器及其平台,并将Windows SDK定义的类型(WORDDWORD等)与适当大小的平台类型相关联。
这就是为什么Windows SDK实际上使用内部定义的类型(如WORD)而不是使用语言类型: 这样他们可以确保他们的定义始终是正确的。
随Microsoft工具链一起发布的Windows SDK可能有点懒惰,因为Microsoft c++工具链总是使用16位无符号短整型。
如果在GCC、clang等中放置Visual Studio C++附带的windows.h,我不会指望它能正确工作,因为包括使用.iib文件导入dll的机制在内的许多细节都是Microsoft特定的实现方式。
另一种解释是:
Microsoft表示WORD是16位。如果“某人”想调用Windows API,他们必须传递一个16位值,其中API将字段定义为WORD
Microsoft还可能表示,为了构建一个有效的Windows程序,使用其Windows SDK中存在的windows头文件,用户必须选择具有16位short的编译器。
C++规范并不表示编译器必须将short实现为16位 - Microsoft表示您选择用于构建Windows可执行文件的编译器必须这样做。

@DavidHeffernan 他们不能这样做,但是他们可以记录编译器供应商需要确保每个大小的适当数据类型映射到Windows类型。 Windows库是在一定的假设下构建的(ABI),必须由用于针对其开发的工具链满足。 - Joe
@DavidHeffernan 这不是问题所在。这是OP在问题陈述中包含的一个误导性内容。但问题本身涉及到WORD的大小。 - Chris Becke
1
有趣的是现在可以实现可移植性。#include <stdint.h> typedef uint16_t WORD; - Joshua

8
最初有一个假设,即所有旨在在Windows上运行的代码都将使用Microsoft自己的编译器编译 - 或完全兼容的编译器。事实证明是这样的。Borland C:与Microsoft C匹配。Zortech的C语言:与Microsoft C匹配。gcc不是很匹配,所以你甚至不尝试(更不用说没有运行时等)。
随着时间的推移,这个概念被系统化并扩展到其他操作系统(或者其他操作系统首先采用了它),现在它被称为平台的应用程序二进制接口(ABI),假定(在实践中需要)该平台上的所有编译器均符合ABI。这意味着符合整型大小的期望(以及其他一些内容)。
一个有趣的相关问题是:为什么16位被称为“字”?在我们的32和现在的64位体系结构中,本机机器“字”大小为32位或64位,而不是16位,为什么32位是“双字”?因为:80286。

一些研究告诉我,16位被称为一个单词是由于历史原因,并且由于兼容性问题而没有改变。这也让我感到困惑,但比实际的typedef声明更容易理解。 - codegrumps
3
@brokyle - 没错。将来,当我们运行在128位von Neumann机器或8量子比特的量子机器上时,我们的_Windows代码_仍将使用16位的WORD和32位的DWORD。因为:80286。 - davidbak
1
在英特尔的汇编文档和语法中,"word=16bits, dword=32bits, qword=64bits"随处可见。例如,pshufd指令助记符(_mm_shuffle_epi32)是Packed(整数) Shuffle Dword。psraw是Packed Shift Right Arithmetic Word。(打包-FP指令使用ps或pd后缀而不是p前缀。)另请参阅x86标签wiki获取更多链接。术语的最初原因:8086。指令如cbw(将al符号扩展为ax)与cwd(将ax符号扩展为dx:ax)。386添加了cwde(将ax符号扩展为eax)。 - Peter Cordes

2
在Windows头文件中,有很多#define,根据平台可以确保WORD为16位,DWORD为32位等等。在过去的某些情况下,我知道他们为每个平台分发了适当的SDK。无论如何,这只是适当的#define和头文件的混合。

问题是微软如何确保short为16位,当C标准没有规定它时。 - David Heffernan
@DavidHeffernan,OP的帖子中有很多问题,更加强调的是“微软如何说WORD是16位”的问题。 - M.M
@DavidHeffernan,请看标题,注意末尾的问号。 - M.M
@DavidHeffernan,正文中有4个不同的问题。例如,“但是到底是谁决定使用什么位大小呢?” - M.M

2
BYTE=8位WORD=16位DWORD=32位(双字)这些术语来自于Intel 8086指令助记符和文档。 这只是术语,目前并不意味着运行代码的实际机器上的“机器字”(machine word)的大小。

我的猜测:

这些C类型名称最初引入的原因可能与C99标准化 uint8_tuint16_tuint32_t相同。 思想可能是允许具有不兼容ABI(例如16比特与32比特的int)的C实现仍能编译使用WinAPI的代码,因为ABI使用DWORD而不是longintstruct,函数参数/返回值中。

随着Windows的发展,足够多的代码以各种方式依赖于WORD和DWORD的确切定义,MS决定标准化精确的typedef。 这与C99 uint16_t的想法不同,您不能假设它是unsigned short

正如@supercat所指出的那样,这可能对别名规则很重要。例如,如果您通过DWORD*修改unsigned long []数组,则保证它将按预期工作。 但是,如果您通过DWORD*修改unsigned int []数组,则编译器可能会假设这不会影响它已经在寄存器中具有的数组值。 这对于printf格式字符串也很重要。(C99的<stdint.h>解决方案是像PRIu32这样的预处理器宏。)

或者,也许想法仅仅是使用与汇编相匹配的名称,以确保没有人会对类型的宽度感到困惑。 在Windows的早期时期,直接使用asm编写程序(而不是C)很受欢迎。 WORD/DWORD使得为用汇编语言编写的人员提供更清晰的文档。

也许这个想法只是为了提供固定宽度类型以便于可移植代码。例如,对于SUNOS,使用适当的类型来定义#ifdef SUNOS。目前而言,这就是它的全部用处,正如你所注意到的:
“Windows API如何能够typedef unsigned short WORD;并且说WORD是一个16位无符号整数,而C标准本身并不保证short始终是16位呢?”
你是正确的,记录精确的typedef意味着在使用不同ABI的系统中(例如其中long为64位或short为32位的系统),无法正确地实现WinAPI头文件。这也是x86-64 Windows ABI将long作为32位类型的部分原因。x86-64 System V ABI(Linux,OS X等)将long作为64位类型。
然而,每个平台确实需要一个标准ABI。结构布局,甚至函数参数的解释都需要所有代码同意所使用的类型的大小。同一C编译器的不同版本的代码可以互操作,甚至遵循相同ABI的其他编译器也可以。 (但是,C++ ABI不够稳定,不能标准化。例如,g ++从未标准化过ABI,新版本确实会破坏ABI兼容性。)
请记住,C标准只告诉您可以在每个符合C实现中假定什么。 C标准还说,有符号整数可能是符号/大小,反码或二进制补码。任何特定平台都将使用硬件所使用的任何表示方式。
平台可以自由地标准化基本C标准未定义或实现定义的任何内容。例如,x86 C实现允许创建不对齐指针存在,甚至对它们进行解引用。这在__m128i矢量类型中经常发生。
实际选择的名称将WinAPI与其x86遗产联系起来,对于不熟悉x86汇编语言或至少Windows 16位DOS遗产的任何人来说,这是令人困惑的。
包括w表示字和d表示双字的8086指令助记符通常用作idiv有符号除法的设置。

这些指令在32位和64位模式下仍然存在并且执行相同的操作。(386和x86-64添加了扩展版本,如Intel的指令集参考中所示)。还有lodsw, rep movsw等字符串指令。

除了这些助记符外,在某些情况下需要显式指定操作数大小,例如:
mov dword ptr [mem], -1,其中没有一个操作数是可以暗示操作数大小的寄存器。(要查看汇编语言的外观,只需反汇编一些内容。例如在Linux系统上,objdump -Mintel -d /bin/ls | less)。

因此,在x86汇编中术语非常杂乱,这是你在开发ABI时需要熟悉的内容。


x86汇编背景、历史和当前命名方案

以下内容与WinAPI或原始问题无关,但我认为它很有趣。


请参见标签wiki,以获取Intel官方PDF的链接(以及其他好东西)。这种术语在Intel和AMD文档和指令助记符中仍然普遍存在,因为在一个使用一致性的特定架构的文件中,它完全不含糊。

386扩展了寄存器的大小到32位,并引入了cdq指令:cdq (eax (dword) -> edx:eax (qword))。(还引入了movsx和movzx,以在不需要将数据先加载到eax中的情况下进行符号或零扩展。)总之,quad-word是64位,即使在386之前也用于双精度内存操作数fld qword ptr [mem] / fst qword ptr [mem]。
英特尔仍然使用这种b/w/d/q/dq约定来命名向量指令,因此这绝不是他们试图淘汰的东西。例如,pshufd insn助记符 (_mm_shuffle_epi32 C intrinsic) 是Packed (integer) Shuffle Dword。psraw是Packed Shift Right Arithmetic Word。(FP向量insn使用ps (packed single)或pd (packed double)后缀而不是p前缀。)
随着向量变得越来越宽,命名开始变得愚蠢:例如,_mm_unpacklo_epi64是punpcklqdq指令的intrinsic:Packed-integer Unpack L Quad-words to Double-Quad。或movdqu用于Move Double-Quad Unaligned loads/stores(16字节)。一些汇编器使用o(oct-word)声明16字节整数常量,但英特尔助记符和文档始终使用dq。
为了保持我们的理智,AVX 256b (32B) 指令仍然使用SSE助记符,因此vmovdqu ymm0, [rsi]是一个32字节的加载,但没有四重术语。即使不含糊,包括操作数大小的反汇编器也会打印vmovdqu ymm0, ymmword ptr [rsi]
一些AVX-512扩展名甚至使用b/w/d/q术语。AVX-512F(基础)并未包含每个指令的所有元素大小版本。某些指令的8位和16位元素大小版本仅在支持AVX-512BW扩展的硬件上可用。还有AVX-512DQ,用于额外的双字和四字元素大小指令,包括浮点/双精度和64位整数之间的转换以及一个乘法运算,其大小为64b x 64b => 64b。
一些新指令在助记符中使用数字大小。
AVX的vinsertf128等提取256位向量的高128位通常可以使用dq,但它使用128
AVX-512引入了一些insn助记符,例如vmovdqa64(在64位元素粒度下进行掩码的向量加载)或vshuff32x4(以32位元素粒度进行混洗128b元素)。
请注意,由于AVX-512几乎所有指令都具有合并掩码或零掩码,即使以前不关心元素大小的指令(如pxor / _mm_xor_si128),现在也有不同的大小:_mm512_mask_xor_epi64 (vpxorq)(每个掩码位影响一个64位元素),或_mm512_mask_xor_epi32 (vpxord)。无掩码内在函数_mm512_xor_si512可以编译为vpxorqvpxord;这没有关系。

大多数AVX512新指令在助记符中仍然使用b / w / d / q,例如VPERMT2D(从两个源向量中选择元素的完整置换)。


0

目前还没有支持 Windows API 但不是 16 位的 unsigned short 的平台。

如果有人制作了这样的平台,该平台的 Windows API 标头将不包括以下行:typedef unsigned short WORD;

您可以将 MSDN 页面视为描述 MSVC++ 在 x86/x64 平台上的典型行为。


几乎所有其他平台上的编译器实现也都有16位短整型。 - phuclv
1
“您可以将MSDN页面视为描述MSVC ++的典型行为。” - MSDN描述了Windows的ABI。这不仅限于微软的编译器,而是针对该平台的任何编译器。 - IInspectable

0

像WORD这样的类型的遗留问题可以追溯到MSDOS时代,遵循MASM定义的类型(后来更名为ML)。Windows API没有采用MASM的有符号类型,如SBYTE、SWORD、SDWORD、SQWORD。

MASM中的QWORD / SQWORD可能直到MASM / ML支持80386才被定义。

当前的参考资料:

http://msdn.microsoft.com/en-us/library/8t163bt0.aspx

Windows添加了像HANDLE、WCHAR、TCHAR等类型。

对于Windows / Microsoft编译器来说,size_t是一个无符号整数,与指针相同大小,如果在32位模式下,则为32位,如果在64位模式下,则为64位。

MASM中的DB和DW数据指令可以追溯到英特尔8080汇编程序的年代。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接