从C语言的整数取值范围说开去
在ILP32中, char, short, int, long, long long, pointer分别占1, 2, 4, 4, 8, 4个字节,
在 LP64中, char, short, int, long, long long, pointer分别占1, 2, 4, 8, 8, 8个字节,
无论是在ILP32中还是LP64中, long long总是占8个字节,下面给出简单的C代码实现表征出整数的取值范围先。
o foo.c
#include <stdio.h>
/**
* The size (n bytes) of basic types
* =================================
* char short int long long long pointer
* ----- ---- ----- --- ---- --------- -------
* LP64 1 2 4 8 8 8
* ILP32 1 2 4 4 8 4
*/
typedef char __s8;
typedef short __s16;
typedef int __s32;
typedef long long __s64;
typedef unsigned char __u8;
typedef unsigned short __u16;
typedef unsigned int __u32;
typedef unsigned long long __u64; #define SMAX8 ((__s8 )(((__u8 )~0) >> 1))
#define SMAX16 ((__s16)(((__u16)~0) >> 1))
#define SMAX32 ((__s32)(((__u32)~0) >> 1))
#define SMAX64 ((__s64)(((__u64)~0) >> 1)) #define SMIN8 -SMAX8
#define SMIN16 -SMAX16
#define SMIN32 -SMAX32
#define SMIN64 -SMAX64 #define UMAX8 ((__u8 )~0)
#define UMAX16 ((__u16)~0)
#define UMAX32 ((__u32)~0)
#define UMAX64 ((__u64)~0) #define UMIN8 ((__u8 )0)
#define UMIN16 ((__u16)0)
#define UMIN32 ((__u32)0)
#define UMIN64 ((__u64)0) int main(int argc, char *argv[])
{
__s8 smax8 = SMAX8;
__s16 smax16 = SMAX16;
__s32 smax32 = SMAX32;
__s64 smax64 = SMAX64;
__s8 smin8 = SMIN8;
__s16 smin16 = SMIN16;
__s32 smin32 = SMIN32;
__s64 smin64 = SMIN64;
printf("s64: [%llx, %llx]\t[%lld, %lld]\n", smin64, smax64, smin64, smax64);
printf("s32: [%x, %x]\t\t\t[%d, %d]\n", smin32, smax32, smin32, smax32);
printf("s16: [%x, %x]\t\t\t\t[%d, %d]\n", smin16, smax16, smin16, smax16);
printf("s8 : [%x, %x]\t\t\t\t[%d, %d]\n", smin8, smax8, smin8, smax8);
printf("\n"); __u8 umax8 = UMAX8;
__u16 umax16 = UMAX16;
__u32 umax32 = UMAX32;
__u64 umax64 = UMAX64;
__u8 umin8 = UMIN8;
__u16 umin16 = UMIN16;
__u32 umin32 = UMIN32;
__u64 umin64 = UMIN64;
printf("u64: [%llx, %llx]\t\t\t[%lld, %llu]\n", umin64, umax64, umin64, umax64);
printf("u32: [%x, %x]\t\t\t\t[%d, %u]\n", umin32, umax32, umin32, umax32);
printf("u16: [%x, %x]\t\t\t\t\t[%d, %u]\n", umin16, umax16, umin16, umax16);
printf("u8 : [%x, %x]\t\t\t\t\t[%d, %u]\n", umin8, umax8, umin8, umax8); return ;
}
o 编译并执行
$ gcc -g -Wall -m32 -o foo foo.c
$ ./foo
s64: [, 7fffffffffffffff] [-, ]
s32: [, 7fffffff] [-, ]
s16: [ffff8001, 7fff] [-, ]
s8 : [ffffff81, 7f] [-, ] u64: [, ffffffffffffffff] [, ]
u32: [, ffffffff] [, ]
u16: [, ffff] [, ]
u8 : [, ff] [, ]
注意: 二进制数在计算机中一律以补码表示。 这里简单说说二进制编码中的原码,反码以及补码(注:移码这里不谈)以帮助理解上面的输出。
1. 原码的编码规则
1.1 原码即"原始编码", 最高位为符号位,0表示整数,1表示负数;
1.2 +0和-0的原码表示是不同的。在16位机器上,
+0 = 0000 0000 0000 0000b
-0 = 1000 0000 0000 0000b
2. 反码的编码规则
2.1 正数的反码等于其原码;
2.2 负数的反码是符号位不变,除符号外之外的其他位按位取反;
2.3 +0和-0的反码表示也是不同的。在16位机器上,
+0 = 0111 1111 1111 1111b
-0 = 1111 1111 1111 1111b
3. 补码的编码规则
3.1 正数的补码等于原码;
3.2 负数的补码是符号位不变,除符号外之外的其他位按位取反,再给最低位加1;
3.3 +0和-0的补码是唯一的,都是0。在16位机器上,
+0 = 0000 0000 0000 0000b ;= +0(反)
-0 = 0000 0000 0000 0000b ;= -0(反)+1
4. 为什么要引入补码?
4.1 无论是原码,还是反码,都无法解决0的的二义性问题。补码的引入,解决了这一问题,也就是0的表示是唯一的;
4.2 让符号位参与运算。因此,所有减法都可以用加法器实现。
o 因为编译选项是-m32, 所以:
-127 的补码表示是 0xffffff81 = (1111 1111 1111 1111 1111 1111 1000 0001b)
-32767 的补码表示是 0xffff8001 = (1111 1111 1111 1111 1000 0000 0000 0001b)
-2147483647 的补码表示是 0x80000001 = (1000 0000 0000 0000 0000 0000 0000 0001b)
-9223372036854775807 的补码表示是0x8000000000000001 = (1000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0001b)
以-127为例,在32位机器上,其原码、反码和补码可表示为:
o 000 0000 0000 0000 0000 0000 0111 1111b ; 原码
o 111 1111 1111 1111 1111 1111 1000 0000b ; 反码: 在原码的基础上, 符号位不变, 剩下31位按位取反
o 111 1111 1111 1111 1111 1111 1000 000b ; 补码: 在反码的基础上, 给最低位加1
小结: 将常见的整数的取值范围牢记于心,有利于在实际的程序设计中根据需求快速地确定变量(或结构体成员)的基本数据类型,写出优质无错的代码。
- 对于占N个二进制位的有符号整数, 能表示的范围是[- (2^N-1 - 1), +((2^N-1 - 1)], N=8, 16, 32, 64, ... (因为符号位占了一位,所以是N-1)
- 对于占N个二进制位的无符号整数, 能表示的范围是[0, +((2^N - 1)], N=8, 16, 32, 64, ...
另外,在做算法设计的时候,将下面的表格(2的N次方)烂熟于心也有利于快速做出判断。 例如,一个将每个32位无符号整数映射为布尔值的hash表可以将一台计算机的内存填满。
| 2的N次方 | 准确值 | 近似值 | K/M/G/T...表示 |
| 7 | 128 | ||
| 8 | 256 | ||
| 千(Thousand) | 1K | ||
| 65, 536 | 64K | ||
| 1, 048, 576 | 百万(Million) | 1M | |
| 1, 073, 741, 824 | 十亿(Billion) | 1G | |
| 4, 294, 967, 296 | 4G | ||
| 1, 099, 511, 627, 776 | 万亿(Trillion) | 1T |
1K : 2^10 : 千
1M : 2^20 : 百万 (Million) ; 千千
1G : 2^30 : 十亿 (Billion) ; 千百万
1T : 2^40 : 万亿 (Trillion); 千十亿
1E : 2^50
1Z : 2^60
256: 2^8
64K: 2^16
4G : 2^32
4Z : 2^64
从C语言的整数取值范围说开去的更多相关文章
- C语言:整数取值范转及溢出
short.int.long 是C语言中常用的三种整数类型,分别称为短整型.整型.长整型.在现代操作系统中,short.int.long 的长度分别是 2.4.4 或者 8,它们只能存储有限的数值,当 ...
- 建议 for 语句的循环控制变量的取值采用“半开半闭区间”写法
建议 for 语句的循环控制变量的取值采用“半开半闭区间”写法. #include <iostream> /* run this program using the console pau ...
- C语言中数据类型取值范围的计算的理解与总结
c语言中,数据类型有short,int,long,char,float,double,然后除了浮点型只有 有符号数(signed)外,其他的数据类型都分为有符号(signed)和无符号(unsigne ...
- 编写一个js函数,该函数有一个n(数字类型),其返回值是一个数组,该数组内是n个随机且不重复的整数,且整数取值范围是[2,32]
首先定义个fn用来返回整数的取值范围: function getRand(a,b){ var rand = Math.ceil(Math.random()*(b-a)+a); return rand; ...
- C语言各种数据类型取值范围
速查表: char -128 ~ +127 1Byte -2^7 ~ 2^7-1 unsigned char 0 ~ 255 1Byte 0 ~ 2^8-1 short -32767 ~ + 3276 ...
- 编写一个javscript函数 fn,该函数有一个参数 n(数字类型),其返回值是一个数组,该数组内是 n 个随机且不重复的整数,且整数取值范围是 [2, 32]。
function fn(n){ if(n<2 || n>32) { return; } if(!n) { return;} //判断n是否为数字 if(!/^[0-9]+.?[0-9 ...
- 带符号的char类型取值范围为什么是-128——127
以前经常看到带符号的char类型取值范围是-128——127,今天突然想为什么不是-127——127,-128是怎么来的? 127好理解,char类型是8位,最高位是符号位,0正1负,所以011111 ...
- C语言数据类型取值范围
一.获取数据类型在系统中的位数 在不同的系统中,数据类型的字节数(bytes)不同,位数(bits)也有所不同,那么对应的取值范围也就有了很大的不同,那我们怎么知道你当前的系统中C语言的某个数据类型的 ...
- GO语言学习笔记2-int类型的取值范围
相比于C/C++语言的int类型,GO语言提供了多种int类型可供选择,有int8.int16.int32.int64.int.uint8.uint16.uint32.uint64.uint. 1.i ...
随机推荐
- pagecontrol
PageControl组件位于组件板的Win32页中,该组件用于 实现窗体上多页面技术,每个页面上均能添加若干控件.程序运行时,单击页面标签就可以在多页之间切换.1.建立多页 用鼠标右键单击PageC ...
- [js]利用闭包向post回调函数传参数
最近在闲逛校园XX站的时候,打算搞个破坏,试试有多少人还是用初始密码登陆.比较懒,所以直接打开控制台来写. 所以问题可以描述为: 向后端不断的post数据,id从1~5000自增,后端会根据情况来返回 ...
- 创建 ASP.NET Web API的Help Page
转:创建WEBAPI项目 转:添加测试API中的ASP.NET Web API帮助页面
- 各类型转换成byte[] 和HexString
public class ByteUtil { /// <summary> /// string >>Length /// &l ...
- ML.NET 0.9 版本发布---.net下的机器学习引擎
欢迎来到 2019年!在过去的9个月里, 我们一直在为ML.NET添加新的特征和改进相关功能.在提交1.0版本之前,我们将专注于包的整体稳定性并对API进行不断优化, 扩大测试的覆盖面并对开发文档进行 ...
- SDOI2013直径(树的直径)
题目描述: 点这里 题目大意: 就是在一个树上找其直径的长度是多少,以及有多少条边满足所有的直径都经过该边. 题解: 首先,第一问很好求,两边dfs就行了,第一次从任一点找距它最远的点,再从这个点找距 ...
- AHOI2005航线规划 bzoj1969(LCT缩点)
题目描述 对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系——一个巨大的由千百万星球构成的Samuel星系. 星际空间站的Samuel II巨型计算 ...
- linux密码修改实验
1.在单用户模式下进行引导 在不同的运行级别中,一个重要的运行级别就是单用户模式(运行级别1),该模式中,只有一个系统管理员使用特定的机器,而且尽可能少地运行系统服务,其中包含登录.单用户模式对少数管 ...
- 性能测试—认识JMeter(一)
性能测试—认识JMeter(一) <零成本web性能测试>第二章 JMeter基础知识总结和自己的理解 一.JMeter百度词条概念 Apache JMeter是Apache组织开发的基 ...
- java里面的标识符、关键字和类型
1. 注释 Java中有三种注释: (1) // -单行注释,注释从“//”开始,终止于行尾: (2) -多行注释,注释从““结束: (3) -是Java特有的doc注释,这种注释主 ...