章节目录

对象大小和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个字节的对象可以保存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 有符号整数

下一节