本节必须掌握的知识点:
???整型数据类型的取值范围
?? 示例八
?? 代码分析
?? 汇编解析
?? 获取数据类型的取值范围
整型是用来表示限定范围内连续整数的数据类型。表3-1列出了C语言编译器定义的整型数据类型及其大小和取值范围。
表3-1整型数据类型
类型 | 存储大小 | 值范围 |
整型????????? int | 2或4字节 | 【-32768 ~32767】或 【-2147483648~2147483647】 |
短整型?????? short int | 2字节 | 【-32768 到 32767】 |
长整型?????? long int | 4字节 | 【-2147483648~2147483647】 |
无符号整型 unsigned int | 2或4字节 | 【0 ~65535】 或【 0 ~4294967295】 |
无符号短整型 unsigned short int | 2字节 | 【0 ~65535】 |
无符号长整型 unsigned long int | 4字节 | 【 0 ~4294967295】 |
【注:各种数据类型的存储大小与操作系统位数有关,如下表3-2、3-3、3-4所示,分别列出了16位、32位、64位操作系统中基本类型存储的字节大小】
【16位操作系统】
类型 | 存储大小 | 值范围 |
整型????????? int | 2字节 | 【-32768 ~32767】 |
短整型?????? short int | 2字节 | 【-32768 到 32767】 |
长整型?????? long int | 4字节 | 【-2147483648~2147483647】 |
无符号整型 unsigned int | 2 | 【0 ~65535】 |
无符号短整型 unsigned short int | 2字节 | 【0 ~65535】 |
无符号长整型 unsigned long int | 4字节 | 【 0 ~4294967295】 |
?????????? ?????????????????
表3-2 16位操作系统整型数据类型
【32位操作系统】
类型 | 存储大小 | 值范围 |
整型????????? int | 4字节 | 【-2147483648~2147483647】 |
短整型?????? short int | 2字节 | 【-32768 到 32767】 |
长整型?????? long int | 4字节 | 【-2147483648~2147483647】 |
无符号整型 unsigned int | 4字节 | 【 0 ~4294967295】 |
无符号短整型 unsigned short int | 2字节 | 【0 ~65535】 |
无符号长整型 unsigned long int | 4字节 | 【 0 ~4294967295】 |
表3-3 32位操作系统整型数据类型
【64位操作系统】
类型 | 存储大小 | 值范围 |
整型????????? int | 4字节 | 【-2147483648~2147483647】 |
短整型?????? short int | 2字节 | 【-32768 到 32767】 |
长整型?????? long int | 8字节 | 【-9223372036854775808~ 9223372036854775807】 |
long long | 8字节 | 【-9223372036854775808~ 9223372036854775807】 |
_int64 | 8字节 | 【-9223372036854775808~ 9223372036854775807】 |
无符号整型 unsigned int | 4字节 | 【 0 ~ 4294967295】 |
无符号短整型 unsigned short int | 2字节 | 【0 ~ 65535】 |
无符号长整型 unsigned long int | 8字节 | 【 0 ~ 18446744073709551615】 |
unsigned long long | 8字节 | 【0 ~ 18446744073709551615】 |
unsigned __int64 | 8字节 | 【0 ~ 18446744073709551615】 |
???? ????????????????????????????????
表3-4 64位操作系统整型数据类型
接下来以代码+解释说明的形式详细讲述怎样使用这些数据类型,怎样检测数据类型,以及怎么判断它的取值范围。
我们以32位操作系统下的整型数据类型为例。
【整型】
“整型”从字面理解是整数类型的意思,整数类型只能存储整数,不能存储小数。整型按照存储空间的大小可以分为short类型、int类型、long类型和long long类型。如果存储的数据超出该数据类型的存储空间,则会溢出,造成数据丢失。或者编译时以错误提示的形式告知。C语言编译器在编译时会对数据类型做严格的检查,帮助程序员减少错误的发生。
如果按取值范围来划分,我们又可以把整型分为有符号整型和无符号整型。在C语言中,无符号类型表示非负整数,即大于或等于0的数;有符号类型既可以是正数也可以是负数,也可以是0,但只能是整数。
实验二十三:超出数据类型存储空间
以示例七为例,控制台窗口输入一个超出范围的整数值,如下所示:
请输入一个整型:
11111111111111111111111111111111111111111111111111111111111111111111
用户输入的内容是-1。
源程序中定义控制台输入的整数值存储在int类型的变量y中。在C语言中,如果有符号整数超出范围,将以其最大值的补码形式存储。如果无符号整数溢出,将丢弃溢出的数据位。假如存储的是一个超大值,编译器将提示错误信息。
还存在另外一种情况,假如是控制台输入一个超大值,不论是有符号整型还是无符号整型,最终存储值均为-1。
仍以示例其为例,将变量y修改为无符号整型:
unsigned int y;???? //准备变量
请输入一个整型:
11111111111111111111111111111111111111111111111111111111111111111111
用户输入的内容仍然是-1。
举例
#include?<stdio.h>
int?main()
{
unsigned?int?a?=?0x100000000;//丢弃最高位1
int?b?=?0xffffffff;????? //int类型最大值0x7fffffff的补码为0x10000001
printf("a=%u, b=%d\n",?a,?b);
return?0;
}
运行结果:
a=0, b=-1
假设是一个超大值:
unsigned int a = 222222222222222222222222222222222222;
编译时提示:
error C2177: 常量太大
注:计算补码的方法:符号位不变,其余各位取反后加1。
接下来验证int、short int、long int有符号整型能储存多少字节。
示例代码八
int main(void)
{
??? int i = 0;
??? printf("int 存储大小 : %u byte\n", sizeof(i));//无符号整型输出
??? short int? x = 0;//【可以缩写为 short x = 0;】
??? printf("short 存储大小 : %u byte\n", sizeof(x));//无符号整型输出
??? long int y = 0;//【可以缩写为 long y = 0;】
??? printf("long 存储大小 : %lu byte\n", sizeof(y));//无符号长整型输出
??? system("pause");
??? return 0;
}
?????? ●输出结果:
?????? int 存储大小 : 4 byte
short 存储大小 : 2 byte
long 存储大小 : 4 byte
请按任意键继续. . .
示例八输出的结果显示:
在VS编译器中,使用sizeof()运算符取出有符号整型数据长度。int存储空间大小为4字节;short 存储空间大小为2字节;long存储空间大小为4字节。
printf函数输出的格式化说明符’%u’表示无符号整型unsigned int格式,’%lu’表示long unsigned无符号长整数格式。之所以使用无符号整数格式输出,说明使用sizeof()运算符取出的数据类型的长度均为以字节为单位的正整数。
当然在这里使用’%d’有符号整数格式输出也是没有问题的,只要不超出有符号整数的数据范围即可。在很多情况下,程序员处于习惯的原因,使用有符号整数数据类型显示无符号整数,但是建议采用规范的编码格式,避免不必要的错误。
实验二十四:超出数据类型存储空间
将示例八中printf函数的格式化说明符改为’%d’,
printf("int 存储大小 : %d byte\n", sizeof(i));
printf("short 存储大小 : %d byte\n", sizeof(x));
printf("long 存储大小 : %d byte\n", sizeof(y));
按F7编译:
========= 生成: 成功 1 个,失败 0 个,最新 0 个,跳过 0 个 ==========
然后输出结果:
int 存储大小 : 4 byte
short 存储大小 : 2 byte
long 存储大小 : 4 byte
请按任意键继续. . .
■汇编代码
;C标准库头文件和导入库
include vcIO.inc
.data?????? ;全局区
i????? sdword 0???? ;等价于int类型
x????? sword ?? 0???? ;等价于short int类型
y???? sdword 0???? ;等价于long int类型
.const???? ;常量区
szMsg1 db "int 存储大小 : %u byte",0dh,0ah,0
szMsg2 db "short 存储大小 : %u byte",0dh,0ah,0
szMsg3 db "long 存储大小 : %lu byte",0dh,0ah,0
.code????? ;代码区
start:
?????? push sizeof i
?????? push offset szMsg1?????
?????? call printf?????????????
?????? ;
?????? push sizeof x
?????? push offset szMsg2?????
?????? call printf?????????????
?????? ;
?????? push sizeof y
?????? push offset szMsg3?????
?????? call printf?????????????
?????? ;?????
?????? invoke _getch
?????? ret???????????????????????
end start
●输出结果:
int 存储大小 : 4 byte
short 存储大小 : 2 byte
long 存储大小 : 4 byte
汇编代码中,使用sizeof操作符取出变量的长度。汇编数据类型sdword等价于C语言数据类型int,sword类型等价于short int类型,sdword类型等价于long int类型。
?????? ■反汇编代码
??????? int i = 0;
00E81838? mov???????? dword ptr [i],0?
??? printf("int 存储大小 : %u byte\n", sizeof(i));//无符号整型输出
00E8183F? push??????? 4?
00E81841? push??????? offset string "int \xb4\xe6\xb4\xa2\xb4\xf3\xd0\xa1 : %u byte\n" (0E87B30h)?
00E81846? call??????? _printf (0E8104Bh)?
00E8184B? add???????? esp,8?
??? short int? x = 0;//【可以缩写为 short x = 0;】
00E8184E? xor???????? eax,eax?
??? short int? x = 0;//【可以缩写为 short x = 0;】
00E81850? mov???????? word ptr [x],ax?
??? printf("short 存储大小 : %u byte\n", sizeof(x));//无符号整型输出
00E81854? push??????? 2?
00E81856? push??????? offset string "short \xb4\xe6\xb4\xa2\xb4\xf3\xd0\xa1 : %u byte\n" (0E87B4Ch)?
00E8185B? call??????? _printf (0E8104Bh)?
00E81860? add???????? esp,8?
??? long int y = 0;//【可以缩写为 long y = 0;】
00E81863? mov???????? dword ptr [y],0?
??? printf("long 存储大小 : %lu byte\n", sizeof(y));//无符号长整型输出
00E8186A? push??????? 4?
00E8186C? push??????? offset string "long \xb4\xe6\xb4\xa2\xb4\xf3\xd0\xa1 : %lu byte\n" (0E87B6Ch)?
00E81871? call??????? _printf (0E8104Bh)?
00E81876? add???????? esp,8
观察下面的反汇编语句:
??? int i = 0;
mov dword ptr [i],0
short int? x = 0;//【可以缩写为 short x = 0;】
mov word ptr [x],ax
long int y = 0;//【可以缩写为 long y = 0;】
mov dword ptr [y],0
int类型对应汇编dword类型是4字节; short类型对应word类型是2字节;long类型对应dword类型是4字节。
提示
在Linux系统上,int的字长与处理器的字长一致;在Windows操作系统上,操作系统的字长与处理器的字长不一定一致,编译器根据系统的字长来定义int的字长。
?????? 4.在VS编译器中,默认缺省的数据类型为int类型,默认缺省的浮点类型是double类型。
实验二十五:获取有符号数据类型的取值范围
C标准库<limits.h>头文件中使用宏定义了各种数据类型的取值范围,宏定义可以直接使用,例如CHAR_BIT、INT_MIN、INT_MAX等,具体见附录D。
/*
?? limits.h 头文件取数据类型取值范围
*/
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main(void)
{
printf("The number of bits in a byte %d\n", CHAR_BIT);//一个字节数据位数
printf("The minimum value of INT = %d\n", INT_MIN); // int 最小值
printf("The maximum value of INT = %d\n", INT_MAX); // int 最大值
printf("The minimum value of SHORT INT = %d\n", SHRT_MIN);// short 最小值
printf("The maximum value of SHORT INT = %d\n", SHRT_MAX);// short 最大值
printf("The minimum value of LONG = %ld\n", LONG_MIN); // long 最小值
printf("The maximum value of LONG = %ld\n", LONG_MAX); // long 最大值
system("pause");
return 0;
}
●输出结果:
The number of bits in a byte 8
The minimum value of INT = -2147483648
The maximum value of INT = 2147483647
The minimum value of SHORT INT = -32768
The maximum value of SHORT INT = 32767
The minimum value of LONG = -2147483648
The maximum value of LONG = 2147483647
请按任意键继续. . .
?????? ●查看limits.h头文件
VS中鼠标选中limits.h头文件,点击鼠标右键,选中“打开文档<limits.h>”,显示内容如下:
//
// limits.h
//
//????? Copyright (c) Microsoft Corporation. All rights reserved.
//
// The C Standard Library <limits.h> header.
//
#pragma once
#define _INC_LIMITS
#include <vcruntime.h>
_CRT_BEGIN_C_HEADER
#define CHAR_BIT????? 8???????? // number of bits in a char
#define SCHAR_MIN?? (-128)????? // minimum signed char value
#define SCHAR_MAX???? 127?????? // maximum signed char value
#define UCHAR_MAX???? 0xff????? // maximum unsigned char value
#ifndef _CHAR_UNSIGNED
??? #define CHAR_MIN??? SCHAR_MIN?? // mimimum char value
??? #define CHAR_MAX??? SCHAR_MAX?? // maximum char value
#else
??? #define CHAR_MIN??? 0
??? #define CHAR_MAX? ??UCHAR_MAX
#endif
#define MB_LEN_MAX??? 5???????????? // max. # bytes in multibyte char
#define SHRT_MIN??? (-32768)??????? // minimum (signed) short value
#define SHRT_MAX????? 32767???????? // maximum (signed) short value
#define USHRT_MAX???? 0xffff?? ?????// maximum unsigned short value
#define INT_MIN???? (-2147483647 - 1) // minimum (signed) int value
#define INT_MAX?????? 2147483647??? // maximum (signed) int value
#define UINT_MAX????? 0xffffffff??? // maximum unsigned int value
#define LONG_MIN??? (-2147483647L - 1) // minimum (signed) long value
#define LONG_MAX????? 2147483647L?? // maximum (signed) long value
#define ULONG_MAX???? 0xffffffffUL? // maximum unsigned long value
#define LLONG_MAX???? 9223372036854775807i64?????? // maximum signed long long int value
#define LLONG_MIN?? (-9223372036854775807i64 - 1)? // minimum signed long long int value
#define ULLONG_MAX??? 0xffffffffffffffffui64?????? // maximum unsigned long long int value
#define _I8_MIN???? (-127i8 - 1)??? // minimum signed 8 bit value
#define _I8_MAX?????? 127i8???????? // maximum signed 8 bit value
#define _UI8_MAX????? 0xffui8?????? // maximum unsigned 8 bit value
#define _I16_MIN??? (-32767i16 - 1) // minimum signed 16 bit value
#define _I16_MAX????? 32767i16????? // maximum signed 16 bit value
#define _UI16_MAX???? 0xffffui16??? // maximum unsigned 16 bit value
#define _I32_MIN??? (-2147483647i32 - 1) // minimum signed 32 bit value
#define _I32_MAX????? 2147483647i32 // maximum signed 32 bit value
#define _UI32_MAX???? 0xffffffffui32 // maximum unsigned 32 bit value
// minimum signed 64 bit value
#define _I64_MIN??? (-9223372036854775807i64 - 1)
// maximum signed 64 bit value
#define _I64_MAX????? 9223372036854775807i64
// maximum unsigned 64 bit value
#define _UI64_MAX???? 0xffffffffffffffffui64
#if _INTEGRAL_MAX_BITS >= 128
??? // minimum signed 128 bit value
??? #define _I128_MIN?? (-170141183460469231731687303715884105727i128 - 1)
??? // maximum signed 128 bit value
??? #define _I128_MAX???? 170141183460469231731687303715884105727i128
??? // maximum unsigned 128 bit value
??? #define _UI128_MAX??? 0xffffffffffffffffffffffffffffffffui128
#endif
#ifndef SIZE_MAX
??? #ifdef _WIN64
??????? #define SIZE_MAX _UI64_MAX
??? #else
?????? ?#define SIZE_MAX UINT_MAX
??? #endif
#endif
#if __STDC_WANT_SECURE_LIB__
??? #ifndef RSIZE_MAX
??????? #define RSIZE_MAX (SIZE_MAX >> 1)
??? #endif
#endif
_CRT_END_C_HEADER
实验二十六:获取无符号数据类型的长度
/*
?? 输出无符号整型的长度
*/
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
??? unsigned int i = 0;
??? unsigned short int? s = 0;//【可以缩写为unsigned short s = 0;】
??? unsigned long int l = 0;//【可以缩写为 unsigned long l = 0;】
??? printf("unsigned int 存储大小 : %u byte\n", sizeof(i));
??? printf("unsigned short int 存储大小 : %u byte\n", sizeof(s));
??? printf("unsigned long int 存储大小 : %u byte\n", sizeof(l));
??? system("pause");
??? return 0;
}
●输出结果:
unsigned int 存储大小 : 4 byte
unsigned short int 存储大小 : 2 byte
unsigned long int 存储大小 : 4 byte
请按任意键继续. . .
?
结论
unsigned int 存储空间大小为4字节;unsigned short int 存储空间大小为2字节;unsigned long int 存储空间大小为4字节;
unsigned int、unsigned short int、unsigned long int的 范围在这里就不做实验了,希望读者能够自己动手完成【同样使用limits.h库函数】。
【无符号整型和有符号整型的区别】
???? 本节只对32位操作系统下int 和 unsigned int做比较。
???? int:有符号整数,占4个字节,可表示范围【-2147483648~2147483647】。
???? unsigned int:无符号整数,占4个字节,可表示范围【 0 ~4294967295】。
在C语言中初始化 int i = -1;其实等价与signed int i = -1;关键字signed可以省略。因为C语言默认就是有符号类型的。
?? 为了说明signed和unsigned的区别,首先要搞清楚数据在内存中是如何存储的。
举例
Unsigned int i =1;变量i在内存中是 0000 0000 0000 0000 0000 0000 0000 0001存储的,由于unsigned int 是无符号整型,所以它的32位全部用来存储数据;用图表的形式表示:
32位存储数据32
int j = -1;由于int是有符号整型,所以就要考虑符号位的问题了。还有一点就是正数是以它自身形式存储的,而负数是以补码的形式存储的,参见《X86汇编基础教程》预备知识,此处不再赘述,直接写出-1原码、反码、补码。
-1的源码:?? 1000 0000 0000 0000 0000 0000 0000 0001
反码????????????? 1111 1111 1111 1111 1111 1111 1111 1110
补码:????? 1111 1111 1111 1111 1111 1111 1111 1111
? ? ? ? ? ??
从高位到地位??? 最高位为符号位,其余31位存储数据
实验二十七:获取无符号数据类型的长度
分别定义int类型变量j和unsigned int类型变量i,并给两个变量赋给相同的值-1,然后分别按照’%u’和’%d’格式输出。
/*
?? 输出无符号整型和有符号整数
*/
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
??? unsigned int? i = 0;
??? int j = -1;
??? i = j;
??? printf("i = %u\n", i);
??? printf("j = %d\n", j);
??? system("pause");
??? return 0;
}
●输出结果:
i = 4294967295
j = -1
请按任意键继续. . .
控制台窗口中显示的”i = 4294967295“,这个结果怎么来的呢?
因为变量j为int有符号整数类型,值为-1,负整数以补码形式存储在内存中,-1的补码是:
变量i为unsigned int无符号整数类型,存储时编译器将-1转换为补码然后存储到内存中,无符号整数将32位全部看作是数值位,因此按照无符号整数格式输出的0xffffffff的十进制数为4294967295。
unsigned int 类型的取值范围0 ~4294967295,即0~232-1,int 类型的取值范围-231~231-1,-1是最大负整数,如下图所示:
?????????????????? ????????
????????????????????????????????????????????????图3-4 32位整数取值范围
在图3-4中,无符号数均为正数,取值范围是:【0~0xFFFFFFFF】。
有符号数正整数的范围是:【0~0x7FFFFFFF】。
有符号数负整数的范围是:【0x80000000~0xFFFFFFFF】。
接下来我们在内存中观察变量i和变量j的值:
第一步:在VS中unsigned int? i = 0;一行F9下断点;
第二步:F5调试执行,在监视窗口1输入’&i’和’&j’,监视窗口显示两个变量的初始值:
第三步:F10单步执行到ystem("pause");语句,此时监视窗口按照十六进制格式显示内容如下:
?????? 第四步:监视窗口点击鼠标右键,去掉“十六进制显示”选项,按照默认十进制格式显示内容如下:
其中已返回printf表示printf函数的返回值为7,即7个字符"j = %d\n"。
以上介绍的是整型数据类型,下面介绍浮点型数据类型。
本文摘自编程达人系列教材《汇编的角度——C语言》。