Skip to content
编码者
编码者

IT规划、数字化转型、架构设计、项目管理、软件开发

  • 首页
  • 系统设计
  • 程序员基础
    • 计算机网络
  • 生活随记
  • 编程记录
    • C语言编程
    • Python编程
    • iOS App开发
    • .NET技术栈
    • WordPress
    • Unity游戏开发
    • UE虚幻引擎
  • 数据科学
    • 数据合规
    • 数据分析
  • 行业领域
    • 软件开发服务
    • 供应链
  • 思维框架
    • 咨询框架
    • 项目管理
  • 人工智能
  • 关于我
编码者

IT规划、数字化转型、架构设计、项目管理、软件开发

C语言中为什么Win 64机器int和long类型是4个字节?

编码者, 2024年11月14日2024年11月14日

三位C/C++程序员走进了一家酒吧。第一位争辩说 sizeof(void*) 等于 sizeof(long),第二位争辩说 sizeof(void*) 等于 sizeof(int),第三位争辩说是 sizeof(long long)。与此同时,他们每个人都对,也都错(需要上关于可移植C代码的课)。这是怎么回事呢?

程序员在写“Hello, World!”后,可能写的第一个程序是这样的:

#include <stdio.h>
int main () {
  printf("sizeof(int): %zu\n", sizeof(int));
  printf("sizeof(long): %zu\n", sizeof(long));
  printf("sizeof(long long): %zu\n", sizeof(long long));
  printf("sizeof(void*): %zu\n", sizeof(void*));
}

注意使用了 %zu 格式说明符,这是C99新增的特性,但对于老编译器不一定兼容!(这篇文章更多是讨论将旧代码迁移到新机器时的考虑,而不是将新代码迁移到旧机器。没有标准兼容的C编译器会使得编写可移植的C代码更加困难,这也是另一个博客话题)。

当我在我的x86-64 OSX机器上运行这段代码时,得到的输出是:

sizeof(int): 4
sizeof(long): 8
sizeof(long long): 8
sizeof(void*): 8
看起来我就是故事中第一位程序员的情况,因为在我的机器上,sizeof(long) 等于 sizeof(void*)。还要注意,sizeof(long long) 也是相等的。

但如果我在一台32位机器上编译这段代码,会发生什么呢?幸运的是,我的处理器支持32位二进制的向后兼容,所以我可以在本地交叉编译并运行。示例:

➜  clang sizeof.c -Wall -Wextra -Wpedantic
➜  file a.out
a.out: Mach-O 64-bit executable x86_64
➜  clang sizeof.c -Wall -Wextra -Wpedantic -m32
➜  file a.out
a.out: Mach-O executable i386
➜  ./a.out
sizeof(int): 4
sizeof(long): 4
sizeof(long long): 8
sizeof(void*): 4

哇,突然间 sizeof(void*) 等于 sizeof(int) 等于 sizeof(long)!这似乎是故事中第二位程序员的情况。

第一位和第二位程序员可能都同意指针的大小等于各自机器的字长,但这个假设对于可移植的C/C++代码来说也是错误的!

第三位程序员经历了安装Windows编译器的痛苦过程,并构建了一个64位命令行应用(公平地说,安装OSX的命令行工具更糟糕;安装大多数操作系统的编译器都很麻烦)。当他们运行该程序时,看到的是:

sizeof(int): 4
sizeof(long): 4
sizeof(long long): 8
sizeof(void*): 8

这是第三种情况(故事中的第三位程序员)。在这种情况下,只有 sizeof(long long) 等于 sizeof(void*)

数据模型

这些程序员看到的情况被称为数据模型。

第一位程序员在一台64位的x86-64 OSX机器上运行,使用的是LP64数据模型,其中long(L),long long(更大的long long),以及指针(P)是64位的,但int是32位的。

第二位程序员在32位的x86 OSX机器上运行,使用的是ILP32数据模型,其中int(I),long(L),和指针(P)是32位的,但long long是64位的。

第三位程序员在64位的x86-64 Windows机器上运行,使用的是LLP64数据模型,其中只有long long(LL)和指针(P)是64位的,int和long是32位的。

数据模型sizeof(int)sizeof(long)sizeof(long long)sizeof(void*)示例
ILP3232b32b64b32bWin32, i386 OSX & Linux
LP6432b64b64b64bx86-64 OSX & Linux
LLP6432b32b64b64bWin64

还有一些更旧的数据模型,比如LP32(Windows 3.1,Macintosh,其中int是16位),以及一些更奇特的数据模型,如ILP64和SILP64。因此,了解数据模型对于编写可移植的C/C++代码非常重要。

历史背景

计算机中,地址空间不足的问题一直存在,并且会继续存在。随着计算机性能和内存的便宜,应用程序变得越来越大。公司希望销售具有更大字长的芯片来访问更多内存,但早期的采用者不想购买无法运行其最喜爱应用程序的计算机(因为应用程序还没有为新硬件编译)。有人大喊虚拟机,接着一个椅子飞过来。

