Windows数据类型……为什么如此冗余/不合理?
有人可以确切地知道为什么定义了以下typedef
s / #define
? 与原件相比,它们有什么价值?
typedef char CHAR; #define CONST const typedef float FLOAT; typedef unsigned __int64 DWORD64; //A 64-bit "double"-word?! typedef ULONGLONG DWORDLONG; //What's the difference? typedef ULONG_PTR DWORD_PTR; //What's the difference? typedef long LONG_PTR; //Wasn't INT_PTR enough? typedef signed int LONG32; //Why not "signed long"? typedef unsigned int UINT; //Wait.. UINT is "int", "LONG" is also int? typedef unsigned long ULONG; //ULONG is "long", but LONG32 is "int"? what? typedef void *PVOID; //Why not just say void*? typedef void *LPVOID; //What?! typedef ULONG_PTR SIZE_T; //Why not just size_t?
而且,最重要的是:
#define VOID void //Assuming this is useful (?), why not typedef?
这些背后的原因是什么? 这是某种抽象我不理解吗?
编辑 :
对于那些提到编译器交叉兼容性的人:
我的问题不在于他们为什么不使用unsigned long long
而不是DWORD64
。 我的问题是为什么有人会使用DWORD64
而不是ULONG64
(反之亦然)? 这两种类型都不是64位宽吗?
或者,作为另一个例子:即使在一个“假设的”编译器中,它意味着在各方面欺骗我们, ULONG_PTR
和UINT_PTR
以及DWORD_PTR
之间的区别是什么? 那些所有抽象数据类型不仅仅意味着相同的东西 – SIZE_T
?
但是,我在问为什么他们使用了ULONGLONG
而不是long long
– 是否有任何潜在的意义上的差异,既不long long
也不long long
也不是DWORDLONG
?
大多数这些冗余名称的存在主要有两个原因:
- 它们是为了向后兼容而保留的历史类型
- 它们是由不同开发团队产生的相同类型的不同名称(团队在如Windows这样庞大的项目中保持一致可能会非常困难)
typedef char CHAR;
char
的签名可能因平台和编译器而异,这是一个原因。 最初的开发人员可能也会保留这个字符编码的未来变化,但当然这已不再相关,因为我们现在就为此目的使用TCHAR
。
typedef unsigned __int64 DWORD64; //A 64-bit "double"-word?!
在迁移到64位期间,他们可能发现他们的一些DWORD
参数确实需要64位长,并且他们可能将其重命名为DWORD64
以便这些API的现有用户不会混淆。
typedef void *PVOID; //Why not just say void*? typedef void *LPVOID; //What?!
这个可以追溯到16位的日子,那里有常规的“近”指针,它们是16位,而“远”指针是32位。 类型上的L
前缀代表“长”或“远”,现在没有意义,但在那些日子里,这些可能是这样定义的:
typedef void near *PVOID; typedef void far *LPVOID;
更新:至于FLOAT
, UINT
和ULONG
,鉴于未来的变化,这些只是“更多抽象是好的”的例子。 请记住,Windows也可以在x86以外的平台上运行 – 您可以想到一种架构,其中浮点数以非标准格式表示,并且API函数已经过优化以利用此表示。 这可能与C的float
数据类型冲突。
当Windows API头文件在25年前首次构建时, int
为16位, long
为32位。 随着时间的推移,头文件已经发生变化,以反映编译器和硬件的变化。
此外,Microsoft C ++并不是唯一可以与Windows头文件一起使用的C ++编译器。 当Microsoft添加size_t
关键字时,并非所有编译器都支持它。 但他们可以轻松创建一个宏SIZE_T
来表达它。
此外,还有(或曾经)自动化工具将API头文件从C / C ++转换为其他语言。 其中许多工具最初编写为使用当前(当时)标头定义。 如果微软只是按照你的建议改变头文件来简化它们,那么很多工具都会停止工作。
基本上,头文件将Windows类型映射到最小公分母,以便多个工具可以使用它们。 它有时似乎有些混乱,我怀疑如果微软愿意抛弃任何后向兼容性,他们可以减少很大一部分混乱。 但这样做会打破很多工具(更不用说很多文档)了。
所以,是的,Windows头文件有时是一团糟。 这是我们为进化,向后兼容性以及使用多种语言的能力付出的代价。
附加信息:
我同意乍一看所有这些定义似乎都很疯狂。 但是,随着人们看到Windows头文件随着时间的推移而发展,我了解它们是如何产生的。 大多数这些定义在引入时都非常有意义,即使现在它们看起来很疯狂。 至于特定情况ULONGLONG
和DWORD64
,我想它们是为了保持一致而添加的,因为旧的头文件有ULONG
和DWORD
,所以程序员会期望另外两个。 至于为什么ULONG
和DWORD
都被定义为同一个东西,我可以想到几种可能性,其中两种是:
- 一个API团队使用了
ULONG
,另一个使用了DWORD
,ULONG
文件被合并时,他们只保留两者而不是通过转换为其中一个而破坏代码。 - 有些程序员在使用
ULONG
比DWORD
更舒服。ULONG
意味着您可以进行数学运算的整数类型,而DWORD
只是意味着某种类型的通用32位值,通常是您不想修改的键,句柄或其他值。
你最初的问题是,看似疯狂的定义背后是否存在某些推理,或者是否存在一个你不会遗漏的抽象概念。 简单的答案是定义的演变,当时的变化是有意义的。 没有特别的抽象,尽管意图是如果你编写代码来使用头文件中定义的类型,那么你应该能够毫无困难地将代码从32位移植到64位。 也就是说, DWORD
在两种环境中都是相同的。 但是,如果在API表示返回值为HANDLE
时使用DWORD
作为返回值,那么您将遇到麻烦。
一个原因是在C编译器之间保持某种可移植性。
特别是DWORD64,理论上你只需要改变DWORD64的定义就可以在其他编译器上编译代码。