微软怎么能说WinAPI中一个单词的大小是16位?

我刚开始学习WinAPI。 在MSDN中,为WORD数据types提供了以下说明。


一个16位无符号整数。 范围是0到65535十进制。
这个types在WinDef.h中声明如下:
typedef unsigned short WORD;

很简单,它与我一直用来学习的其他资源相匹配,但它怎么可以明确地说,它是16位? 维基百科上的C数据types页面指​​定

short / short int / signed short / signed short int
短签名整数types。
能够至less包含[-32767,+32767]范围; 因此它至less有 16位的大小。

所以根据C标准, short的大小short可能是32位。 但谁决定使用什么样的比特尺寸呢? 我在这里find一个实际的解释 。 具体而言,该行:

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

所以这是ISA,这是有道理的,我想。 这是我迷路的地方。 看看维基百科上的Windows页面 ,我可以在侧面看到:

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

我真的不知道这些是什么,但我认为这些是处理器,每个将有一个ISA。 也许Windows支持这些平台,因为所有这些平台都保证使用16位作为unsigned short ? 这听起来不太对,但我对这个东西还没有足够的了解。

回到我的问题:Windows API如何能够inputtypedef unsigned short WORD; 那么当C标准本身不能保证一个总是16位时, WORD是一个16位无符号整数?

Solutions Collecting From Web of "微软怎么能说WinAPI中一个单词的大小是16位?"

简而言之,一个WORD总是16位。

由于WORD总是16位,但是unsigned short不是,所以WORD并不总是unsigned short

对于Windows SDK支持的每个平台,Windows头文件包含#ifdef样式的宏,它们可以检测编译器及其平台,并将Windows SDK定义的类型( WORDDWORD等)关联到相应大小的平台类型。

这就是为什么Windows SDK实际使用内部定义的类型,如WORD ,而不是使用语言类型:以便他们可以确保它们的定义总是正确的。

微软工具链附带的Windows SDK可能是懒惰的,因为微软的c ++工具链总是使用16位无符号短裤。

我不会指望随Visual Studio C ++一起提供的windows.h如果放到GCC,clang等等中,就能正常工作,因为很多细节,包括导入dll使用平台SDK分发的.iib文件的机制,都是Microsoft特定的实现。


不同的解释是:

微软说WORD是16位。 如果“某人”想要调用一个Windows API,那么它们必须传递一个16位值,API将该字段定义为WORD。 微软也可能会说,为了构建一个有效的Windows程序,使用Windows SDK中存在的Windows头文件,用户必须选择一个16位short的编译器。

c ++规范并没有说编译器必须以16位来实现short s,微软说你选择编译windows可执行文件的编译器必须这样做。

最初有一个假设,所有打算在Windows上运行的代码都要用微软自己的编译器编译 – 或者一个完全兼容的编译器。 这就是它的工作方式。 Borland C:匹配的微软C. Zortech的C:匹配的微软C. gcc:没有太多,所以你甚至没有尝试(更不用说没有运行时等)。

随着时间的推移,这个概念被编纂并扩展到了其他操作系统(或者其他操作系统首先得到它),现在它被称为ABI- 应用程序二进制接口 – 对于一个平台,假设所有该平台的编译器练习,需要)来匹配ABI。 这意味着匹配整数类型(除其他外)的大小的期望。

你没有问的一个有趣的相关问题是:为什么16位称为一个单词 ? 为什么我们的32位和64位体系结构中的32位是双字(双字),本地机器的“字”大小是32位还是64位,而不是16位? 因为:80286。

在Windows头文件中有很多基于平台的#define可以确保WORD是16位,DWORD是32等。在过去的某些情况下,我知道他们为每个平台分配一个合适的SDK。 在任何情况下,没有什么魔术,只是正确的#defines和标题的混合物。

对于BYTE=8bitsBYTE=8bitsWORD=16bitsDWORD=32bits (双字)术语来自Intel的指令助记符和文档。这只是术语,在这一点上并不意味着“机器字“在运行代码的实际机器上。

我猜:

这些C型名称最初可能是由于与C99标准化的uint8_tuint16_tuint32_t相同的原因而引入的。 这个想法可能允许具有不兼容的ABI(例如16位int或32位short )的C实现继续编译使用WinAPI的代码,因为ABI在struct s中使用DWORD而不是longint ,并且函数参数/返回值。

可能随着Windows的发展,足够多的代码开始以各种方式对WORD和DWORD的精确定义,MS决定对标准化的typedef进行标准化 。 这与C99 uint16_t想法不同,在这里你不能认为它是unsigned short

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

或者,也许这个想法只是使用与asm匹配的名称 ,以确保没有人对类型的宽度感到困惑。 在Windows的早期,直接用asm编写程序,而不是C,很受欢迎。 WORD / DWORD使得用户在写入文档时更清晰。

或者,也许这个想法只是为可移植代码提供一个固定宽度的类型 。 例如#ifdef SUNOS :将其定义为该平台的适当类型。 正如你注意到的,在这一点上,这一切都是好事。

Windows API如何能够输入unsigned short WORD; 那么当C标准本身不能保证一个总是16位时,WORD是一个16位无符号整数?

你是正确的,记录确切的typedef意味着在一个使用不同ABI的系统(例如一个long 64位或short 32位)的系统中正确实现WinAPI头文件是不可能的。 这是x86-64 Windows ABI long 32bit类型的原因之一。 x86-64 System V ABI(Linux,OS X等)使64位类型变long

