为啥 C 程序员使用 typedef 来重命名基本类型?
Posted
技术标签:
【中文标题】为啥 C 程序员使用 typedef 来重命名基本类型?【英文标题】:Why do C programmers use typedefs to rename basic types?为什么 C 程序员使用 typedef 来重命名基本类型? 【发布时间】:2011-03-21 10:18:27 【问题描述】:所以我远不是 C 方面的专家,但是我一直在阅读的代码一直困扰着我:有人可以向我解释为什么 C(++) 程序员使用 typedef 来重命名简单类型吗?我理解你为什么将它们用于结构,但我看到的声明的确切原因是什么
typedef unsigned char uch;
typedef uch UBYTE;
typedef unsigned long ulg;
typedef unsigned int u32;
typedef signed short s16;
这是否有一些我不清楚的优势(一个程序员,他的经验是从 Java 开始的,并且在严格的类型安全语言之外还没有冒险过)?因为我想不出任何原因——看起来它只会让不熟悉该项目的人的代码可读性降低。
请随意将我视为 C 新手,老实说,我对此知之甚少,而且很可能有些事情我从一开始就误解了。 ;)
【问题讨论】:
你应该看看stdint.h
。当某些东西的类型可能与系统不同但代码需要保持兼容时,也会使用它们。
【参考方案1】:
所有[|u]intN_t
类型,其中N=8|16|32|64 等等,都是以这种精确的方式在每个架构中定义的。这是该标准不要求char
、int
、float
等具有恰好 N 位这一事实的直接结果——这太疯狂了。相反,该标准定义了每种类型的最小值和最大值作为对程序员的保证,并且在各种体系结构中,类型可能会超出这些界限。这并不罕见。
您帖子中的typedef
s 用于在特定架构中定义一定长度的类型。这可能不是命名的最佳选择;在我看来,u32
和 s16
有点太短了。此外,公开名称ulg
和uch
是一件坏事,可以在它们前面加上一个特定于应用程序的字符串,因为它们显然不会公开。
希望这会有所帮助。
【讨论】:
您绝对不能在它们前面加上_
或__
。这些名称是为为实现保留而不是为您的应用程序保留的。
只有一个下划线后跟一个大写字母或另一个下划线的标识符才完全超出 lim
但最好还是避免使用 _ 前缀。
使用 _ 前缀有什么意义?我认为它只会增加代码的大小并降低代码的可读性。【参考方案2】:
重命名类型而不改变其暴露的语义/特征没有多大意义。在你的例子中
typedef unsigned char uch;
typedef unsigned long ulg;
属于该类别。除了取一个更短的名字之外,我不明白这一点。
但是这些
typedef uch UBYTE;
typedef unsigned int u32;
typedef signed short s16;
是一个完全不同的故事。例如,s16
代表“有符号 16 位类型”。这种类型不一定是signed short
。哪个特定类型将隐藏在 s16
后面取决于平台。程序员引入了这种额外级别的命名间接来简化对多个平台的支持。如果在其他平台上签名的 16 位类型恰好是 signed int
,程序员只需更改一个 typedef 定义。 UBYTE
显然代表无符号机器字节类型,不一定是unsigned char
。
值得注意的是,C99 规范已经为特定宽度的整数类型提供了标准命名法,例如int16_t
、uint32_t
等。在不支持 C99 的平台上坚持这个标准命名约定可能更有意义。
【讨论】:
+1 推荐使用标准名称,但比我更圆滑.. :-) 哈哈,@R..,你让我笑了。 :-)【参考方案3】:长话短说, 您可能希望这样做以使您的代码可移植(减少工作量/编辑)。 这样您就不必依赖于“int”,而是使用可以是任何您想要的任何东西的 INTEGER。
【讨论】:
【参考方案4】:以下引自 The C Programming Language (K&R)
除了纯粹的审美问题,使用的主要原因有两个 类型定义。
首先-参数化程序
首先是针对可移植性问题对程序进行参数化。 如果 typedefs 用于数据类型 可能取决于机器,仅 typedefs 需要更改时 程序被移动。
一种常见的情况是对各种整数使用 typedef 名称 数量,然后做出适当的 一组 short、int 和 long 的选择 对于每台主机。 类型如 标准库中的 size_t 和 ptrdiff_t 就是示例。
斜体部分告诉我们程序员typedef
为可移植性的基本类型。如果我想确保我的程序在不同的平台上运行,使用不同的编译器,我会尽一切可能确保它的可移植性,typedef
就是其中之一。
当我开始在 Windows 平台上使用 Turbo C 编译器进行编程时,它给了我们 int
2 的大小。当我转移到 Linux 平台和 GCC 编译器时,我得到的大小是 4。如果我使用Turbo C 依赖于 sizeof( int )
始终为 2 的断言,它不会正确移植到我的新平台。
希望对你有帮助。
以下来自 K&R 的报价与您的查询无关,但为了完整起见,我也已将其发布。
第二——提供更好的文档
typedef 的第二个目的是为 程序 - 一种称为 Treeptr 的类型可能比仅声明为 指向复杂结构的指针。
【讨论】:
【参考方案5】:这些模式中的大多数都是来自阅读和复制现有不良代码的不良做法。它们通常反映了对 C 需要或不需要什么的误解。
-
类似于
#define BEGIN
,只是它节省了一些输入而不是输入更多内容。
类似于#define FALSE 0
。如果您认为“字节”是最小的可寻址单元,那么char
就是定义的字节。如果您的“字节”概念是八位字节,那么char
是八位字节类型,或者您的机器没有八位字节类型。
对于不会打字的人来说真是丑陋的简写......
是一个错误。应该是typedef uint32_t u32;
或者更好,uint32_t
应该直接使用。
与4相同。将uint32_t
替换为int16_t
。
请在他们所有人身上盖上“认为有害”的标记。 typedef
应该在你真的需要创建一个新类型时使用.
【讨论】:
鉴于编译 typedef 的底层架构没有上下文,我认为这样禁止它们是不合适的。char
根据定义不是八位字节。 sizeof(char) == 1
根据定义,它的意思不是一个字节,而是“至少 CHAR_BIT 位”(CHAR_BIT 很可能超过 8,尽管现在很少见) .此外,所有[|u]intN_t
类型,其中 N=8|16|32|64 等等,都以这种精确的方式在每个体系结构中定义。唯一不好的模式是暴露ulg
和uch
,可以在它们前面加上__
,因为它们实际上是保留名称。
我从来没有说过 char 根据定义是一个八位字节。我说 either char 是八位字节或您的系统没有八位字节类型。这句话完全正确。
另外,您不能(在应用程序中)为这些类型添加前缀__
。 implementation 可以(并且必须)这样做,如果它做出这样的 typedef。我认为您对“保留”的含义感到非常困惑。【参考方案6】:
我们使用它来使其特定于项目/平台,一切都有一个共同的命名约定
pname_int32, pname_uint32, pname_uint8
-- pname 是项目/平台/模块名称
还有一些#defines
pname_malloc, pname_strlen
像 unsigned char 这样的长数据类型更易于阅读和缩短为 pname_uint8,这也使其成为所有模块的约定。
移植时只需要修改单个文件,从而使移植变得容易。
【讨论】:
为什么您需要自己的替代标准库已经提供的一切? @R.. 这样好吗? #define pname_free(ptr) free(ptr) #define pname_safe_free(ptr) \ if(ptr!=NULL) \ pname_free((void*) ptr); \ ptr =NULL;\ OEM 功能呢?当您替换上述宏并保留其他标准名称时,它看起来不太可读。可能又是它的个人意见。 @R.. Microsoft 特定关键字是 __int8、__int16 等。您将如何为 Microsoft C 编译器编写代码,甚至可以在 linux 上使用 gcc 编译?这有意义吗? #ifdef(_MSC_VER) typedef unsigned __int8 PNAME_INT #elif defined(__GNUC) typedef unsigned int8_t PNAME_INT #endif @Praveen,在缺少正确 standard 类型的损坏编译器上,只需提供一个插入式标头,为它们提供标准名称。不需要可怕的PNAME_
污染。顺便说一句,您的 pname_safe_free
宏已损坏。考虑:if (foo) pname_safe_free(foo); else ...
如果它让你更快乐,请将其更改为 if (bar)
。关键是你会在else
得到一个编译器错误。类似函数的宏应该从不封装在
中。使用((ptr)?(free((ptr)),(ptr)=0):0)
或将代码包装在do ... while(0)
中。【参考方案7】:
有很多原因。我的想法是:
-
Typename 变得更短,因此代码也更小,更易读。
较长结构名称的别名效果。
在特定团队/公司/风格中使用的约定。
移植 - 在所有操作系统和机器上具有相同的名称。它的本机数据结构可能略有不同。
【讨论】:
【参考方案8】:有时它被用来将像volatile unsigned long
这样笨重的东西简化为更紧凑的东西,比如vuint32_t
。
其他时候它有助于提高可移植性,因为像 int
这样的类型在每个平台上并不总是相同的。通过使用 typedef,您可以将您感兴趣的存储类设置为与平台最接近的匹配,而无需更改所有源代码。
【讨论】:
补充一点,在 c++ 中,它甚至可以在一个平台上用一个模拟浮点数的整个类替换可能是引擎盖下的本机类型(如浮点数) (例如对于定点,在没有浮动的平台上) 虽然,POSIX 保留以_t
结尾的类型名称。【参考方案9】:
这允许便携性。例如,您需要一个无符号的 32 位整数类型。那是哪种标准类型?你不知道 - 它是实现定义的。这就是为什么你 typedef
一个单独的类型是 32 位无符号整数并在你的代码中使用新类型。当您需要在另一个 C 实现上进行编译时,您只需更改 typedef
s。
【讨论】:
不推荐使用这种用法,因为 C 有uint32_t
等。
@R:仅当您的编译器已开始实现 C99 时。上次我查了一下,微软没有。
编译器几乎不必实现 C99 即可拥有 inttypes.h
和 stdint.h
。如果您的编译器缺少它们,只需使用目标架构的正确定义进行替换即可。使用非标准名称没有任何借口。
@R:在这种情况下,无论如何您都在有效地提供 typedef。您的方法是可取的,但与您自己将 typedef 添加到项目中并没有什么不同。
@R: uint32_t is 来自 stdint.h 的 typedef,因此几乎不推荐使用。它只是标准化并为您完成。以上是关于为啥 C 程序员使用 typedef 来重命名基本类型?的主要内容,如果未能解决你的问题,请参考以下文章
C/c++:语言中关键字typedef(类型重命名)enum(整型常量值集合,代替#define)的使用
为啥我们要在 C 中如此频繁地对结构进行 typedef 呢?