根据大小选择数据类型

Choosing a data type based on size

本文关键字:选择 数据类型      更新时间:2023-10-16

我想知道当你们(有经验的程序员)在C++编程时,你如何选择原始数据类型?例如,如果你有一个知道迭代 4 次的 for 循环,你是使用无符号的 short int 还是 int8_t?为什么?或者为什么不呢?这一切都是为了优化内存吗?

这么多不同大小的int类型,我想一定是有原因的。

当不需要专门的大小时,我使用本机单词大小。例如,我对所有计数循环使用 unsigned int

访问硬件时,我使用特定大小的整数,例如 uint16_tuint8_t .

对于具有大量内存和速度的现代台式计算机,无需过早进行优化,例如担心可变大小。

在嵌入式系统中,特别是在内存受限的情况下,可变大小可能会有所不同。

最佳做法是使程序正常工作,然后根据需要应用优化。 一个非常小的错误程序不如一个大型的正确和健壮的程序有用。 速度也一样。

理由

处理器在使用其本机字大小时非常高效。 字大小因处理器而异。 有些处理器是 8 位字大小,有些是 16 位,有些是 32 位(是的,大小介于或更大之间)。

不是处理器字(寄存器)大小的数据可能会导致处理器付出额外的努力。 例如,具有 16 位字大小的处理器需要进行两次内存提取才能构建 32 位数量。 32 位处理器将获取 32 位的 16 位数量,而不是可能需要移动位或屏蔽位才能将 16 位数量放入处理器寄存器中的正确位置。

硬件寄存器有不同的大小。 当存在 8 位宽的硬件寄存器时,人们不希望将 32 位写入寄存器,这就是为什么存在 8 位宽数据类型而不是 32 位数据类型的原因。

通常,如果没有特殊原因,否则,我会使用int来表示带有循环计数器的所有迭代循环。当然,在迭代容器时,我使用新的样式循环:

std::string str = "Hello, World!";
for(auto c: str)
    std::cout << c;

(一种相当愚蠢的打印"你好,世界!"的方式)

使用 int 的原因是,它意味着在该特定体系结构上速度的整数的"最佳"大小。使用 int8_t 或其他类型可能不会慢,但也不能保证编译器在进行比较时不必执行额外的步骤来"扩大"int8_t,例如:

 for(int8_t i = 0; i < 4; i++)
   ...

本质上变成:

 for(int8_t i = 0; (int)i < 4; i++)
  ...

因此,代码更大更慢。

在除了非常小的处理器[或计数器本身不经常使用的非常大的循环]之外的所有东西中,循环变量i无论如何都会在寄存器中,并且99%的情况下,您不能有效地在寄存器中存储多个东西[没有惩罚,即使例如x86允许alah作为由64位rax形成的两个8位寄存器, 它是一个寄存器,处理器将不得不处理"部分寄存器更新",这对处理器来说很复杂,并且在x86处理器的许多变体上会减慢代码速度,编译器不太可能确实使用寄存器的其他部分进行任何有用的操作]

在比较方面(如while(i < 4)的情况),最好的选择是当被比较的值具有相同的类型时。整数文字4的类型为 int ,因此i应属于相同(或更大)的类型以避免溢出。同样重要的是,值应具有相同的符号。如果由于某种原因它们具有不同的签名性,则应手动将它们强制转换为适当的类型,但请确保强制转换是安全的。一个典型的例子:

size_t max = 4096;
ssize_t n = read(fd, buf, max);
if(n < 0) {
    handle_error(errno);
    return;
}
assert(n >= 0); // Now we are sure we can cast away signedness safely
if((size_t)n < max) {
    // ...
}

固定大小的整数类型(如uint8_tuint32_t等)只应在大小对某个问题至关重要时才使用。例如,将整数序列化为可在另一台计算机上读取的文件时。

在选择基元类型时,我通常不会关心性能,因为健壮性对我来说更重要。