C++带符号类型和无符号类型
//摘自 C++ Primer 第5版
各种基本数据类型地大小
char 字符类型 类型占用8位
short 短整型 16位
int 整型 16位
long 长整型 32位
long long 长整型 32位
float 浮点类型 类型占用32位 1 符号位 8 指数位 23 尾数位
double 浮点类型 类型占用64位 1 符号位 11 指数位 52 尾数位
带符号类型和无符号类型
除去布尔型和扩展的字符型之外,其他整型可以划分为带符号的( signed)和无符号的(unsigned)两种。
带符号类型可以表示正数、负数或0,无符号类型则仅能表示大于等于0的值。
类型int、 short、long 和 long long 都是带符号的,通过在这些类型名前添加unsigned就可以得到无符号类型,
例如 unsigned long。类型unsigned int可以缩写为unsigned。
与其他整型不同,字符型被分为了三种: char. signed char和 unsigned charo
特别需要注意的是:类型char和类型signed char并不一样。尽管字符型有三种,但是字符的表现形式却只有两种:带符号的和无符号的。类型char实际上会表现为上述两种形式中的一种,具体是哪种由编译器决定。
无符号类型中所有比特都用来存储值,例如,8比特的unsigned char可以表示0至255区间内的值。
C++标准并没有规定带符号类型应如何表示,但是约定了在表示范围内正值和负值的量应该平衡。因此,8比特的signed char理论上应该可以表示-127至127区间内的值,大多数现代计算机将实际的表示范围定为-128至127。
建议:如何选择类型
和C语言一样,C++的设计准则之一也是尽可能地接近硬件。C++的算术类型必须满足各种硬件特质,所以它们常常显得繁杂而令人不知所措。事实上,大多数程序员能够(也应该)对数据类型的使用做出限定从而简化选择的过程。以下是选择类型的一些经验准则:
1.当明确知晓数值不可能为负时,选用无符号类型。
2.使用int执行整数运算。在实际应用中,short常常显得太小而long一般和 int有一样的尺寸。如果你的数值超过了int的表示范围,选用long long.
3.在算术表达式中不要使用 char或bool,只有在存放字符或布尔值时才使用它们。因为类型char在一些机器上是有符号的,而在另一些机器上又是无符号的,所以如果 使用char进行运算特别容易出问题。如果你需要使用一个不大的整数,那么明确指定它的类型是signed char或者unsigned char。
4.执行浮点数运算选用double,这是因为float通常精度不够而且双精度浮点数和单精度浮点数的计算代价相差无几。事实上,对于某些机器来说,双精度运算甚至比单精度还快。long double提供的精度在一般情况下是没有必要的,况且它带来的运行时消耗也不容忽视。
类型转换
unsigned char c = -1; //假设char占8比特,c的值为255
unsigned char c2 = 256; //假设char占8比特,c的值是未定义的
当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。例如,8比特大小的unsigned char可以表示0至255 区间内的值,如果我们赋了一个区间以外的值,则实际的结果是该值对256取模后所得的余数。因此,把-1赋给8比特大小的unsigned char所得的结果是255。
当我们赋给带符号类型一个超出它表示范围的值时,结果是未定义的(undefined)。此时,程序可能继续工作、可能崩溃,也可能生成垃圾数据。
含有无符号类型的表达式
尽管我们不会故意给无符号对象赋一个负值,但实际缺很常见
unsigned u = 10;
int i = -42;
std::cout << i + i << std::endl;//输出-84
std::cout << u + i << std::endl;//如果int占32位,输出4292967264
解释:
相加前首先把整数-42转换成无符号数,把负数转换成无符号数类似于直接给无符号数赋一个负值,
结果等于这个负数加上无符号数的模。
当从无符号数中减去一个值时,不管这个值是不是无符号数,我们都必须确保结果不能是一个负值:
unsigned ul = 42,u2= 10;
std::cout << ul -u2 << std::endl;//正确:输出32
std::cout << u2 - ul << std::endl;// 正确:不过,结果是取模后的值
无符号数不小于0这个事实一定要牢记,涉及一个经典的死循环错误。
如果你想写一个循环
for (int i = 10; i >= 0; --i)
std::cout << i << std::endl;//这样写完全没有问题
可能你会觉得反正也不打算输出负数,可以用无符号数来重写这个循环。
然而,不经意的改变意味着死循环。
或者,你在循环中使用了
vector<int> nums;
nums.size()//使用了vector的这个函数,其返回值是无符号数
那么,会出现下面的场景:(关于vector的详细用法,可参考C++中的vector)
for (unsigned u = 10; u >= 0; --u)
std::cout << u << std::endl;
for (unsigned u = nums.size(); u >= 0; --u)
std::cout << u << std::endl;
来看看当u等于0时发生了什么,这次迭代输出了0,然后继续执行for语句里面的表达式,
表达式--u从u当中减去1,得到的结果-1并不满足无符号数的要求。此时,像所有表示范围之外的其他数字一样,
-1被自动地转换成了一个合法地无符号数。假设int类型占32位,则当u等于0时, --u地结果将会是4294967295。
一种解决的办法是,用while语句来替代for语句,因为前者让我们能够在输出变量之前(而非之后)先减去1:
unsigned u = 11;//确定要输出的最大数,从比它大1的数开始
while (u > 0) {
--u; //先减1,这样最后一次迭代就会输出0
std::cout << u << std::endl;
}
改写后的循环先执行对循环控制变量减1的操作,这样最后一次迭代时,进入循环的u值为1。此时将其减1,则这次迭代输出的数就是0;下一次再检验循环条件时,u的值等于0而无法再进入循环。因为我们要先做减1的操作,所以初始化u的值应该比要输出的最大值大1。这里,u初始化为11,输出的最大数是10。
提示:切勿混用带符号类型和无符号类型
如果表达式里既有带符号类型又有无符号类型,当带符号类型取值为负时会出现异常结果,这是因为带符号数会自动地转换成无符号数。例如,在一个形如 a*b的式子中,如果a =-1,b=1,而且a和b都是int,则表达式的值显然为-1。然而,如果a是int,而b是unsigned,则结果须视在当前机器上int所占位数而定。在我们的环境里,结果是4294967295。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)