从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 ...
随机推荐
- Android-ContentProvider理解操作系统的多媒体
在多年以前,我做一款音乐播放器,要把很多.mp3文件,放置在自己新建的文件夹里面,然后去读取这个新建的文件夹里面的音乐列表,现在想想是多么的无知: 因为只要往手机里面放入.mp3文件,系统会自动检测, ...
- Android-ListView-SimpleCursorAdapter
在上篇博客,Android-ListView-SimpleAdapter,中介绍了SimpleAdapter的使用操作(SimpleAdapter面向的数据是非Cursor数据),而SimpleCur ...
- Transaction And Lock--已提交读快照
--===================================================== --行版本控制已提交读ALTER DATABASE DB5 SET READ_COMMI ...
- 使用pycharm专业版创建虚拟环境
Location为工程地址 D:\My_python 第二个Location为 虚拟环境放在这个工程下 Base interpreter:基于那个解释器来创建虚拟环境 Create后进入到目录查看下
- Ocelot Consul ACL
Ocelot允许您指定服务发现提供程序,并使用它来查找Ocelot正在将请求转发给下游服务的主机和端口.目前,这仅在GlobalConfiguration部分中受支持,这意味着所有ReRoute将使用 ...
- mongodb 搭建集群(分片+副本集)
mongodb 搭建集群(分片+副本集) 一.搭建结构图: 二.搭建步骤:
- VS 2015 IDE 不支持 MS SQL 2000 生成 dbml
解决办法: 通过命令手动生成 然后把生成的ERendering.dbml 文件,通过工程项目-添加-现有项,加入项目.
- SSL证书可以给多个域名使用吗?
欢迎访问网易云社区,了解更多网易技术产品运营经验 从信任等级的角度来说,SSL证书主要分为三类: 1.域名型https证书(DVSSL):信任等级一般,只需验证网站的真实性便可颁发证书保护网站: 2. ...
- POJ - 2031C - Building a Space Station最小生成树
You are a member of the space station engineering team, and are assigned a task in the construction ...
- 斐讯K2P通过配置文件开启telnet的原理分析
看过几篇教程之后我已经知道怎么备份固件了.但是现在有一个问题,我的本意是把K2P原机带的固件备份出来,用教程上的方法进行开启telnet.备份固件等操作是否会改变固件呢?下面我们来验证这个问题. Op ...