4-3 对象大小与sizeof运算符

对象大小

正如你在第4.1节——基础数据类型介绍中所学到的,现代计算机的内存通常以字节为单位组织,每个内存字节都有唯一的地址。到目前为止,将内存视为一堆储物格或信箱来存取信息,并将变量视为访问这些储物格或信箱的名称,这种比喻非常有用。

然而这种比喻存在一个不准确之处——大多数对象实际占用的内存超过1字节。单个对象可能使用1、2、4、8甚至更多连续的内存地址。对象占用的内存大小取决于其数据类型。

由于我们通常通过变量名(而非直接通过内存地址)访问内存,编译器能够隐藏特定对象实际占用的字节数细节。当我们在源代码中访问某个变量x时,编译器会根据变量x的类型确定需要检索多少字节数据,并输出相应的机器语言代码来处理这些细节。

即便如此,了解对象占用内存仍有若干实用价值:

首先,对象占用内存越多,其能承载的信息量就越大。

单比特可存储两种可能值:0 或 1:

bit 0
0
1

2位可表示4种可能的值:

bit 0 bit 1
0 0
0 1
1 0
1 1

3位可表示8种可能的值:

bit 0 bit 1 bit 2
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

概括而言,一个具有n位(其中n为整数)的对象可存储2n(即2的n次方,通常写作2n)个唯一值。因此,使用8位字节的对象可存储2⁸(即256)种不同值。而使用2字节的对象则能存储2¹⁶(即65536)种不同值!

由此可见,对象的大小决定了其存储唯一值的上限——使用更多字节的对象能存储更多唯一值。我们将在后续讨论整数时深入探讨这一特性。

其次,计算机的可用内存是有限的。每次定义对象时,都会占用一小部分可用内存,且该占用将持续到对象存在期间。由于现代计算机拥有海量内存,这种影响通常微不足道。但对于需要大量对象或数据的程序(例如渲染数百万多边形的游戏),使用1字节对象与8字节对象之间的差异可能非常显著。

关键洞见
新手程序员常过度专注于优化代码以尽可能减少内存占用。在大多数情况下,这种做法带来的差异微乎其微。应专注于编写可维护的代码,仅在优化能带来实质性收益时才进行优化。


基本数据类型的大小

接下来的问题显然是“给定数据类型的对象占用多少内存?”。令人惊讶的是,C++标准并未明确定义任何基本类型的精确大小(以位为单位)。

相反,标准规定如下:

  • 对象必须至少占用1字节(确保每个对象拥有独立的内存地址)。
  • 字节必须至少为8位。
  • 整数类型char、short、int、long和long long的最小尺寸分别为8位、16位、16位、32位和64位。
  • char和char8_t恰好为1字节(至少8位)。

命名法
当我们谈论某种类型的大小时,实际上指的是该类型实例化对象的大小。

在本教程系列中,我们将通过一些适用于现代架构的合理假设来呈现简化视图:

  • 字节为8位。
  • 内存支持字节寻址(可独立访问内存中的每个字节)。
  • 浮点运算支持符合IEEE-754标准。
  • 我们采用32位或64位架构。

基于上述假设,我们可以合理地得出以下结论:

Category Type Minimum Size Typical Size
Boolean bool 1 byte 1 byte
Character char 1 byte (exactly) 1 byte
wchar_t 1 byte 2 or 4 bytes
char8_t 1 byte 1 byte
char16_t 2 bytes 2 bytes
char32_t 4 bytes 4 bytes
Integral short 2 bytes 2 bytes
int 2 bytes 4 bytes
long 4 bytes 4 or 8 bytes
long long 8 bytes 8 bytes
Floating point float 4 bytes 4 bytes
double 8 bytes 8 bytes
long double 8 bytes 8, 12, or 16 bytes
Pointer std::nullptr_t 4 bytes 4 or 8 bytes

提示

为实现最大可移植性,不应假设对象大于指定的最小尺寸。

或者,若需假设某类型具有非最小尺寸(例如 int 至少占用 4 字节),可使用 static_assert 使编译器在架构不符时终止构建。具体实现方法详见第 9.6 课——Assert 与 static_assert。

相关内容
您可以在这里找到更多关于C++标准对各种类型最小尺寸规定的信息。


sizeof 运算符

为确定特定机器上数据类型的大小,C++ 提供了一个名为 sizeof 的运算符。sizeof 运算符sizeof operator是一元运算符,可接受类型或变量作为参数,并返回该类型对象的大小(以字节为单位)。您可以编译并运行以下程序来了解某些数据类型的大小:

#include <iomanip> // for std::setw (which sets the width of the subsequent output)
#include <iostream>
#include <climits> // for CHAR_BIT

int main()
{
    std::cout << "A byte is " << CHAR_BIT << " bits\n\n";

    std::cout << std::left; // left justify output

    std::cout << std::setw(16) << "bool:" << sizeof(bool) << " bytes\n";
    std::cout << std::setw(16) << "char:" << sizeof(char) << " bytes\n";
    std::cout << std::setw(16) << "short:" << sizeof(short) << " bytes\n";
    std::cout << std::setw(16) << "int:" << sizeof(int) << " bytes\n";
    std::cout << std::setw(16) << "long:" << sizeof(long) << " bytes\n";
    std::cout << std::setw(16) << "long long:" << sizeof(long long) << " bytes\n";
    std::cout << std::setw(16) << "float:" << sizeof(float) << " bytes\n";
    std::cout << std::setw(16) << "double:" << sizeof(double) << " bytes\n";
    std::cout << std::setw(16) << "long double:" << sizeof(long double) << " bytes\n";

    return 0;
}

以下是作者机器的输出结果:

bool:           1 bytes
char:           1 bytes
short:          2 bytes
int:            4 bytes
long:           4 bytes
long long:      8 bytes
float:          4 bytes
double:         8 bytes
long double:    8 bytes

您的结果可能因编译器、计算机架构、操作系统、编译设置(32位与64位)等因素而有所不同。
尝试对不完整类型(如void)使用sizeof操作符将导致编译错误。

(我:尝试基本类型和void的类型大小的结果如下:)
image
image

对于 gcc 用户
若未禁用编译器扩展功能,gcc 会允许 sizeof(void) 返回 1 值,而非触发诊断错误(指针运算)。我们在第 0.10 课——配置编译器:编译器扩展中展示了如何禁用编译器扩展功能。
似乎通过不了[clang version 21.1.8 (Fedora 21.1.8-4.fc43),gcc version 15.2.1 20260123 (Red Hat 15.2.1-7) (GCC)]
image

您也可以对变量名使用 sizeof 运算符:

#include <iostream>

int main()
{
    int x{};
    std::cout << "x is " << sizeof(x) << " bytes\n";

    return 0;
}

image

面向高级读者
sizeof 操作符不包含对象使用的动态分配内存。动态内存分配将在后续课程中讨论。


基本数据类型的性能

在现代机器上,基本数据类型的对象运行速度很快,因此使用或复制这些类型时通常无需担心性能问题。

顺带一提……
你可能会认为占用内存较少的类型比占用内存较多的类型更快。但事实并非总是如此。CPU通常针对特定数据大小(如32位)进行了优化,与该大小匹配的类型可能处理得更快。在这样的机器上,32位整型可能比16位短整型或8位字符型更快。

posted @ 2026-02-13 13:22  游翔  阅读(0)  评论(0)    收藏  举报