ALLCAPS 类型定义始于 Windows 编程的最初几天(1.0 及之前)。例如,当时还没有 bool 类型这样的东西。 Windows API 和标头是为老式 C 定义的。C++ 在开发时甚至都不存在。
因此,为了帮助更好地记录 API,引入了像 BOOL 这样的编译器宏。即使BOOL 和INT 都是相同底层类型(int)的宏,这让您可以查看函数的类型签名以了解参数或返回值是否打算作为布尔值(定义为“ 0 表示 false,任何非零值表示 true") 或任意整数。
作为另一个例子,考虑LPCSTR。在 16 位 Windows 中,有两种指针:near 指针是 16 位指针,far 指针使用 16 位“段”值和该段的 16 位偏移量。实际内存地址在硬件中计算为( segment << 4 ) + offset。
每种类型的指针都有宏或类型定义。 NPSTR 是 near 指向字符串的指针,LPSTR 是 far 指向字符串的指针。如果是const 字符串,那么C 将被添加到:NPCSTR 或LPCSTR。
您可以在“小”模型(默认使用 near 指针)或“大”模型(默认使用 far 指针)中编译代码。各种NPxxx 和LPxxx“类型”将明确指定指针大小,但您也可以省略L 或N,只使用PSTR 或PCSTR 来声明一个可写或常量指针匹配您当前的编译模式。
大多数 Windows API 函数使用 far 指针,因此您通常会在那里看到 LPxxx 指针。
BOOL 与 INT 并不是两个名称是同一基础类型的同义词的唯一情况。考虑一个例子,你有一个指向单个字符的指针,而不是一个以零结尾的字符串。也有一个名字。您可以使用 PCH 作为指向字符的指针,以将其与指向零终止字符串的 PSTR 区分开来。
即使底层指针类型完全相同,这也有助于记录代码的意图。当然有所有相同的变体:PCCH 用于指向常量字符的指针,NPCH 和 LPCH 用于显式近和远,当然 NPCCH 和 LPCCH 用于近和远指针一个恒定的字符。是的,在这些名称中使用C 来表示“const”和“char”令人困惑!
当 Windows 使用“平面”内存模型迁移到 32 位时,不再有 near 或 far 指针,只有平面 32 位指针用于所有内容。但是所有这些类型名称都被保留下来,以使旧代码可以继续编译,它们都被合并为一个。所以NPSTR、LPSTR、普通PSTR,以及上面提到的所有其他变体都成为相同指针类型的同义词(有或没有const修饰符)。
Unicode 大约在同一时间出现,最不幸的是,UTF-8 还没有被发明出来。因此,Windows 中的 Unicode 支持采用 ANSI 的 8 位字符和 Unicode 的 16 位字符(UCS-2,后来的 UTF-16)的形式。是的,当时人们认为 16 位字符应该对任何人都足够了。世界上怎么可能有超过 65,536 个不同的角色?! (著名的遗言……)
你可以猜到这里发生了什么。 Windows 应用程序可以在 ANSI 或 Unicode(“宽字符”)模式下编译,这意味着它们的默认字符指针将是 8 位或 16 位。您可以使用上面的所有类型名称,它们将与编译您的应用程序的模式相匹配。几乎所有采用字符串或字符指针的 Windows API 都有 ANSI 和 Unicode 版本,带有 A 或 W 后缀实际的函数名。例如,SetWindowText( HWND hwnd, LPCSTR lpString) 变成了两个函数:SetWindowTextA( HWND hwnd, LPCSTR lpString ) 或 SetWindowTextW( HWND hwnd, LPCWSTR lpString )。而SetWindowText 本身变成了一个宏定义为其中一个或另一个,这取决于你是为 ANSI 还是为 Unicode 编译的。
那时,您可能真的想编写代码,以便可以在 ANSI 或 Unicode 模式下编译它。所以除了宏化的函数名之外,还有一个问题是你的窗口标题是使用"Howdy"还是L"Howdy"。 TEXT() 宏(现在更常见的是 _T())修复了这个问题。你可以写:
SetWindowText( hwnd, TEXT("Howdy") );
它会根据你的编译模式编译成其中任何一个:
SetWindowTextA( hwnd, "Howdy" );
SetWindowTextW( hwnd, L"Howdy" );
当然,今天大部分内容都没有实际意义。几乎每个人都在 Unicode 模式下编译他们的 Windows 应用程序。这是所有现代版本的 Windows 上的本机模式,API 函数的 ...A 版本是本机 Unicode ...W 版本的垫片/包装器。通过为 Unicode 编译,您可以避免执行所有这些 shim 调用。但是您仍然可以根据需要在 ANSI(或“多字节字符集”)模式下编译您的应用程序,因此所有这些宏仍然存在。