【C++】常见易犯错误之数值类型取值溢出与截断(1)
1. 数据类型数值范围溢出
如标题所述,该错误出现的原因是由于变量的值超出该数据类型取值范围而导致的错误。
例题如下:
(IDE环境:C-Free,编译器为mingw5,如下图)

# include <iostream>
int main(){
short int a = ; // short int 取值范围:-32768~32768
short b = ; // short 是short int 的缩写
int c = a + b; // int 取值范围:-2147483648 ~2147483648
short d = a + b;
std::cout << c << ' ' << d << std::endl;
return ;
}
运行结果:

分析原因:a在内存中存储如下,其中第一位是符号位,0表示为正数,1表示负数。
| 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
那么 a + b 在内存如下:
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
对于 “int c = a + b;” 由于c的取值范围为 -2147483648 ~2147483648,共32位,其中31位数据位,1位符号位,a + b 在c的取值范围内,其符号位依然是0,所以c的取值正常,为32768。
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
反观“short d = a + b;”其取值范围是 -32768~32768,共16位,其中15位数据位,1位符号位,d在内存中内容如下,其符号位为1,表示为负数,又因为数据在内存是以补码存储的,正数的原码反码补码相同,复数的补码是其反码加1,复数的补码的补码就是复数的原码。所以该内存存储形式的反码为 1111 1111 1111 1111,原码 1000 0000 0000 0000,换成10进制-32768(而不是-0).
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
为了验证上述说明的正确性,在VS2015中重新运行,并打印结果的十六进制形式。
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <iostream>
#include <cstddef> int main() {
short int a = ;
short b = ;
int c = a + b;
short d = a + b;
printf("%d: Dec: %d; Hex: %x; size: %d;\n",c, c, c, sizeof(c));
printf("%d: Dec: %d; Hex: %x; size: %d\n",d, d, d, sizeof(d));
printf("%d: Dec: %d; Hex: %x; size: %d\n", d+, d + , d + , sizeof(d + )); system("pause");
return ;
}
结果如下:

对于c的结果,上述分析是正确的;而对于分析d的结果,似乎存在偏差。上述分析中对于d只有16位,而真正表示成十六进制时,却用到了32位,多分配了一倍的位数用来表示(但因为是short型,实际仍然只占2个字节),形成了向最高位借1(最低位是第0位).而为什么会形成这种问题,而不是上述分析那样,暂时不明白(手动标红,待解决)。(已解决,详情查看“【C++】常见易犯错误之数值类型取值溢出与截断(2)”)为了验证编译器为解决这种取值范围溢出,自动多分配1倍内存来表示,做下面实验。
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <iostream>
#include <iostream>
#include <cstddef> int main() {
system("color 3f");
short int a = ;
short b = ;
int c = a + b;
short d = a + b;
printf("%d: Dec: %d; Hex: %x; size: %d;\n",c, c, c, sizeof(c));
printf("%d: Dec: %d; Hex: %x; size: %d\n",d, d, d, sizeof(d));
printf("%d: Dec: %d; Hex: %x; size: %d\n", d+, d + , d + , sizeof(d + )); std::cout << "\n" << std::endl;
char f = ;
char g = ;
char h = f + g;
printf("%d: Dec: %d; Hex: %x; size: %d\n\n", h, h, h, sizeof(h));
system("pause");
return ;
}
结果:

