大家好呀,我是小生😎😎😎我们开始剖析C语言的第二期的学习啦!🙈🙊🙉
C语言是一门非常经典的语言,远没有我们在学校学的那几个语法而已,让小生再来带大家重新认识一遍C语言吧🥳🥳🥳对了对了,大家在看第二期的时候可以结合第一期一起看哦这样你会学到更多:🥳🥳🥳 深入剖析C语言第一期https://blog.csdn.net/qq_59955115/article/details/124210012?spm=1001.2014.3001.5501
废话不多说,大神们,我们先来看看这几个问题吧。 大神们可以思考一下哈🏃♀️🏃♀️🏃♂️🏃♂️🏃
🚀🚀🚀问题1:无符号整型变量是否能存储负数?数据存储的本质是什么?如何从本质上看待数据的存和取?
🚀🚀🚀问题2:什么是内存的大小端?如何深入理解内存的大小端?如何判断是大端存储还是小端存储?
🚀🚀🚀问题3:原码反码补码如何快速转换?在计算机中为什么要用补码?
🚀🚀🚀问题4:signed char类型的存储范围中为何包含-128?什么是数据的截断?截断是错误吗?
🚀🚀🚀问题五:sizeof你真的了解吗?如何验证它是关键字而不是函数?
或许大家对上面的问题并不是特别了解,那小生就带着大家更加深入地去学一下C语言吧,加油,技术人!!! 😎😎😎
🥳🥳🥳signed 和 unsigned 关键字
原码反码补码
通过之前的学习我们可以知道,在存储数据的时候我们需要开辟一块内存空间,但是数据是如何放到这一块内存空间里面的呢?首先我们需要将其分为两种:有符号数和无符号数。在vs编译器中int 类型默认是有符号的,但是在不同的编译器中int 类型并不一定总是默认有符号的。 相同的二进制序列存储无符号整型和有符号整型是不一样的,我们可以通过下面这个图认识一下😵😵😵
在计算机中数据都必须被转化成为二进制,为什么数据会被转化为二进制呢?这是因为计算机在运行的时候最后都只能处理二进制序列,以占32个比特位的int类型为例,一个int类型的变量所占四个字节,也就是32个比特位,而每个比特位只能存储0或者1。而有符号数的最高比特位会被作为符号位,0表示整数1表示负数,剩下的比特位便是数据位。
因此我们可以知道,在有符号数类型下且为正数的时候,原码=反码=补码。我们可以举两个例子,signed int num1 = 10; 那么10对应的原码为:0000 0000 0000 0000 0000 0000 0000 1010 正数的原码反码补码是相同的,然后我们把数据写成16进制:0x 00 00 00 0A
再举一个例子:signed int num2 = -20; 那么-20对应的原码为
0000 0000 0001 0100 但是计算机虽然只直接执行二进制序列,但它所需要的是补码,我们应该首先将其转变为反码,然后再将其转变为补码。那我们可以直接对-20的原码转为反码为:1111 1111 1111 1111 1111 1111 1110 1011,再转变为补码,那补码我们应该如何转变呢?只需在反码的基础上加1即可变为补码:1111 1111 1111 1111 1111 1111 1110 1100 。我们可以知道当原码转为反码的时候,符号位不参与运算,但是在反码变为补码的时候符号位是需要参与运算的。同样我们把它转变为16进制:0x FF FF FF EC究竟是不是如我们所想的一样呢?我们可以来调试一下监视内存的数据
实践证明我们的猜想是正确的。以上的是有符号数的存储,那么无符号数的存储就更加简单了,与有符号数里面的正数类似,直接存就行了。以上我们说了原码转反码,那么反码如何转为原码呢?大家可以思考一下,相信大家都能自己算出来吧,小生就不罗嗦啦~~~🙈🙉🙊
存储的本质
大家先思考一下下面这两行代码是否正确:
unsigned int num1 = 10;
unsigned int num2 = -20;
第一个肯定是正确的,但是第二个是否正确呢?首先我们此时实现定义变量并且做了初始化,计算机在存储的时候先把数字转化为二进制保存在变量当中,先开辟空间,然后把数字转为二进制再转变为补码,但在放置变量的时候空间是不会关心内容的,不会关心是不是正数还是负数,只是把二进制序列存储就行了。因此编译器不会直接报警告和报错。那unsigned有什么意义呢?在什么时候起作用呢?实在读取的时候具有意义,类型决定了如何解释空间内部保存的二进制序列。我们直接打印一下试试:(unsigned int 打错了,呜呜呜,大家别在意哈😭😭😭)
为什么会出现这种情况呢?那我们就需要用到下面的知识了
什么是变量的存和取
变量存的过程:字面数据必须先转换成补码,放入空间中,因此符号位完全看数据本身是否携带正负号,和变量是否有符号无关。
变量取的过程:取数据一定要看变量本身类型,然后再决定要不要看变量最高符号位,如果不需要直接二进制转化成十进制,如果需要则需转成原码才能够识别。
🥳🥳🥳内存的大小端:
当我们监视内存时以1字节显示的时候,地址从上到下是依次增大的,我们可以通过调试来看一下内存的地址。如果我们以四字节显示呢?结果会出现什么情况呢?我们也可以来看一下:
当我们监视num的地址存放的数据的时候我们会发现和我们常规所期待的数据有出入,我们本来想让这块内存显示aabbccdd,但是显示的是ddccbbaa是不是出错了呢?🙈哈哈,这里我们需要更加详细地说一说大小端问题了。
如何理解大小端:
数据存储在内存当中,内存再寻址的时候都是以字节为基本单位的,以int 类型为例,需要四个字节存储,而且这四个地址肯定是不同的,既有高地址也有低地址。因此数据在肯定会被划分成四块,那么如何划分呢?数据按照字节为单位,也是有高权值位和低权值位,那么我们把高权值位放到高地址处还是放到低地址处呢?其实无论是放在哪个位置,只要存和取的位置是一样的就行。
大小端的基本概念:
大端:按照字节为单位,低权值位数据存储在高地址处叫做大端。
小端:按照字节为单位,低权值位数据存储在低地址处叫做小端。
让小生画个图带大家理解一下:🚕🚕🚕
大小端存储方案本质上是数据和空间按照字节为单位的一种映射关系,按照大端存储写入就以大端的规则读取,按照小端存储写入就按照小端的规则存储。以小端存储有符号数为例,先看大端还是小端存储,再看自身类型,即首位是否为1,然后将该补码转为原码最后转为十进制。
我们再来思考一个问题,既然char类型的空间只有一个字节也就是八个比特位大小,而且还有一个比特位是符号位,通过排列组合的知识我们可以知道,我们可以存放-127~0和0~127的数,但是实际上在我们这种思考里0出现了两次,转为二进制即0000 0000和1000 0000,但是计算机在存储的时候0只能有一个对应的二进制,那可不可以把1000 0000 省略呢?不可以,因为计算机不能忽视任何一种排列组合,因此我们把1000 0000 规定为值-128,也就是char类型存储的数据可以在-128~127中,但是仅仅是规定那么简单吗?就没有一点依据吗?显然是不可能的
当我们执行char c = -128的时候,我们先将变为二进制原码,或许你会好奇,为什么会出现九位,不是只能存八位吗?我们一步一步来看,二进制的转化是一个数学计算的过程而非存储的过程,我们来看转为二进制原码的结果即 1 1000 0000,再取反即1 0111 1111 最后变为补码1 1000 0000 存到八个字节中因此一定会发生空间截断问题,恰好截断后的存储的是1000 0000 是不是和之前我们所说的规定是相同的呢?确实挺有意思的。那截断是一种错误吗?不是,只能说截断是一种丢弃数据的方式。
🥳🥳🥳如何看待数据类型和sizeof关键字
sizeof确定一种类型对应在开辟空间的时候的空间的大小,我们先来看一下:😵😵😵
或许你可能会很疑惑,这么简单的程序为啥还要写出来,😂😂😂其实不然,我们只是为了引入sizeof关键字而已,我们可以试着运行下面的代码: 😮😮😮
#include <stdio.h>
int main()
{
int num = 10;
printf("%d\n",sizeof(num));
printf("%d\n",sizeof(int));
printf("%d\n",sizeof num );
printf("%d\n",sizeof int );
return 0;
}
在vs中直接运行会报错,但当我们注释掉printf("%d\n",sizeof int);这一行的时候程序就可以运行, 前两种是正确的很好理解,但是我们发现第三种也可以运行得出正确答案,其实很多人都会误认为sizeof是一个求空间大小的函数,但是我们在调用函数的需要形参列表或者括号,而sizeof并不需要,因此sizeof不是一个函数,而是一个关键字,大家不要误会了,哈哈。😎😎😎但是作为关键字的sizeof函数在不用括号的时候不能直接来求其他内置类型的空间大小。同时除了内置类型,我们也可以用sizeof求自定义类型的内容。
C语言中为什么需要类样型,本质上是对内存进行合理划分,按需索取。因为应用场景不同,对于不同的场景计算方式是不同的,所需要的内存大小是不一的,因此类型是让我们,使用最小的成本解决不同的计算问题。
🥳🥳🥳小生想说的话
其实我想和大家讨论一个问题🐻🐻🐻怎么说呢?从小生的角度看,算法不止是算法,语言也不止是语言,两者当然有很紧密的结合,但是两者的联系或许并没有想得那么大。不知道大家是如何学习的,但小生之前就用做题来衡量对C语言的掌握程度,现在我觉得这种方法是不正确的😭😭😭,这只能说明我们对C语言的语法和算法的掌握,但是和语言的本身好像并无太大的关联。 或许我们需要重新认识这门语言了,而并非是语法和算法😵😵😵 由此我们学的C语言实际上是语言,因此我们首先得深入了解这门语言,然后用这门语言去实现算法。 这仅仅是小生的个人观点,如果大家有啥想法也可以跟小生说呀,谢谢啦。🙈🙈🙈
最后,如果大家觉得本文对大家有帮助的话,可以给小生一个三连哦,小生后面会不断更新更优质的内容的,你们的鼓励就是我最大的动力,加油技术人!!!🥳🥳🥳🥳