但是,每个平台都需要一个标准的ABIstruct布局,甚至函数参数的解释,都要求所有的代码在所用类型的大小上达成一致。 来自相同C编译器的不同版本的代码可以互操作,甚至可以遵循相同ABI的其他编译器。 (但是,C ++ ABIs不够标准化,例如g++从未标准化过ABI,而新版本则会破坏ABI的兼容性。

请记住,C标准只告诉你在每个合格的C实现中你可以假定什么。 C标准还指出,有符号整数可能是符号/量级,补码或二进制补码。 不过,任何具体的平台都将使用硬件的任何表示。

平台可以自由地标准化基本C标准未定义或实现定义的任何内容。 例如,x86 C实现允许创建未对齐的指针存在,甚至取消引用它们。 这种情况在__m128i矢量类型中发生了很多。


所选择的实际名称将WinAPI与x86的传统相结合,不幸的是,任何不熟悉x86 asm或至少Windows的16位DOS遗产的人都会感到困惑。


8086指令助记符包括w对于word和d对dword通常用作idiv符号除法的设置。

  • cbw :将AL(字节)扩展为AX(字)
  • cwd :符号将AX(word)扩展为DX:AX(dword) , 即将 ax的符号位复制到dx每一位。

这些insn在32bit和64bit模式下依然存在,并且完全一样。 (386和x86-64增加了扩展版本,正如你可以在英特尔的insn set引用的提取中看到的那样)。还有lodswrep movsw等字符串指令。

除了这些助记符,操作数的大小需要在某些情况下明确指定,例如
mov dword ptr [mem], -1 ,其中两个操作数都不是可以指示操作数大小的寄存器。 (要看看汇编语言是什么样的,只需要反汇编一下,例如在Linux系统上, objdump -Mintel -d /bin/ls | less 。)

所以这个术语在x86 asm中已经遍地开花了,这是开发ABI时需要熟悉的东西。


更多的x86 asm背景,历史和当前的命名方案

没有低于这一点与WinAPI或原始问题有关,但我认为这很有趣。


另请参阅x86标记维基以获取有关英特尔官方PDF(以及其他许多好东西)的链接。 这个术语在英特尔和AMD的文档和指令助记符中是无处不在的,因为它在一个特定的体系结构的文档中是完全明确的,并且一致地使用它。

386扩展寄存器大小为32位,并引入了cdq指令: cdq (eax(dword) – > edx:eax(qword))。 (也引入了movsxmovzxmovzx或者零扩展,而不需要首先将数据写入eax )无论如何,四字是64位的,并且甚至在386之前的版本中用于fld qword ptr [mem] double精度内存操作数fld qword ptr [mem] / fst qword ptr [mem]

英特尔仍然使用这个b / w / d / q / dq惯例来进行向量指令命名 ,所以它根本不是他们试图逐步淘汰的东西。

例如pshufd insn助记符( _mm_shuffle_epi32 C内部 )被_mm_shuffle_epi32 (整数)Shuffle Dword。 psrawpsraw右移算术字。 (FP向量insns使用ps (压缩单个)或pd (压缩双向)后缀而不是p前缀。)

随着向量变得越来越宽,命名开始变得愚蠢:例如, _mm_unpacklo_epi64punpcklqdq指令 的固有 punpcklqdq :Packed-integer将L四元组punpcklqdq为Double-Quad。 (即将64位低半部分交织成一个128b)。 或移动Double-Quad未对齐的加载/存储的movdqu (16个字节)。 一些汇编程序使用o (八字)来声明16字节的整数常量,但英特尔助记符和文档总是使用dq

幸运的是,为了我们的理智,AVX 256b(32B)指令仍然使用SSE助记符,所以vmovdqu ymm0, [rsi]是一个32B的负载,但是没有quad-quad的术语。 包含操作数大小的反汇编程序即使vmovdqu ymm0, ymmword ptr [rsi]也会打印vmovdqu ymm0, ymmword ptr [rsi]


甚至一些AVX-512扩展名的名称都使用b / w / d / q术语。 AVX-512F(基础)不包括每个指令的所有元素大小的版本。 某些指令的8位和16位元件尺寸版本仅在支持AVX-512BW扩展的硬件上可用。 AVX-512DQ还有额外的dword和qword元素大小的指令,包括float / double和64bit整数之间的转换以及64b x 64b => 64b元素大小的乘法 。


一些新的说明在助记符中使用数字大小

AVX的vinsertf128和类似的提取一个256位矢量的高128位通道可能已经使用dq ,而是使用128

AVX-512引入了一些名称为vmovdqa64 (64位元素粒度的向量加载掩码)或vshuff32x4 (随机128位元素,32位元素粒度掩蔽)的insn助记符。

请注意,由于AVX-512对几乎所有指令都进行了合并屏蔽或零屏蔽,所以甚至不需要关心元素大小的指令(如pxor / _mm_xor_si128 )现在都有不同的大小: _mm512_mask_xor_epi64vpxorq )位影响64位元素)或_mm512_mask_xor_epi32vpxord )。 无掩码内部_mm512_xor_si512可以编译为vpxorqvpxord ; 没关系。

大多数AVX512新指令在其助记符中仍然使用b / w / d / q,但像VPERMT2D (从两个源矢量中选择全部置换元素)一样。

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

如果有人曾经制作过这样的平台,那么该平台的Windows API头文件将不包含行typedef unsigned short WORD;

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

像WORD这样的类型的传统使Windows早于由MASM定义的类型(后来名称改为ML)的时代早于MSDOS的时代。 未被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是一个无符号整数,与poitner的大小相同,如果是32位模式则是32位,如果是64位模式则是64位。

MASM中的DB和DW数据指令可以回溯到Intel8080汇编程序的日子。