章节目录

对象大小和sizeof运算符

本节阅读量:

对象大小

正如您之前所学的那样,现代机器上的内存通常以字节为单位进行组织,每个字节的内存都有一个唯一的地址。到目前为止,可以将内存想象成一排文件柜或邮箱,我们可以在其中存放和取出信息,而变量则是用来访问这些文件柜或邮箱的名称。

然而,这种类比在一个方面并不完全准确——大多数对象实际上占用不止1个字节的内存。单个对象可以使用1、2、4、8甚至更多个连续的内存地址。对象使用的内存量取决于其数据类型。

由于我们通常通过变量名(而不是直接通过内存地址)来访问内存,编译器为我们隐藏了给定对象使用多少字节的细节。当我们访问某个变量x时,编译器知道需要读取多少字节的数据(基于变量x的类型),并会替我们处理这一操作。

即便如此,了解对象使用了多少内存还是很有意义的,原因有以下几点。

首先,对象使用的内存越多,它可以容纳的信息就越多。

单个比特可以保存2个可能的值,即0或1:

2比特可以保存4个可能的值:00,01,10,11

3比特可以保存8个可能的值:000,001,010,011,100,101,110,111

概括来说,具有n位(其中n是整数)的对象可以保存2^n(2的n次幂,也常写作2^n)个不同的值。因此,一个8位大小的对象可以保存2^8(256)个不同的值。而使用2个字节(每个字节8位)的对象可以保存2^16(65536)个不同的值!

因此,对象的大小限制了它能存储的不同值的数量——使用更多字节的对象可以存储更多的不同值。当我们讨论整数时,会进一步探讨这一点。

其次,计算机的空闲内存是有限的。每次定义对象时,只要该对象存在,就会占用一小部分空闲内存。由于现代计算机拥有大量内存,这种影响通常可以忽略不计。然而,对于需要大量对象或数据的程序(例如,渲染数百万个多边形的游戏),使用1字节和8字节对象之间的差异可能非常显著。


基本数据类型大小

显而易见的下一个问题是”不同数据类型的变量分别占用多少内存?”。

也许令人意外的是,C++标准并未定义任何基本类型的确切大小。它只为整型和char类型定义了最小大小(以比特为单位),而将所有类型的实际大小留给具体的编译器实现来决定!C++标准也不假设一个字节一定是8比特。

在本教程系列中,我们将通过做出一些合理的假设来简化概念,这些假设通常适用于现代架构:

  1. 一个字节是8比特。
  2. 内存是字节可寻址的,因此最小的对象是1个字节。
  3. 浮点数符合IEEE-754标准。
  4. 我们采用的是32位或64位体系结构。

基于此,我们可以列出如下表格:

类别 类型 最小大小(字节) 通常大小(字节)
布尔 bool 1 1
字符 char 1 1
wchar_t 1 2或4
char8_t 1 1
char16_t 2 2
char32_t 4 4
整数 short 2 2
int 2 4
long 4 4或8
long long 8 8
浮点数 float 4 4
double 8 8
long double 8 8或12或16
指针 std::nullptr_t 4 4或8

sizeof运算符

为了确定特定机器上数据类型的大小,C++提供了一个名为sizeof的运算符。sizeof运算符是一元运算符,它接受一个类型或变量,并返回其大小(以字节为单位)。您可以编译并运行以下程序,来查看各数据类型的大小:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <iomanip> // 引入std::setw (可以指定输出数据的宽度)
#include <iostream>

int main()
{
    std::cout << std::left; // 左对齐
    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;
}

以下是作者机器的输出:

1
2
3
4
5
6
7
8
9
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将导致编译错误。

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

1
2
3
4
5
6
7
8
9
#include <iostream>

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

    return 0;
}
1
x is 4 bytes

基本数据类型性能

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


4.1 Void

上一节

4.3 有符号整数

下一节