即使在脚本中,开发人员也保证变量永远不会超过一个字节,有时甚至是两个字节,这是很常见的。
许多人决定使用int
类型用于表示0-1范围内的数字的每个可能变量。
为什么使用char
或short
代替它会造成太大的伤害?
我想我听到有人说int
是“更标准”类型的..类型。
这是什么意思。我的问题是数据类型int
是否比short
(或其他较小的数据类型)具有任何明确的优势,因为人们过去几乎总是诉诸int
具有哪些优势?
答案 0 :(得分:16)
作为一般规则,C中的大多数算术都是使用类型int
(即普通int
,而不是short
或long
)执行的。这是因为(a)C的定义是这样说的,这与(b)许多处理器(至少是C的设计者想到的)更喜欢工作的方式有关。
因此,如果您尝试使用short
ints来“节省空间”,那么您可以编写类似
short a = 1, b = 2;
short c = a + b;
编译器必须发出代码以将a
从short
转换为int
,将b
从short
转换为int
,执行另外,并将总和转换回short
。您可能在a
,b
和c
的存储空间中节省了一些空间,但您的代码可能更大(更慢)。
如果你改为写
int a = 1, b = 2;
int c = a + b;
您在a
,b
和c
中花费了更多的存储空间,但代码可能更小更快。
这有点过于简单了,但是你的观察结果是,short
类型的使用很少,通常建议使用普通int
。基本上,因为它是机器的“自然”大小,所以它被认为是最简单的算术类型,没有额外的转换和来自不太自然的类型。它有点像“在罗马时,像罗马人那样做”的论点,但它通常 使用普通int
有利。
如果你有 lot 不大的整数来存储,另一方面(大量的,或包含不那么大整数的大量结构),数据的存储节省可能很大,值得与代码大小(相对较小的)增加以及潜在的速度增加进行交易。
另请参阅this previous SO question和this C FAQ list entry。
附录:与任何优化问题一样,如果您真的关心数据空间使用,代码空间使用和代码速度,您将需要使用您的确切机器和处理器进行仔细测量。毕竟,您的处理器可能最终不需要任何“额外的转换指令”来转换为较小的类型,因此使用它们可能不会有太大的劣势。但与此同时,您可能会确认,对于孤立变量,使用它们可能也不会产生任何可衡量的优势。
附录2.这是一个数据点。我试验了代码
extern short a, b, c;
void f()
{
c = a + b;
}
我编译了两个编译器,gcc和clang(在Mac上编译Intel处理器)。然后我将short
更改为int
并再次编译。 int
- 使用代码在gcc下缩小了7个字节,在clang下缩小了10个字节。检查汇编语言输出表明差异在于截断结果以便将其存储在c
中;提取short
而不是int
似乎不会改变指令数。
然而,我接着尝试调用两个不同的版本,并发现它在运行时间上几乎没有差别,即使在10000000000次调用之后也是如此。所以“使用short
可能会使代码更大”答案的一部分得到确认,但也许不会“并且也会让它变慢”。
答案 1 :(得分:3)
我对基于短码的代码在任何重要方面应该更慢和更大的说法持怀疑态度(假设这里的局部变量,如果合适的话short
肯定能够获得回报的大型数组没有争议),所以我试图在Intel(R) Core(TM) i5 CPU M 430 @ 2.27GHz
我用过(long.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;
}
在基于long
,int
和short
的版本(%s/long/TYPE/g
)中,使用gcc
和clang
构建了该计划-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);
main.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
结果非常接近,但它们在不同的编译器和编译器设置下相对变化很大。
我的结论是在函数体或签名中选择int
和short
之间(数组是一个不同的问题)因为一个应该比另一个表现更好或生成更密集的代码几乎是徒劳的(至少在没有固定到具有特定设置的特定编译器的代码中)。要么是快速的,所以我选择哪种类型更适合我的程序的语义或更好地传达我的API
(如果我期望一个短的正值,也可以在签名中使用uchar或ushort。)
C程序员倾向于使用int
,因为C在历史上一直偏爱它们(整数文字倾向于int
s,促销倾向于int
s,过去常有隐含声明和未声明函数的int规则等)和int
应该是非常适合体系结构的,但最终,具有可读,可维护源的密集,高性能的机器代码是什么重要的是,如果你在源代码中做某事的理论对这些目标中的至少一个没有明显的贡献,我认为这是一个糟糕的理论。
答案 2 :(得分:2)
这里有几个问题。
首先,char
类型完全不适合保存整数值。它只应用于保存字符。这是因为它具有实现定义的签名,char
实际上是与signed char
和unsigned char
分开的不同类型。请参阅Is char signed or unsigned by default?。
如果可能,应避免使用char
和short
等小整数类型的主要原因是隐式类型提升。这些类型受整数提升的影响,这反过来可能导致危险的事情,如无声的签名改变。有关详细信息,请参阅Implicit type promotion rules。
出于这个原因,一些编码标准实际上完全禁止使用较小的整数类型。虽然这样的规则是可行的,但您需要32位或更高的CPU。因此,如果要考虑各种微控制器,它并不是一个真正的通用解决方案。
另请注意,以这种方式管理微内存大多只与嵌入式系统编程有关。如果您正在编程PC程序,使用较小的类型来节省内存可能是一种预先成熟的优化&#34;。
默认的&#34;原始数据类型&#34;包括char
,short
,int
在内的C的整体非常不便携。它们可能会在代码移植时改变大小,从而使它们具有不确定的行为。此外,C允许这些类型的各种模糊和异国情调的签名格式,例如一个补码,符号和&amp;幅度,填充位等。
坚固耐用,质量可靠的代码根本不使用这些类型,而是使用stdint.h
的类型。作为奖励,该图书馆只允许合理的行业标准的补充。
出于上述所有原因,使用较小的整数类型来节省空间并不是一个好主意。同样,stdint.h
更可取。如果您需要一个可以节省内存的通用类型,除非保存内存意味着降低执行速度,请使用int_fast8_t
等。这些将是8位,除非使用更大的类型意味着更快的执行。