Windows数据类型......为什么如此冗余/不合理?

时间:2011-02-28 16:15:43

标签: c winapi types history

有人可以确切地说明为什么定义了以下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(反之亦然)?这两个typedef都不是64位宽吗?

或者,作为另一个例子:即使在一个“假设的”编译器中,它意味着在各方面欺骗我们,ULONG_PTRUINT_PTR以及DWORD_PTR之间会有什么区别?那些所有抽象数据类型不仅仅意味着相同的东西 - SIZE_T

但是,询问他们使用ULONGLONG代替long long的原因 - 是否存在意义上的任何潜在差异,既不包含long long也不涵盖DWORDLONG {1}}?

3 个答案:

答案 0 :(得分:18)

大多数这些冗余名称的存在主要有两个原因:

  • 它们是为了向后兼容而保留的历史类型
  • 他们是来自不同开发团队的同一类型的不同名称(团队在如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;

更新关于FLOATUINTULONG,鉴于未来的变化,这些只是“更多抽象是好的”的例子。请记住,Windows也可以在x86以外的平台上运行 - 您可以想到一种架构,其中浮点数以非标准格式表示,并且API函数已经过优化以利用此表示形式。这可能与C的float数据类型冲突。

答案 1 :(得分:9)

25年前首次构建Windows API头文件时,int为16位,long为32位。随着时间的推移,头文件已经发生变化,以反映编译器和硬件的变化。

此外,Microsoft C ++并不是唯一可以与Windows头文件一起使用的C ++编译器。当Microsoft添加size_t关键字时,并非所有编译器都支持它。但他们可以轻松创建一个宏SIZE_T来表达它。

此外,还有(或曾经)自动化工具将API头文件从C / C ++转换为其他语言。其中许多工具最初编写为使用当前(当时)标头定义。如果微软只是按照你的建议改变头文件来简化它们,那么很多工具都会停止工作。

基本上,头文件将Windows类型映射到最小公分母,以便多个工具可以使用它们。它有时似乎有些混乱,我怀疑如果微软愿意抛弃任何后向兼容性,他们可以减少很大一部分混乱。但这样做会打破很多工具(更不用说很多文档了)。

所以,是的,Windows头文件有时候很乱。这是我们为进化,向后兼容性以及使用多种语言的能力付出的代价。

其他信息:

我同意乍一看所有这些定义似乎都很疯狂。但是,随着人们看到Windows头文件随着时间的推移而发展,我了解它们是如何产生的。大多数这些定义在引入时都非常有意义,即使现在看起来很疯狂。至于特定情况ULONGLONGDWORD64,我认为它们是为了保持一致而添加的,因为旧的头文件有ULONGDWORD,因此程序员会期望其他二。至于为什么ULONGDWORD在它们是同一个东西时被定义的原因,我可以想到几种可能性,其中两种是:

  • 一个API团队使用了ULONG而另一个使用了DWORD,当汇总了头文件时,他们只保留了两个而不是通过转换为其中一个来破坏代码。
  • 一些程序员在ULONG而不是DWORD方面更为自在。 ULONG意味着你可以进行数学运算的整数类型,而DWORD只是意味着某种类型的通用32位值,通常是一个键,句柄或其他值,你不会我想修改。

你最初的问题是,看似疯狂的定义背后是否存在某种推理,或者是否存在抽象,你不会错过。简单的答案是定义的演变,当时的变化是有意义的。没有特别的抽象,尽管意图是如果你编写代码来使用头文件中定义的类型,那么你应该能够将你的代码从32位移植到64位没有麻烦。也就是说,DWORD在两种环境中都是相同的。但是,如果您在API声明返回值为DWORD时使用HANDLE作为返回值,那么您将遇到麻烦。

答案 2 :(得分:1)

一个原因是在C编译器之间保持某种可移植性。

特别是DWORD64,理论上你只需要改变DWORD64的定义就可以在其他编译器上编译代码。