数据的基本类型
/*
1.数据的基本类型
char 1
short 2
int 4
long 4/8
long long 8
float 4
double 8
规定:
sizeof(long) >= sizeof(int)
32位 long 4
64位 long 8
*/
整型家族
/*
整数定义范围的头文件为 limits.h
整型家族
char //char到底是unsigned char还是signed char 这在C语言标准中是未定义的,取决于编译器的实现
unsigned char
signed char
short == signed short
unsigned short
signed short
int == signed int
unsigned int
signed int
long == signed long
unsigned long
signed long
生活中有些数据是没有负数的,所以用unsigned int,比如身高,体重等
signed int 的最高位是符号位,没有数值意义
unsigned int的最高位具有数值意义
*/
浮点型家族
/*
浮点型家族
float的精度低,表示范围相对小,double精度高,表示范围相对较高
float
double
*/
构造类型--自定义类型 我们可以自己创建的新的类型
/*
构造类型--自定义类型 我们可以自己创建的新的类型
数组类型
比如 int arr[5]的类型为 int [5]
char sy[8]的类型为char [8]
结构体类型
枚举类型
联合类型
*/
指针类型
/*
指针类型
char *p;
int *p;
void *p;
*/
空类型
/*
空类型
void表示空类型,无类型
通常用于函数的参数,返回类型,指针类型
*/
整形再内存中的存储
/*
数值有不同的表现形式
二进制,八进制,十进制,十六进制
整数的二进制表示有三种
原码:最高位符号位,其余正常计算
反码:符号位不变,其余位取反
补码:反码+1
整数的原码,反码,补码相同
负数需要计算
整数再内存中存放的是补码的二进制序列
*/
大小端介绍
/*
0x11 22 33 44
低地址 高地址
大端字节序存储 11 22 33 44 (相反)数据低位在高地址,高位在低地址
小端字节序存储 44 33 22 11 (相同)低位在低地址,高位在高地址
*/
请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序
//计算机读取地址的顺序是从低地址到高地址的顺序
int main()
{
int a = 0x11223344;
char* p = (char* )(&a);
*p = 0;
if (a == 0x11223300)
{
printf("小端存储\n");
}
else if (a == 0x00223344)
{
printf("大端存储\n");
}
printf("%x\n", a);
return 0;
}
int main()
{
union
{
int i;
char a;
}p;
p.i = 0x00112233;
if (p.a == 0x00)
{
printf("大端\n");
}
else
{
printf("小端\n");
}
return 0;
}
signed char与unsigned char在内存中存储的二进制范围
/*
signed char在内存中存储的二进制范围
00000000 0
00000001 1
00000010 2
...... ......
01111110 126
01111111 127
10000000 **内存直接定义为 -128
10000001 -127
10000010 -126
........ ......
11111110 -2
11111111 -1
unsigned char在内存中存储的二进制范围 没有符号位,最高位具有数值意义
00000000 0
00000001 1
00000010 2
...... ......
01111110 126
01111111 127
10000000 128
10000001 129
10000010 130
........ ......
11111110 254
11111111 255
*/
整形数据在内存中的存储,怎么打印
/*
数据在内存中存放的数值是一回事
怎么打印,存储方式,变量形式又是一回事
*/
int main()
{
char a = -1;
signed char b = -1;
unsigned char c = -1;
char d = -128;
//整形提升是按照数据类型来补数据的,比如无符号型前面补0
//在C语言中-1默认为int类型的
//-1在内存中的补码为
//11111111 11111111 11111111 11111111
//但是要存储在char类型的变量中,故需要截断,将高地址的截掉
//截掉之后为
//11111111
//然后在通过"="赋给变量 c
printf("a = %d\nb = %d\nc = %d\nd = %u\n", a, b, c,d);
//%d--打印有符号的整形
//打印a,b同理
//先整形提升
// 11111111整形提升为
// 11111111 11111111 11111111 11111111 内存中补码
// 10000000 00000000 00000000 00000001 原码 -1
//打印c
// 11111111整型提升为,因为是无符号型的,所以前面补0
// 00000000 00000000 00000000 11111111 原码255
//打印d
// 在内存中的补码为
// 10000000 整形提升为
// 11111111 11111111 11111111 10000000 在内存中的补码
//%u--打印无符号的整形,故将上式子看成原码
//大小为d = 4294967168
return 0;
}
int main()
{
char a = 128;
// 00000000 00000000 00000000 10000000 128原码
// 00000000 00000000 00000000 10000000 128补码
// 10000000 存入a中的数据
printf("%u\n", a);
//a先整形提升到int型,因为a本身是char类型,所以最高位补符号位
// 10000000整型提升为
// 11111111 11111111 11111111 10000000
// 之后无符号型打印,会将上述看成一个正数
return 0;
}
int main()
{
int i = -20;
// 10000000 00000000 00000000 00010100 原码
// 11111111 11111111 11111111 11101011 反码
// 11111111 11111111 11111111 11101100 补码
// 11111111 11111111 11111111 11101100 存在i中的内容
unsigned int j = 10;
// 00000000 00000000 00000000 00001010 10的原码和补码
// 00000000 00000000 00000000 00001010 存在j中的内容
printf("%d\n", i + j);
// j是unsigned int类型,优先级大于i的int类型 故需要强制类型转换 小转大
// 00000000 00000000 00000000 00001010 存在j中的内容
// 11111111 11111111 11111111 11101100 存在i中的内容 此时i强制类型转换为unsigned int类型
// 11111111 11111111 11111111 11110110 i+j后的内容
// 通过%d的形式打印(打印有符号的整形),系统会默认上述为int类型来打印 --10
// 10000000 00000000 00000000 00001001
// 10000000 00000000 00000000 00001010 -10
return 0;
}
int main()
{
unsigned int i = 0;
for (i = 9; i >= 0; i--)
{
printf("%u ", i);
}
//当运行到i=0时 i-- i变成-1
// 11111111 11111111 11111111 11111111 -1在内存中的补码
// 11111111 11111111 11111111 11111111 存放在i中的数据,类型是unsigned int类型,其值大于0
// 故会进入死循环
return 0;
}
#include<string.h>
int main()
{
char a[1000];
int i = 0;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
/*
a[0] = -1;
a[2] = -2;
......
a[127] = -127
a[128] = -128 10000000
a[129] = -129 --> 127
10000000 00000000 00000000 10000001 -129原码
11111111 11111111 11111111 01111110 -129反码
11111111 11111111 11111111 01111111 -129补码
a[129]内存储 01111111
a[130] = -130 --> 126
10000000 00000000 00000000 10000010 -130原码
11111111 11111111 11111111 01111101 -130反码
11111111 11111111 11111111 01111110 -130补码
a[130]内存储01111110
a[131] = -131-->125
10000000 00000000 00000000 10000011 -131原码
11111111 11111111 11111111 01111100 -131反码
11111111 11111111 11111111 01111101 -131补码
a[131]内存储01111101
...................
a[255] = 1
a[256] = 0
共计255个
*/
/*
char类型不断-1或+1思路
char(-128----127)共256个 不算0 255个
00000000(0) --> 00000001(1) .........--> 01111110(126) --> 01111111(127)
↑ ↓
↑ ↓
11111111(-1)<--11111110(-2)<--......<-- 10000001(-127) <-- 10000000(-128)
顺时针+1,逆时针-1
*/
printf("%d\n", strlen(a));//255
return 0;
}
unsigned char i = 0;
int main()
{
for (i = 0; i <= 255; i++)
{
printf("hehe ");
}
/*
i++ --> i = i+1;
i=0
i=1
.........
i=127
i = 128
i=129
..........
i=255 //11111111
--> i = 0
故死循环
*/
return 0;
}
#include<string.h>
int main()
{
// streln 的返回类型是size_t === unsigned int 类型
if (strlen("abc") - strlen("abcdef") >= 0)
printf(">\n");
else
printf("<\n");
return 0;
}
浮点型在内存中的存储
//浮点型在内存中的存储
//浮点型定义范围的头文件为 float.h
//内存在存储的时候 解码和译码的方式要相同
// 比如以大端的形式存储到内存中,就要以大端的形式读取出来
//int main()
//{
// int n = 9;
// float* pFloat = (float*)&n;
// printf(" n = %d \n", n);
// printf(" *pFloat = %f\n", *pFloat);
// *pFloat = 9.0;
// printf(" n = %d \n", n);
// printf(" *pFloat = %f\n", *pFloat);
// return 0;
//}
浮点数存储规则
/*
根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数可以表示成下面的形式:
V = (-1)^S * M * 2^E
(-1)^S 表示符号位,当S为0表示整数,当S为1表示负数
M表示有效数字 大于等于1 小于2
2^E 表示指数位
float:
对于32位的浮点数(单精度)(写入方式均为小端)
S:最高位
E:后八位
M:最后23bit
double
对于64位的浮点数(双精度)
S:最高位
E:后11位
M:最后52bit
M:
对于M的存储方式
1 <= M <2,故M可以写成 1.xxxxxxx 的形式,其中xxxxxxx表示小数部分。
754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxx小数部分。
比如保存1.01时,只保存01,等到读取的时候,再把第一位加上去。这样做的目的是节省了以为有效数字。
以32位浮点数为例,留给M的只有23位,将第一位1舍去之后等于可以存储24位有效数字
E
对于E的存储方式
1.E为一个无符号整数 unsigned int
如果E为8位,那么他的取值范围为0-255
如果E位11位,那么他的取值范围为0-2047.
2.但是科学计数法中E是可以小于0的,即E可以是负数
故754规定存入内存是,E的真实值必须再加上一个中间数
对于8位的E,这个中间数是127;
对于11位的E,这个中间数是1023;
比如2^-1的E是-1,所以保存成32位浮点数时,必须保存成-1+127=126
3.指数E从内存中取出,还可以分为三种情况
*/
int main()
{
float f = 5.5;
/*
5.5
101.1
1.011 * 2^2
S = 0 M = 1.011 E = 2+127=129
S 0 0 共一位
M 011 01100000000000000000000 共23位
E 129 10000001 共8bit
故在内存中存储形式为
S E M
0 10000001 01100000000000000000000
0100 0000 1011 0000 0000 0000 0000 0000
4 0 b 0 0 0 0 0
0x40 b0 00 00
*/
return 0;
}
浮点型数据从内存中如何取出
/*
指数E从内存中取出可以分为三种情况
1.E不为全0也不为全1
这时浮点数就使用以下规则表示
E的值减去127(1023),得到真实值
再将有效数字M前加上第一位的1.
0.5
0.1
(-1)^0 * 1.0 * 2^(-1)
E = 127 + (-1) = 126
01111110 E在内存中的存储形式(单精度8,双精度11) unsigned int
尾数1.0去掉1为 0
M = 0
00000000000000000000000 M在内存中的存储形式(单精度23位,双精度52位)
则二进制表现形式为
0 01111110 00000000000000000000000 0.5的内存表现形式(单精度32位,双精度64位)
从内存中取出时
将E的值减去127即为真实值 01111110(unsigned int) - 127 = -1
在M的值前面加上1. 00000000000000000000000 ---> 1.0
即 1.0 * 2^(-1)
2.E全为0(+-无穷小)
这是浮点数的指数E等于1-127(1-1023)即为真实值
有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。
这样做是为了表示+-0,以及接近于0的很小的数字
3.E全为1(正负无穷大)
这时如果*有效数字M全为0,则表示+-无穷大(正负取决于符号位S)
*/
int main()
{
int n = 9;
// 00000000 00000000 00000000 00001001 9的原反补码
// 00000000 00000000 00000000 00001001 存入n中的数据
float* pFloat = (float*)&n;
printf(" n = %d \n", n);
// 00000000 00000000 00000000 00001001 n中存储的9的补码
// 按照有符号整形打印 故打印9
printf(" *pFloat = %f\n", *pFloat);
// 0 000000000 0000000000000000001001 *pFloat存储的9的补码
// S E M
// E 全0 代表无穷小
// 故输出0.000000
*pFloat = 9.0;
// 9.0的二级制为
// 1001 写成 1.001 * 2^3
// S = 0 M = 00100000000000000000000 E = 3+127 = 130 10000010
// 0 10000010 00100000000000000000000 *pFloat中存储的内容
printf(" n = %d \n", n);
// 0 10000010 00100000000000000000000 n中存储的内容
// %d 按照有符号整形的形式打印
// 故应该是一个很大的正数
printf(" *pFloat = %f\n", *pFloat);
// 0 10000010 00100000000000000000000 *pFloat中存储的内容
// S = 0 M = 00100000000000000000000 E = 3+127 = 130 10000010
// S = 0 M = 1.001 E = 130 - 127 = 3
// *pFloat = (-1)^S * M * 2^(3) = 1 * 1.001 * 2^3 = 1001 = 9.000000
return 0;
}