为什么用短不好

Why is it bad to use short

本文关键字:为什么      更新时间:2023-10-16

即使在开发人员保证变量永远不会超过一个字节,有时超过两个字节的脚本中,这也是很常见的; 许多人决定对用于表示 0-1 范围内的数字 nay 的每个可能的变量使用int类型。

为什么使用charshort会这么痛?

我想我听到有人说int是"更标准"的类型......类型。 这是什么意思。我的问题是,数据类型int是否比short(或其他较小的数据类型)有任何定义的优势,因为哪些优点,人们过去几乎总是求助于int

作为一般规则,C 中的大多数算术都是使用int类型(即普通int,而不是shortlong)执行的。 这是因为(a)C的定义是这样说的,这与(b)这是许多处理器(至少是C的设计师所想到的处理器)更喜欢工作的方式有关。

因此,如果您尝试通过使用shortints 来"节省空间",并且您编写类似

short a = 1, b = 2;
short c = a + b;

编译器可能必须发出代码才能有效地将ashort转换为int,将bshort转换为int,执行加法,并将总和转换回short。 你可能已经为abc节省了一点存储空间,但你的代码最终可能会变得更大(更慢)。

如果你改为写

int a = 1, b = 2;
int c = a + b;

您可能会在abc上花费更多的存储空间,但代码可能会更小、更快。

这有点过于简化的论点,但它支持您的观察,即很少使用类型short,通常建议使用普通int。 基本上,由于它是机器的"自然"大小,因此它被认为是最直接的算术类型,而无需与不太自然的类型进行额外的转换。 这有点像"在罗马时,像罗马人那样做"的论点,但它通常确实使使用简单int有利。

另一方面,如果你有很多不太大的整数要存储(它们的大数组,或者包含不太大的整数的大量结构数组),数据的存储节省可能很大,值得与代码大小的(相对较小的)增加和潜在的速度提高进行权衡。

另请参阅前面的 SO 问题和此 C 常见问题列表条目。


附录:像任何优化问题一样,如果你真的关心数据空间使用、代码空间使用和代码速度,你会希望使用你的机器和处理器进行仔细的测量。 毕竟,您的处理器可能最终不需要任何"额外的转换指令"来转换为较小的类型,因此使用它们可能不是那么大的缺点。 但与此同时,您可能可以确认,对于孤立变量,使用它们也可能不会产生任何可衡量的优势。

<小时 />

增编2. 这是一个数据点。 我尝试了代码

extern short a, b, c;
void f()
{
c = a + b;
}

我用两个编译器编译,gcc和clang(为Mac上的英特尔处理器编译)。 然后我short更改为int并再次编译。int- using 代码在 gcc 下小 7 个字节,在 clang 下小 10 个字节。 对汇编语言输出的检查表明,区别在于截断结果以将其存储在c中;获取short而不是int似乎不会改变指令计数。

但是,我随后尝试调用两个不同的版本,发现即使在 10000000000 次调用之后,它在运行时几乎没有任何差异。 因此,答案的"使用short可能会使代码变大"部分得到了确认,但可能不是"并且还使其变慢"。

这里有几个问题。

  • 首先,char类型完全不适合保存整数值。它应该仅用于保存字符。这是因为它具有实现定义的符号,char实际上是独立于signed charunsigned char的不同类型。请参阅默认情况下字符是有符号还是无符号?。

  • 但是,应尽可能避免使用小整数类型(如charshort)的主要原因是隐式类型提升。这些类型会受到整数提升的影响,这反过来又可能导致危险的事情,例如符号的静默更改。有关详细信息,请参阅隐式类型升级规则。

    出于这个原因,一些编码标准实际上完全禁止使用较小的整数类型。尽管要使此类规则可行,但您需要 32 位或更大的 CPU。因此,如果要考虑各种微控制器,它实际上不是一个好的通用解决方案。

    另请注意,以这种方式进行微观管理内存主要与嵌入式系统编程相关。如果您正在编写PC程序,则使用较小的类型来节省内存可能是一种"过早的优化"。

  • C的默认"原始数据类型",包括charshortint,总体上是不可移植的。移植代码时,它们的大小可能会发生变化,这反过来又会给它们带来不确定的行为。此外,C 允许为这些类型提供各种晦涩和奇特的符号格式,例如一个人的补码、符号和大小、填充位等。

    坚固、可移植、高质量的代码根本不使用这些类型,而是使用stdint.h的类型。作为奖励,该库只允许理智的行业标准二的补充。

  • 由于上述所有原因,使用较小的整数类型来节省空间不是一个好主意。同样,stdint.h更可取。如果您需要可移植地节省内存的通用类型,除非节省内存意味着降低执行速度,否则请使用int_fast8_t和类似功能。这些将是 8 位,除非使用更大的类型意味着更快的执行。

我对基于短的代码应该以任何重要方式更慢、更大的说法持怀疑态度(假设这里的局部变量,没有关于大型数组的争议,如果合适,short肯定会得到回报),所以我试图在我的Intel(R) Core(TM) i5 CPU M 430 @ 2.27GHz

我用了(长.c):

long long_f(long A, long B)
{
//made up func w/ a couple of integer ops 
//to offset func-call overhead
long r=0;
for(long i=0;i<10;i++){
A=3*A*A;
B=4*B*B*B;
r=A+B;
}
return r;
}

