C++ 小型与全部大写数据类型

C++ small vs all caps datatype

为什么在 C++ (MSVS) 中,定义了所有大写的数据类型(并且大多数都相同)?

  1. 这些完全一样。为什么定义所有大写版本?

    doubletypedef double DOUBLE

    chartypedef char CHAR

  2. boolBOOLtypedef int BOOL),这里所有小写和全部大写都代表布尔状态,为什么用int后者?

通过这些额外的数据类型获得了什么额外的能力?

Microsoft 决定在 Windows 代码中为所有这些类型创建宏或类型别名。他们可能会使用 all-caps WinAPI 类型别名来获得 "consistency",例如 LPCSTR.

但是它有什么真正的好处呢? None.

BOOL的情况尤其headache-inducing。虽然一些 old-school C 有这样做的约定(在实际 bool 进入该语言之前),但现在它真的只是混淆......特别是在将 WinAPI 与 C++ 一起使用时。

ALLCAPS 类型定义开始于 Windows 编程的最初几天(1.0 及之前)。例如,那时候还没有 bool 类型这样的东西。 Windows API 和 headers 是为 old-school C 定义的。C++ 在开发时甚至不存在。

因此,为了帮助更好地记录 API,引入了编译器宏,如 BOOL。尽管 BOOLINT 都是同一基础类型 (int) 的宏,这让您可以查看函数的类型签名以确定参数或 return 值旨在作为布尔值(定义为“0 表示假,任何非零值表示真”)或任意整数。

作为另一个例子,请考虑 LPCSTR。在 16 位 Windows 中,有两种指针:near 指针是 16 位指针,而 far 指针同时使用 16 位 "segment" 值和该段的 16 位偏移量。实际内存地址在硬件中计算为 ( segment << 4 ) + offset.

这些类型的指针中的每一种都有宏或类型定义。 NPSTR是一个指向字符串的near指针,LPSTR是一个指向字符串的far指针。如果它是一个 const 字符串,那么 C 将被添加到:NPCSTRLPCSTR.

您可以在 "small" 模型(默认使用 near 指针)或 "large" 模型(默认使用 far 指针)中编译代码。各种 NPxxxLPxxx "types" 会明确指定指针大小,但您也可以省略 LN 并仅使用 PSTRPCSTR 声明一个与您当前编译模式匹配的可写或 const 指针。

大多数 Windows API 函数使用 far 指针,因此您通常会在那里看到 LPxxx 指针。

BOOLINT 并不是唯一两个名称是同一基础类型的同义词的情况。考虑这样一种情况,您有一个指向 单个字符 而不是 zero-terminated 字符串的指针。也有一个名字。您将使用 PCH 作为指向字符的指针,以将其与指向 zero-terminated 字符串的 PSTR 区分开来。

即使底层指针类型完全相同,这也有助于记录代码的意图。当然有所有相同的变体:PCCH 用于指向常量字符的指针,NPCHLPCH 用于显式 near 和 far,当然还有 NPCCHLPCCH 用于 near 和 far 指向常量字符的指针。是的,在这些名称中使用 C 来表示 "const" 和 "char" 令人困惑!

当 Windows 使用 "flat" 内存模型移动到 32 位时,不再有 nearfar 指针,所有内容都只有平面 32 位指针.但是所有这些类型名称都被保留下来,以便旧代码可以继续编译,它们只是全部合并为一个。所以 NPSTRLPSTR、plain PSTR 以及上面提到的所有其他变体都成为相同指针类型的同义词(有或没有 const 修饰符)。

Unicode 大约在同一时间出现,最不幸的是,当时还没有发明 UTF-8。因此 Windows 中的 Unicode 支持采用 8 位字符的形式用于 ANSI 和 16 位字符(UCS-2,后来的 UTF-16)用于 Unicode。是的,当时人们认为 16 位字符对任何人来说都应该足够了。世界上怎么可能有超过65,536个不同的角色?! (著名遗言...)

你可以猜到这里发生了什么。 Windows 应用程序可以在 ANSI 或 Unicode ("Wide character") 模式下编译,这意味着它们的默认字符指针将是 8 位或 16 位。您可以使用上面的所有类型名称,它们将匹配您的应用程序的编译模式。几乎所有采用字符串或字符指针的 Windows APIs 都出现在 ANSI 和 Unicode 版本中,带有AW 后缀实际函数名称。例如,SetWindowText( HWND hwnd, LPCSTR lpString) 变成了两个函数:SetWindowTextA( HWND hwnd, LPCSTR lpString )SetWindowTextW( HWND hwnd, LPCWSTR lpString )SetWindowText 本身变成了一个定义为其中之一的宏,具体取决于您是为 ANSI 还是 Unicode 编译的。

那时候,您可能真的想要编写代码,以便它可以在 ANSI 或 Unicode 模式下编译。所以除了macro-ized函数名之外,还有一个问题就是你的window标题是用"Howdy"还是L"Howdy"TEXT() 宏(今天通常称为 _T())修复了这个问题。你可以这样写:

SetWindowText( hwnd, TEXT("Howdy") );

它会根据您的编译模式编译为以下任一版本:

SetWindowTextA( hwnd, "Howdy" );

SetWindowTextW( hwnd, L"Howdy" );

当然,今天大部分内容都没有实际意义。几乎每个人都在 Unicode 模式下编译他们的 Windows 应用程序。这是 Windows 所有现代版本的本机模式,API 函数的 ...A 版本是 shims/wrappers 围绕本机 Unicode ...W 版本。通过针对 Unicode 进行编译,您可以避免执行所有这些 shim 调用。但是如果你愿意,你仍然可以在 ANSI(或 "multi-byte character set")模式下编译你的应用程序,所以所有这些宏仍然存在。

只有MS知道。

我能想到的唯一好处是某些类型(例如 int)的大小取决于 OS(请参阅 the table here)。这将允许在 64 位 OS 上使用 16 位类型,还有一些 typedefs#defines。该代码将更容易移植到其他 OS 版本。

现在,如果这个 "portable" 是真的,那么其余的类型将遵循相同的约定,甚至它们的大小在所有机器中都是相同的。

这个约定可以追溯到 30 多年前 Windows 操作系统的早期。

70 年代和 80 年代初期的现行做法仍然是使用全部大写字母来编写各种汇编语言和当时的高级语言,Fortran、Cobol...以及命令行解释器和文件系统默认值。打孔卡的编码可能根植于一种习惯,可以追溯到更远的地方,直到 20 世纪初。

我1975年开始编程的时候,我们用的打卡机连小写字母都不支持as you can see on the pictures, it did not even have a shift key.

MS/DOS 是用汇编语言编写的,80 年代早期最成功的 PC 程序包(如 Lotus 1-2-3、MS Word 等)也是如此。C 是在贝尔实验室为 Unix 发明的系统并花了很长时间才在 PC 世界获得动力。

在崭露头角的微处理器世界中,实际上有 2 个独立的流派:Intel little endian 世界有所有大写汇编文档和 big endian 摩托罗拉替代方案,有小型大写汇编、C 和 Unix 操作系统以及克隆和其他奇怪的lisp 等语言。

Windows 是前者的创意,所有大写类型和修饰符的扩散在当时看起来并不难看,它看起来一致且令人放心。 Microsoft 尝试了指针修饰符的各种替代方案:far_far__farFAR 并最终完全摆脱了这些,但出于兼容性目的保留了原始的全大写类型定义,导致愚蠢的妥协,例如 32 位 LONG 即使在 64 位系统上也是如此。

这个回答并不公正,但重温这些回忆很有趣。