char型取值范围-128~127,编译器为了解决这个问题,并非多分配一倍,在这里多分配了三倍内存表示,即内存中同样共用了32位来表示,但实际只占有1个字节。这是为什么呢,待解决,标红!
注:网上有这样一种负数的补码计算方式:模 - 该数的绝对值【1】
例:-1补码:1 0000 0000 - 0000 0001 = 1111 1111;
-3补码:1 0000 0000 - 0000 0011 = 1111 1101;
2. 数据类型数值范围截断
发生截断的例子参考这里第(6)
参考文献
【1】-128在内存中如何存储
https://blog.csdn.net/magiclyj/article/details/75258195
【C++】常见易犯错误之数值类型取值溢出与截断(1)的更多相关文章
- 【C++】常见易犯错误之数值类型取值溢出与截断(3)
0. 前言 本节是“[C++]常见易犯错误之数值类型取值溢出与截断(1)” 的补充,主要探讨浮点型的取值溢出. 1. 相关知识 (1) 浮点型数据取值范围如下: 单精度型 float 3.4 * 1 ...
- 【C++】常见易犯错误之数值类型取值溢出与截断(2)
本节内容紧接上节,解决红色字体遗留问题.本节所有例子运行环境: win10 + VS2015 + X64 + debug 在上节例子中,查看变量 c .d .d+1 的类型. //// Console ...
- 编程中易犯错误汇总:一个综合案例.md
# 11编程中易犯错误汇总:一个综合案例 在上一篇文章中,我们学习了如何区分好的代码与坏的代码,如何写好代码.所谓光说不练假把式,在这篇文章中,我们就做一件事——一起来写代码.首先,我会先列出问题,然 ...
- [golang 易犯错误] golang 局部变量初始化:=的陷阱
我们知道,golang中局部变量初始化方法(使用“:=”创建并赋值),让我们在使用变量时很方便.但是,这也是易犯错误的地方之一.特别是这个初始化符还支持多个变量同时初始化,更特别的是它还支持原有变量赋 ...
- java代码书写易犯错误
java代码书写易犯错误: 常见报错: 控制台报错: 找不到或无法加载主类 HelloWorld 原因: java.lang.NoClassDefFoundError: cn/itcast/day01 ...
- 带符号的char类型取值范围为什么是-128——127
以前经常看到带符号的char类型取值范围是-128——127,今天突然想为什么不是-127——127,-128是怎么来的? 127好理解,char类型是8位,最高位是符号位,0正1负,所以011111 ...
- byte类型取值范围以及溢出具体解释
例1: public class test { public static void main(String[] args) { byte a = 127 ; a = (byte)(a+3) ; Sy ...
- signed char类型取值范围计算
在C语言程序中,给定一个类型,如何计算这个类型变量的取值范围呢?比如有一个字符型变量定义如下: signed char c: 这个字符变量c的取值范围是[-128,127],是计算出来的呢? 假设字符 ...
- Java开发者易犯错误Top10
本文总结了Java开发者经常会犯的前十种错误列表. Top1. 数组转换为数组列表 将数组转换为数组列表,开发者经常会这样做: List<String> list = Arrays.asL ...
随机推荐
- 递归与N皇后问题
递归的基本概念 一个函数调用其自身,就是递归 递归的作用 1) 替代多重循环 2) 解决本来就是用递归形式定义的问题 3) 将问题分解为规模更小的子问题进行求解 一行只能有一个皇后,这个根据游戏规则中 ...
- unittest(生成测试报告)
1.先导入HTMLTestRunner模块 见上篇HTMLTestRunner模块生成文档 2.实例如下 (1)单用例文件执行且生成报告 import unittest import HTMLTest ...
- Spring 注解注入—@Qualifier 注释
当创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,在这种情况下,你可以使用 @Qualifier 注释和 @Autowired 注释通过指定哪一个真正的 bean ...
- Android EXCEL 解析 xls 和 xlsx,方法其实很简单
前言 Excel 解析,一般来说是在服务端进行的,但是如果移动端要实现解析Excel的功能,那也是有实现的方法的. 不过由于Android 原生用Java/Kotlin实现,所以也可以参考服务端解析E ...
- Java——Java泛型
该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架. 一.泛型概述 1.定 ...
- Java——SSM整合所需的Maven配置文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...
- 利用 Maven 构造 Spring Cloud 微服务架构 模块使用 spring Boot构建
采用Maven 聚合工程搭建 Spring Cloud 使用工具: IntelliJ IDEA 版本: 2019.2.2 maven 版本: 3.6.0 JDK ...
- python学习第七天--文件系统常用模块os,os.path,pickle
模块是一个可用代码段的打包,后缀名为py,可被别的程序引入#使用import OS模块:operting system操作系统#import os os.chdir(path) 改变当前工作目录 os ...
- [hdu4911]逆序对相关
思路:由于只能交换相邻的数,所以每次最多减小1个逆序对(且如果存在逆序对那么肯定可以减小1个)!于是乎..就是统计逆序对的裸题了.树状数组或归并都行. #pragma comment(linker, ...
- [hdu3631]背包或中途相遇法
暴力的背包: #pragma comment(linker, "/STACK:10240000,10240000") #include <iostream> #incl ...