本文强调了为什么LP64优于ILP64的原因:ILP64使得只需要32位精度的可移植C代码更难维护(在ILP64中,int是64位,而short是16位!)。它提到,如果数据结构不包含指针,它们的大小在LLP64和ILP32之间是相同的,这是微软的方向。LLP64本质上是具有64位指针的ILP32模型。

Linux还支持一个叫做x32的ABI,它可以使用x86-64 ISA的改进,但使用32位指针来减小数据结构的大小,避免使用64位指针。

如果想了解更多关于字长和数据模型的演变,以及由此产生的“苦难”,这篇论文是一个很好的参考。它描述了微软最终放弃对16位数据模型的支持,改为支持Windows XP 64位。它提到,业界在LP64、LLP64和ILP64之间分歧较大,因为从ILP32向移植的代码会以不同的方式出错。它还指出,Windows应用程序中使用long的情况更多,而Unix应用程序中使用int的情况更多。它还提出了一个关键点,很多来自ILP32时代的程序员假设 sizeof(int) == sizeof(long) == sizeof(void*),但在LP64/LLP64时代这并不成立。

论文还指出,C语言中的typedef直到1977年才加入,那时硬件制造商仍然无法达成一致,无法确定char(CHAR_BITS)有多少位,而有些机器使用的是24位寻址方案。stdint.h和inttypes.h还没有出现。

这篇文章讨论了从ILP32到LP64切换的两个主要类别的影响,并提供了非常好的代码示例。文章最后的那一节值得一读,提供了代码审查时要关注的要点。

结论

字长或ISA并不能告诉你 sizeof(int)、sizeof(long) 或 sizeof(long long) 的具体大小。我们还看到,一台机器可以支持多种不同的数据模型(当我用 -m32 标志编译并运行相同的代码时)。

C标准告诉你这些类型的最小保证大小,但数据模型(作为ABI的一部分,虽然外部但遵循C标准)则告诉你标准整数和指针的具体大小。

原文地址:Data Models and Word Size

补充内容

64-BIT PROGRAMMING MODELS

更多资料:

  • 64-Bit Programming Models: Why LP64?
  • The Long Road to 64 Bits
  • The UNIX System – 64bit and Data Size Neutrality
  • 64-bit data models
  • C Language Data Type Models: LP64 and ILP32
  • ILP64, LP64, LLP64
  • x32 ABI
  • difference between stdint.h and inttypes.h
  • Abstract Data Models
  • The New Data Types
  • Is there any reason not to use fixed width integer types (e.g. uint8_t)?
  • Why did the Win64 team choose the LLP64 model?
Post Views: 260
C语言编程

文章导航

Previous post
Next post

发表回复 取消回复

您的邮箱地址不会被公开。 必填项已用 * 标注

近期文章

  • 2025年AI行业趋势综述
  • 《Unity入门实战》0006 – 第一个 C# 示例代码,演示如何捕获输入
  • 《Unity入门实战》0005 – 创建 C# 脚本
  • 如何通过联盟营销在2025年将您的科技博客变现
  • IT 尽职调查(IT Due Diligence, ITDD) —— 投资与并购视角下的指南与清单
  • 《Unity入门实战》0004 – Unity 刚体(Rigidbody)
  • 《Unity入门实战》0003 – Unity 编辑器窗口与常用快捷键
  • 《Unity入门实战》0002 – 安装Unity 和 Visual Studio
  • 《Unity入门实战》0001 – 什么是 Unity 以及为什么选择它
  • 系统架构设计:核心概念、关键主题和整体思维框架的初步解析

近期评论

    归档

    • 2025 年 5 月 (10)
    • 2025 年 4 月 (5)
    • 2025 年 2 月 (1)
    • 2024 年 12 月 (4)
    • 2024 年 11 月 (7)
    • 2024 年 9 月 (1)
    • 2024 年 8 月 (4)
    • 2024 年 7 月 (1)
    • 2024 年 2 月 (1)
    • 2023 年 12 月 (3)
    • 2023 年 11 月 (6)
    • 2023 年 10 月 (4)
    • 2023 年 9 月 (2)
    • 2023 年 8 月 (38)
    • 2022 年 2 月 (1)
    • 2022 年 1 月 (13)
    • 2021 年 1 月 (1)
    • 2020 年 10 月 (1)
    • 2020 年 1 月 (1)
    • 2014 年 7 月 (2)

    分类

    • 业务领域 (2)
      • 咨询行业 (2)
    • 人工智能 (3)
    • 外语 (1)
      • 英语 (1)
    • 思维框架 (8)
      • 架构 (3)
      • 项目管理 (2)
    • 数据科学 (3)
    • 生活笔记 (23)
      • FESH 周报 (1)
    • 程序员基础 (3)
      • 计算机网络 (2)
    • 系统设计 (1)
    • 编程笔记 (54)
      • .NET技术栈 (3)
      • C语言编程 (1)
      • Golang技术栈 (1)
      • iOS App开发 (1)
      • Python编程 (18)
      • UE虚幻引擎 (1)
      • Unity游戏开发 (7)
      • Wordpress (3)
      • 工具 (1)
    • 行业动态 (14)
    ©2025 编码者 | WordPress Theme by SuperbThemes