在基于longintshort的版本(%s/long/TYPE/g)中,用gccclang-O3-Os构建了程序,并测量了每个函数的100mil调用的大小和运行时。

F.H:

#pragma once
int int_f(int A, int B);
short short_f(short A, short B);
long long_f(long A, long B);

主.c:

#include "f.h"
#include <stdlib.h>
#include <stdio.h>
#define CNT 100000000
int main(int C, char **V)
{
int choose = atoi(V[1]?:"0");
switch(choose){
case 0:
puts("short");
for(int i=0; i<CNT;i++)
short_f(1,2);
break;
case 1:
puts("int");
for(int i=0; i<CNT;i++)
int_f(1,2);
break;
default:
puts("long");
for(int i=0; i<CNT;i++)
long_f(1,2);
}
}

建:

#!/bin/sh -eu
time(){ command time -o /dev/stdout "$@"; }
for cc in gcc clang; do
$cc -Os short.c -c
$cc -Os int.c -c
$cc -Os long.c -c
size short.o int.o long.o
$cc main.c short.o int.o long.o
echo $cc -Os
time ./a.out 2
time ./a.out 1
time ./a.out 0
$cc -O3 short.c -c
$cc -O3 int.c -c
$cc -O3 long.c -c
size short.o int.o long.o
$cc main.c short.o int.o long.o
echo $cc -O3
time ./a.out 2
time ./a.out 1
time ./a.out 0
done

我做了两次,结果似乎很稳定。

text    data     bss     dec     hex filename
79       0       0      79      4f short.o
80       0       0      80      50 int.o
87       0       0      87      57 long.o
gcc -Os
long
3.85user 0.00system 0:03.85elapsed 99%CPU (0avgtext+0avgdata 1272maxresident)k
0inputs+0outputs (0major+73minor)pagefaults 0swaps
int
4.78user 0.00system 0:04.78elapsed 99%CPU (0avgtext+0avgdata 1220maxresident)k
0inputs+0outputs (0major+74minor)pagefaults 0swaps
short
3.36user 0.00system 0:03.36elapsed 99%CPU (0avgtext+0avgdata 1328maxresident)k
0inputs+0outputs (0major+74minor)pagefaults 0swaps
text    data     bss     dec     hex filename
137       0       0     137      89 short.o
109       0       0     109      6d int.o
292       0       0     292     124 long.o
gcc -O3
long
3.90user 0.00system 0:03.90elapsed 99%CPU (0avgtext+0avgdata 1220maxresident)k
0inputs+0outputs (0major+74minor)pagefaults 0swaps
int
1.22user 0.00system 0:01.22elapsed 99%CPU (0avgtext+0avgdata 1260maxresident)k
0inputs+0outputs (0major+73minor)pagefaults 0swaps
short
1.62user 0.00system 0:01.62elapsed 99%CPU (0avgtext+0avgdata 1228maxresident)k
0inputs+0outputs (0major+73minor)pagefaults 0swaps
text    data     bss     dec     hex filename
83       0       0      83      53 short.o
79       0       0      79      4f int.o
88       0       0      88      58 long.o
clang -Os
long
3.33user 0.00system 0:03.33elapsed 99%CPU (0avgtext+0avgdata 1316maxresident)k
0inputs+0outputs (0major+71minor)pagefaults 0swaps
int
3.02user 0.00system 0:03.03elapsed 99%CPU (0avgtext+0avgdata 1316maxresident)k
0inputs+0outputs (0major+71minor)pagefaults 0swaps
short
5.27user 0.00system 0:05.28elapsed 99%CPU (0avgtext+0avgdata 1236maxresident)k
0inputs+0outputs (0major+69minor)pagefaults 0swaps
text    data     bss     dec     hex filename
110       0       0     110      6e short.o
219       0       0     219      db int.o
279       0       0     279     117 long.o
clang -O3
long
3.57user 0.00system 0:03.57elapsed 99%CPU (0avgtext+0avgdata 1228maxresident)k
0inputs+0outputs (0major+69minor)pagefaults 0swaps
int
2.86user 0.00system 0:02.87elapsed 99%CPU (0avgtext+0avgdata 1228maxresident)k
0inputs+0outputs (0major+68minor)pagefaults 0swaps
short
1.38user 0.00system 0:01.38elapsed 99%CPU (0avgtext+0avgdata 1204maxresident)k
0inputs+0outputs (0major+70minor)pagefaults 0swaps

结果相当接近,但随着编译器和编译器设置的不同,它们的差异相对较大。

我的结论是,在函数体或签名(数组是一个不同的问题)中在intshorts 之间进行选择,因为一个应该比另一个表现得更好或生成更密集的代码大多是徒劳的(至少在未固定到具有特定设置的特定编译器的代码中)。两者都很快,所以我会选择更适合我的程序语义或更好地传达我的 API 的类型 (如果我期望一个简短的正值,不妨在签名中使用 uchar 或 ushort。

C程序员倾向于使用ints,因为 C 在历史上一直偏爱它们(整数文字往往ints,提升倾向于ints,曾经有声明和未声明函数的隐式 int 规则等),并且int应该非常适合架构, 但归根结底,具有可读、可维护源代码的密集、高性能机器代码才是最重要的,如果你在源代码中做某事的理论没有明显有助于实现这些目标中的至少一个,我认为这是一个糟糕的理论。