Windows 数据类型...为什么如此冗余/缺乏描述性?

发布于 2024-10-19 17:42:08 字数 1522 浏览 0 评论 0原文

有人能确切地解释一下为什么定义了以下 typedefs/#defines 吗?与原件相比,它们有什么价值?

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

然而,我am问为什么他们使用 ULONGLONG 而不是 long long ——这两者在含义上是否存在任何潜在的差异,而 没有涵盖long long 还是 DWORDLONG

Could someone please exactly why the following typedefs/#defines have been defined? What value do they have, compared to the originals?

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?

And, best of all:

#define VOID void                 //Assuming this is useful (?), why not typedef?

What's the reasoning behind these? Is it some sort of abstraction I'm not understanding?


Edit:

For those people mentioning compiler cross-compatilibity:

My question is not about why they didn't use unsigned long long instead of, say, DWORD64. My question is about why would anyone use DWORD64 instead of ULONG64 (or vice-versa)? Aren't both of those typedefed to be 64 bits wide?

Or, as another example: Even in a "hypothetical" compiler that was meant to deceive us in every respect, what would be the difference between ULONG_PTR and UINT_PTR and DWORD_PTR? Aren't those all abstract data types just meaning the same thing -- SIZE_T?

However, I am asking why they used ULONGLONG instead of long long -- is there any potential difference in meaning, covered by neither long long nor DWORDLONG?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

幽梦紫曦~ 2024-10-26 17:42:08

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

  • 它们是为向后兼容而保留的历史类型
  • 它们是来自不同开发团队的同一类型的不同名称(团队在这样的环境中保持一致可能非常困难)像 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 前缀代表“long”或“far”,现在没有意义,但在那些日子里,它们可能是这样定义的:

typedef void near *PVOID;
typedef void far *LPVOID;

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

Most of these redundant names exist primarily for two reasons:

  • they're historical types preserved for backward compatibility
  • they're different names for the same type that arose from different teams of developers (it can be surprisingly difficult for teams to remain consistent across such a huge project as Windows)

typedef char CHAR;

The signedness of char can vary across platforms and compilers, so that's one reason. The original developers might have also kept this open for future changes in character encodings, but of course this is no longer relevant since we use TCHAR now for that purpose.


typedef unsigned __int64 DWORD64; //A 64-bit "double"-word?!

During the move to 64-bit, they probably discovered that some of their DWORD arguments really needed to be 64 bits long, and they probably renamed it DWORD64 so that existing users of those APIs weren't confused.


typedef void *PVOID;              //Why not just say void*?
typedef void *LPVOID;             //What?!

This one dates back to the 16-bit days, when there were regular "near" pointers which were 16-bit and "far" pointers that were 32-bit. The L prefix on types stands for "long" or "far", which is meaningless now, but back in those days, these were probably defined like this:

typedef void near *PVOID;
typedef void far *LPVOID;

Update: As for FLOAT, UINT and ULONG, these are just examples of "more abstraction is good", in view of future changes. Keep in mind that Windows also runs on platforms other than x86 -- you could think of an architecture where floating-point numbers were represented in a non-standard format and the API functions were optimized to make use of this representation. This could then be in conflict with C's float data type.

倚栏听风 2024-10-26 17:42:08

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 指示返回值为 HANDLE 时使用 DWORD 作为返回值,则会遇到麻烦。

When the Windows API header files were first built 25 years ago, an int was 16 bits and a long was 32 bits. The header files have evolved over time to reflect changes in compilers and in hardware.

Also, Microsoft C++ isn't the only C++ compiler out there that works with the Windows header files. When Microsoft added the size_t keyword, not all compilers supported it. But they could easily create a macro, SIZE_T, to express it.

Also, there are (or were) automated tools that convert the API header files from C/C++ to other languages. Many of those tools were originally written to work with the current (at the time) header definitions. If Microsoft were to just change the header files to streamline them as you suggest, many of those tools would stop working.

Basically, the header files map Windows types to a least common denominator so that multiple tools can work with them. It does seem to be something of a mess at times, and I suspect that if Microsoft were willing to throw out any semblance of backward compatibility, they could reduce a large part of the mess. But doing so would break a lot of tools (not to mention a lot of documentation).

So, yes, the Windows header files are sometimes a mess. That's the price we pay for evolution, backward compatibility, and the ability to work with multiple languages.

Additional info:

I'll agree that at first glance all those definitions seem crazy. But as one who has seen the Windows header files evolve over time, I understand how they came about. Most of those definitions made perfect sense when they were introduced, even if now they look crazy. As for the specific case ULONGLONG and DWORD64, I imagine that they were added for consistency, as the old header files had ULONG and DWORD, so programmers would expect the other two. As for why ULONG and DWORD were both defined when they are the same thing, I can think of several possibilities, two of which are:

  • One API team used ULONG and another used DWORD, and when header files were consolidated they just kept both rather than breaking code by converting to one or the other.
  • Some programmers are more comfortable thinking in terms of ULONG than DWORD. ULONG implies an integer type that you can do math on, whereas DWORD just implies a generic 32-bit value of some sort, typically something that is a key, handle, or other value that you wouldn't want to modify.

Your initial question was whether there was some reasoning behind the seemingly crazy definitions, or if there's an abstraction you're not missing. The simple answer is that the definitions evolved, with the changes making sense at the time. There's no particular abstraction, although the intent is that if you write your code to use the types that are defined in the headers, then you should be able to port your code from 32-bit to 64-bit without trouble. That is, DWORD will be the same in both environments. But if you use DWORD for a return value when the API says that the return value is HANDLE, you're going to have trouble.

葬シ愛 2024-10-26 17:42:08

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

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

One reason is to maintain some kind of portability between C compilers.

In particular DWORD64, in theory you just need to change the definition of DWORD64 to get the code compiling on other compilers.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文