赞
踩
类型的意义:
char在字符存储的时候存的是一个ASCLL码值,而ASCLL码值是一个整数
数值有正数和负数之分
-下面我们来介绍一下这个空指针
void*
叫做【空指针】
void
类型的指针可以用来接收任何类型数据的地址【它就像一个垃圾桶一样,起到临时存放的作用】void 表示空类型(无类型)
通常应用于函数的返回类型、函数的参数、指针类型
计算机中的整数有三种2进制表示方法,即原码、反码和补码。
0
表示“正”,用1
表示“负”,而数值位正数的原、反、补码都相同,负整数的三种表示方法各不相同int a = 10;
int a = -10;
ff ff ff f6
,这里发现是倒着存的,那么就要涉及到大小端存储的~~在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8 bit
低位
保存在内存的高地址
中,而数据的高位
,保存在内存的低地址
中;低位
保存在内存的低地址
中,而数据的高位
,,保存在内存的高地址
中;0x11223344
,以进制的权重来看的话右边的权重低【0】,左边的权重高【3】,所以11为高位,44为低位。若是对其进行小端字节存储的话就要将44存放到低位,11存放到高位请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。(10分)
int a = 1;
01
放在低位;而以【大端字节序存储】会将01
放在高位,那么此时我们只需要获取到内存中规定最低位即可01
在内存中表示一个字节,而一个【char】类型的数据就为1个字节,所以此时我们可以使用到一个字符型指针接受要存放到内存中的这个数,然后对其进行一个解引用,就可以获取到低位的第一个字节了char* p = &a;
1
的话那就是【小端】,反之则是【大端】char* p = (char *)&a;
if (*p == 1){
printf("小端\n");
}
else {
printf("大端\n");
}
int check_sys(int num)
{
char* p = (char*)#
if (*p == 1) {
return 1;
}
else {
return 0;
}
}
int ret = check_sys(1);
if (*(char*)&num == 1)
int check_sys(int num){
return *(char*)#
}
首先我们通过下面这幅图来看一看对于有符号的char
和无符号的char
在内存中所能表示的范围各自是多少:
-128 ~ 127
0 ~ 255
char数据类型在内存中占1个字节,也就是8个比特位。若是从00000000
开始存放,每次+1上去然后逢二进一,之后你就可以得出最大能表示的正整数为【127】,可是在继续+1后又会进行进位然后变为10000000
,符号位为1,这是-128
这是为什么呢?
在内存中都是以【补码】的形式进行存放,所以我们看到的1 1111111
只不过是补码的形式
但是对于10000000
我们直接将其记作【-128】,它就对应的【-128】在内存中的补码,通过去写出【-128】的原、反、补码可以发现是需要9个比特位来进行存放,对于char
类型的数值而言只能存放8个比特位,因此在转换为补码之后会进行一个截断
最后剩下的就是10000000
,即为有符号char的最小负数为【-128】
0 ~ 255
%u
是打印无符号整型,认为内存中存放的补码对应的是一个无符号数%d
是打印有符号整型,认为内存中存放的补码对应的是一个有符号数第一道
#include <stdio.h>
int main()
{
char a = -1;
signed char b = -1;
unsigned char c = -1;
printf("a = %d, b = %d, c = %d", a, b, c);
return 0;
}
-1
存放到这两个数据中去,首先你应该要考虑到的是一个数据放到内存中去是以补码的形式-1
转换为补码的形式1 0000000 00000000 00000000 00000001
1 1111111 11111111 11111111 11111110
1 1111111 11111111 11111111 11111111
11111111
这8个字节。-1
存放到它里面还是11111111
这8个字节不会改变,只不过在内存中的变化与有符号char不太一样~~printf("a = %d, b = %d, c = %d", a, b, c);
%d
的形式进行一个打印,但是呢三个变量所存放的都是char类型的变量,因此会进行一个整型提升,只是有符号数的整型提升和无符号数不太一样//a b - 有符号数
11111111111111111111111111111111 - 补符号位
//c - 无符号数
00000000000000000000000011111111 - 补0
11111111111111111111111111111111
10000000000000000000000000000000
10000000000000000000000000000001 ——> 【-1】
00000000000000000000000011111111 ——> 【255】
第二道
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}
10000000 00000000 00000000 10000000
11111111 11111111 11111111 01111111
11111111 11111111 11111111 10000000
char
类型的变量中,因为进行截断为10000000
char
类型的变量将会填充符号位11111111111111111111111110000000
%u
的形式进行打印,认为在内存中存放的是一个无符号整数。我们知道,对于无符号整数来说,不存在负数,所以其原、反、补码都是一样的,因此在打印的时候就直接将其转换为十进制进行输出printf("%u\n",a);
输出:
第三道
#include <stdio.h>
int main()
{
char a = 128;
printf("%u\n",a);
return 0;
}
-128
变成了128
而已10000000
又因为是%u
的形式打印,然后需要整形提升00000000 00000000 00000000 10000000
10000000
符号位是1,所以整形提升的时候补111111111 11111111 11111111 10000000
第四道
int main()
{
int i = -20;
unsigned int j = 10;
printf("%d\n", i + j);
return 0;
}
下面是解法:
int main() { int i = -20; //1 0000000 00000000 00000000 00010100 //1 1111111 11111111 11111111 11101011 //1 1111111 11111111 11111111 11101100 unsigned int j = 10; //0 0000000 00000000 00000000 00001010 printf("%d\n", i + j); //1 1111111 11111111 11111111 11101100 //0 0000000 00000000 00000000 00001010 //------------------------------------------ //1 1111111 11111111 11111111 11110110 //1 1111111 11111111 11111111 11110110 //1 0000000 00000000 00000000 00001001 //1 0000000 00000000 00000000 00001010 —— 【-10】 //按照补码的形式进行运算,最后格式化成为有符号整数 return 0; }
int
类型的数据,一个是有符号的,一个是无符号的。但无论是有符号还是无符号,放到内存中都是要转换为补码的形式int
类型的变量中去,所以不需要进行【截断】和【整型提升】 1 1111111 11111111 11111111 11101100
0 0000000 00000000 00000000 00001010
------------------------------------------
1 1111111 11111111 11111111 11110110
%d
的形式进行打印输出,那就会将内部中存放的补码看做是一个有符号数,既然是有符号数的话就存正负,可以很明显地看到最前面的一个数字是1
,所以是负数,要转换为原码的形式进行输出1 1111111 11111111 11111111 11110110
1 0000000 00000000 00000000 00001001
1 0000000 00000000 00000000 00001010 —— 【-10】
第五道
int main()
{
unsigned int i;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
}
return 0;
}
运行结果:>死循环
有同学就很诧异为什么会陷入死循环呢?这不是就是一个正常的打印过程吗?
其实,问题就出在这个unsigned
,把它去掉之后就可以正常打印了
回忆一下我们在将无符号整数的时它的数据范围是多少呢
char
类型来说是0 ~ 255
;short
来说是0 ~ 65536
;int
类型来说是0 ~ 16,777,215
;对比进行观察其实可以发现它们的数值范围都是 > 0的,所以对于无符号整数来说就不会存在负数的情况。因此这个for循环的条件【i >= 0】其实是恒成立的,若是当i == 0
再去--
,此时就会变成【-1】
对于【-1】我们有看过它在内存中的补码形式为11...11
是全部都是1,而此时这这个变量i又是个无符号的整型,所以不存在符号位这一说,那么在计算机看来它就是一个很大的无符号整数。此时当i以这个数值再次进入循环的时候,继续进行打印,然后执行--i
,最后知道其为0的时候又变成了-1,然后继续进入循环。。。
光是这么说说太抽象了,我们可以通过Sleep()函数在打印完每个数之后停一会,来观察一下
#include <windows.h>
int main()
{
unsigned int i;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
Sleep(200);
}
return 0;
}
i
循环到0的时候,突然就变成了一个很大的数字,这也就是印证了我上面的说法第六道
unsigned char
来说,最大的整数范围不能超过255
,所以当这里的【i】加到255之后又会再+1就会变成00000000
,此时又会进入循环从0开始,也就造成了死循环的结果unsigned char i = 0;
int main()
{
for (i = 0; i <= 255; i++)
{
printf("hello world\n");
}
return 0;
}
第七道
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
return 0;
}
-1 -2 -3 -4 -5 -6 -7 ...
但是呢,我们在上面学习过的有符号signed char
,它和char
是一样的,数据的范围在【-128 ~ 127】,所以当i
加到128的时候,这个位置上的值变为【-129】,此时在计算机内部会将它识别成【127】,同理【-130】会被识别成为【126】。。。依次类推,最后当这个值为【0】的时候若再去减就会变成【-1】,然后又变成【-2】【-3】【-4】。。。一直当这个i
累加到1001的时候截止
我们要通过strlen()
去求这个数字的长度,对于strlen()来说,求的长度是到\0
截止,那也就是上面的【0】,不需要去关心后面的第二、三轮回
那其实这也就转变成了让我们去求有符号char在内存中可以存储多少个。这很简单,范围是-128 ~ +127
,二者的绝对值一加便知为255
//-1 -2 -3 -4 -5 -6 -7 ...-128 127 126 ... 0 -1 -2 -3....
printf("%d", strlen(a));
在第二模块,我有提到过一个叫做【浮点数家族】,里面包含了[float]
和[double]
类型,对于浮点数其实我们不陌生,在上小学的时候就有接触到的3.14
圆周率,还有以科学计数法表示的1E10
在计算机中整型类型的取值范围限定在:limits.h
;浮点型类型的取值范围限定在:float.h
我们可以在【everything】中找找有关float.h
这个头文件
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n); //1
printf("*pFloat的值为:%f\n", *pFloat); //2
*pFloat = 9.0;
printf("num的值为:%d\n", n); //3
printf("*pFloat的值为:%f\n", *pFloat); //4
return 0;
}
9
float
类型的指针与int一样都可以访问四个字节的地址,所以解引用便访问到了n中的内容,又因为浮点数小数点后仅6位有效,因此打印出来应该是9.000000
%d
的形式进行打印,应该也还是9
9.000000
n
和 *pFloat
在内存中明明是同一个数,为什么浮点数和整数的解读结果会差别这么大?这要涉及到浮点数在内存中【存】与【取】规则,接下去我们首先来了解一下这个规则~~
V = (-1)^S * M * 2^E
(-1)^S
表示符号位,占一位。当S = 0
,V为正数;当S = 1
,V为负数E
为指数,占用8位【阶符采用隐含方式,即采用移码方法来表示正负指数】101
,这毋庸置疑,但是这个小数部分的5要如何去进行转换呢?对于0.5
来说我们刚才看了小数部分的权重之后知道是2-1,所以直接使这一位为1即可101.1
却远远大于1,所以我们可以通过在操作符中学习的【移位】操作将这个数进行左移两位,但是左移之后又要保持与原来的数相同,所以可以再乘上22*使得与原来的二进制相同。接着根据公式就可以写出*(-1)0 * 1.011 * 22这个式子,然后可以得出S、M、E为多少了S == 1
0.5
,如果是0.25
的话你可以将【2-2】置为1,依次类推。。。可以对于下面这个3.3
里面的0.3
你会如何去凑数呢,首先0.5
肯定不能,那只能有0.25
,但若是再加上0.125
的话那就多出来了,那应该配几呢?0.3
的。所以我们可以得出这个数字其实是无法在内存中进行保存。这也是为什么浮点数在内存中容易发生精度丢失的原因进一步探索指数E与尾数M的特性:
IEEE 754标准规定:
32位
的浮点数【float】,最高的1位是符号位S
,接着的8位是指数E
,剩下的23位为有效数字M
64位
的浮点数【double】,最高的1位是符号位S
,接着的11位是指数E
,剩下的52位为有效数字M
IEEE 754对有效数字M和指数E,还有一些特别规定:
首先是对于有效数字(尾数)M
1 ≤ M < 2
,也就是说,M可以写成 1.xxxxxx
的形式,其中xxxxxx表示小数部分1
,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01
,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。22位
,但若是将第一位的1舍去以后,等于可以保存23位
有效数字,精度相当于又高了一位至于指数E,情况就比较复杂
首先,E为一个无符号整数(unsigned int
)
那对于无符号整数来说,我们在上面有介绍过,如果E为8位,它的取值范围为[0 - 255]
;如果E为11位,它的取值范围为[0 - 2047]
但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023
简单一点,还是上面讲到过的5.5
,注意如果定义成【float】类型的变量的话要在后面加上一个f
作为和【double】类型的区分
float f = 5.5f;
5.5
这个数字去进行分析它存入到内存中的样子。通过上面算出来的 S = 0, M = 1.011, E = 2
去写出这32位浮点数存放到内存中是一个怎样的形式
对于符号数S来说就是把0存进去,占一位
对于指数E来说为2,32位浮点数要加上一个中间值127,所以要存入的十进制数为129,再将其转换为8位二进制即为10000001
对于尾数M来说,需要舍去整数位1,然后将【小数部分】的011
这三位放到内存中,但是规定了M为23位,此时我们只需要在后面补上20个0即可
然后便可以对这个32个比特位进行划分,8位一个字节,得出40 b0 00 00
还有一点莫要忘了!还记得我们上面讲到的【大小端】存放吗?要存放到内存中的最后一步就是将其进行小端存放【这是我的机器】,即为00 00 b0 40
如何将浮点数从内存中【读取】出来呢?
指数E从内存中取出还可以再分成三种情况:
1. E不全为0或不全为1
127
,那么此时减去127即可;在计算尾数M的时候舍去了整数部分的1,那次此时再补上这个1即可2. E全为0
00000000
,这个情况是在指数E加上127之后的结果,那么原先最初的指数是多少呢?那也只能是-127
了。那如果这个指数是-127的话也就相当于是【1.xxxx * 2-127】,是一个非常小的数字,几乎是和0没有什么差别±0
,以及接近于0的很小的数字3. E全为1
11111111
,这个情况也是在指数E加上127之后的结果,那么原先最初的指数是多少呢?那便能是128
了,那也只能是-127
了。那如果这个指数是-127的话也就相当于是【1.xxxx * 2128】,是一个非常大的数字以上就是有关浮点数如何【写入】内存和从内存中【读取】的所有相关知识
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n); //1
printf("*pFloat的值为:%f\n", *pFloat); //2
*pFloat = 9.0;
printf("num的值为:%d\n", n); //3
printf("*pFloat的值为:%f\n", *pFloat); //4
return 0;
}
0 0000000 00000000 00000000 00001001
。然后是将这个n的地址存放到了一个浮点型的指针中去int n = 9;
float* pFloat = (float*)&n;
%d
的形式打印n不用考虑就是9;但是后一个就不一样了,对浮点型的指针进行解引用,那也就是要将存放在内存中的浮点数进行读取出来printf("n的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
00000000000000000000000000001001
,将第一位看做是符号位0,即S == 0
,然后接下去就是8个比特位E,不过可以发现这个E是全0呀00000000
,就是我们上面所讲到的这种特殊情况0.xxxxxx
的形式即0.000000000000000000001001
。那对于指数E应该等于1-127为【-126】。所以最后写出来v的形式为(-1)^0 * 0.000000000000000000001001 * 2^ (-126)
0.000000
9.0
存放到n这块地址中去,那也就相当于是我们最先学习了如何将一个浮点数存放到内存中去*pFloat = 9.0;
1001.0
,然后通过v的公式得出(-1)^0 * 1.001 * 2^3
10000010
,尾数M也是同理,舍去1后看到001
,后面添上20个0补齐23个尾数位。最后的结果即为——> 0 10000010 00100000000000000000000
%d
的形式打印n,那么这一串二进制就会被编译器看做是补码,既然是打印就得是原码的形式,不过看到这个符号位为0,那我们也不需要去做一个转换,它就是原码printf("num的值为:%d\n", n);
01000001000100000000000000000000 —— 1,091,567,616
好了,本文到这里就结束了,感谢大家的收看
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/393857
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。