C语言之基础篇-数据类型变量字节

1、数据类型

计算机既然是用来处理数据的,那么就应该在计算机中先保存数据。但是对于计算机而言,只能够识别二进制,也就是0、1这两种状态的数据。但是用01这两位无法来具体的进行描述,因为对于计算机而言,并不知道0和1代表的是什么意思。

所以为了准确的描述出来一段01代码所能够代表的意思,那么0、1代码无法来进行准确描述,使用多个01数字组合起来进行描述具体的含义。同样,数据类型就是其中的体现之一。

那么我们人类所能够识别的数据最终都将以二进制的类型保存到计算机中去;那么对于数据来说,计算机如何识别我们的数据到底是哪种类型的呢?通过数据类型来进行识别。

所以针对于不同数据类型的数据,不同的语言在计算机中来采用不同的字节个数来进行数据存储。

比如C语言中是这样子来进行实现的:

整数类型:byte、short、int、long

小数类型:double、float

字符:'a'

布尔类型:boolean

对于整数的表示(通常): short int 短整型,占用2个字节;int 正常整数,四个字节;long int 长整型,八个字节;

对于小数的表示:float:表示小数,占用4个字节;double:表示小数,占用8个字节;double占用字节比float多,所以存储的数据更多,对应的数据的大小范围也表示的更大;

字符:'x',用单引号括起来的,里面写上一个字符即可,占用一个字节;

复合类型:结构体、枚举。结构体就是基本类型组成的混合体;枚举是可以一一列举出来的数据;

2、解释程序的意思

# include <stdio.h>
int main(void)
{
	int i;
	i = 10;
	printf("value is :%d\n",i);
	return 0;
}

这段代码的大致意思:

程序在运行的时候,首先需要被加载到内存中去,是我们的软件向操作系统来进行申请分配内存申请,int i这段代码表示的要向操作系统申请一块空间来保存这个变量i,申请多大的空间?根据int这个数据类型来确定的,占用四个字节,对应的值是什么?可以看到后面又给其进行了赋值,赋值是10,所以接着将10转换成二进制数据放入到对应的内存中去;然后将这个变量i对应的01的二进制代码以十进制的方式来打印到控制台上。

在程序运行期间,这块空间就属于当前程序,其他的程序无法霸占这块空间。就这样变量i和内存中的一块地址形成了映射操作。

操作变量i,就是操作变量保存的01代码数据。

比如说:

int i = 10;
那么对应的就是操作系统在内存中给i分配的存储空间,那么变量i就和对应的存储空间形成了一一映射的关系。
操作变量i也就是相当于操作i对应地址空间中存储的01数据。也就是说对应空间的01数据使用哪种数据类型来对其进行解读,将解读后的值保存到变量中来。   
    
是以指针类型、int类型、byte类型等,不同的解读方式得到的是不同的结果值。    

3、变量为什么要初始化

变量就是在程序运行过程中处于动态变化的。相对应的,在程序运行过程中一直不变的,那么叫做常量。

int i = 10;
const int j = 10;
i = 20;
// j = 1;  赋值失败,因为常量是不会变的

那么为什么说变量在声明的时候,一定要进行初始化呢?变量在程序运行的时候,会向操作系统申请内存空间来存放数据,但是申请过的内存空间可能是上一个程序所使用过的,内存空间里面已经有了01代码,那么现在去申请的这块空间被当前的变量i给占用了,那么这个里面的值就被i给使用了,但是这个值并不是当前这段程序本身设置的值。也就是说,申请过来的内存空间中的01数据都是并非是我们设置的数据,而是之前的程序运行之后留下来的结果,对于我们的程序使用来说毫无意义,相当于是垃圾值。那么我们必须得初始化我们自己的值进去,为了防止程序出现问题。

这个垃圾值又被称之为填充字,所以变量要进行初始化,不然不进行初始化就失去了意义。

1、程序的运行是在内存中的,但是程序本身是存储在硬盘上的,如果程序想要运行,那么需要先向操作系统申请对应的内存;

2、在程序运行期间,这块内存就独属于这段程序,操作系统将将硬盘中的程序拷贝到这段空间中去,让程序在这片空间中来进行运行。

3、当程序运行结束后,OS将回收这块内存空间。但是这里的回收指的是将这段空间设置成没有使用的状态,内存中的二进制数值没有清洗掉。

目的是为了让后来的程序使用的时候,自己来设置初始值将垃圾值进行覆盖。

比如说0-500这段空间是有程序在使用,那么OS将这段空间的标识全部标识为1,标识已占用,那么OS在给其他程序分配内存的时候发现已经占用了,那么就会去其他地方再找地方给需要运行的程序找到一块没有被占用的空间。

所以说创建对象的时候,如果没有释放掉,那么这块内存空间就一直被占用着,因为没有擦除掉已经被占用的标记。那么随着程序的运行,内存可用的空间越来越小,那么运行的软件将会越来越卡,因为内存小了,系统一直在找可以利用的空间,如果程序都在运行,没有了可用的空间,那么将会导致操作系统崩溃了。

下面分析一段简单的程序:

# include <stdio.h>
int main(void)
{
	int i;
	i = 10;
    i = 1;
	printf("value is :%d\n",i);
	return 0;
}

拿这个来举列子说明一下:

第一步:程序的运行首先向操作系统申请一片空间来保存硬盘中的程序,然后加载到内存中去;在这片空间中属于当前这个程序的空间,其他程序无法占用。

第二步:int i的时候,操作系统为其找到一片空间来存储数据,因为int是四个字节的,那么找到四个字节给这个变量i,那么变量i就和这块空间有了映射关系,但是此时因为变量因为还没有初始化,那么指向的都是垃圾值。我觉得这个时候,因为是先使用填充字来填充;然后i=10的时候,将10转化成二进制放入到这四个字节的位置上去。因为一个字节占用8位,那么四个字节是32位,那么10转换后的二进制将填充到这32位上去;

第三步:因为变量i和这块地址有映射关系,那么操作这块地址就相当于是操作变量i;

第四步:操作变量i,给变量i赋值,那么就相当于是将1转换成32位的二进制数值后,将原来的10转换的二进制数据覆盖了。所以此时变量i映射的这块地址里面的二进制数值是1所对应的二进制数值;

第五步:输出到控制台

第六步:程序运行完成,操作系统将回收这段程序占用的内存空间,但是这块内存空间中上面这段程序操作后的二进制值留在了里面,只是说将这块空间的状态从已占用设置成了未占用,下一个程序就可以使用了。所以又回到了上面说的,后来的程序需要使用,也需要进行初始化。

4、字节

字节是计算机硬件所能够访问的最小单位。上面已经介绍过程序中的数据类型,那么数据存储在计算机中是按照数据类型来决定占用多大的内存空间的。

但是上面又说了位,那么位和计算机中的字节又是什么关系呢?因为计算机无法识别我们人类识别的的字符已经数字,计算机采用的是利用二进制来存储数据。

那么位对应的就是二进制数据,比如0和1都代表了是一位,但是位无法来描述一个具体的数字或者是字符,只有字节表示的数据才能够准确说明是数字还是字符

而且位也无法说明整数中的正负关系,而且位也无法说明一个数字的大小。

所以考虑到这种情况,采用多个位来表示一个数字或者是字符。所以指定了规则,8位为一个字节。

计算机访问数据和存储数据的时候,能够保证了数据的完整性。

8位=1byte;1024byte=1kb;1024kb=1M;1024M=1G等等;

但是这里在实际生活中,并不是这样子来换算的。我去年买了个三星的固态硬盘,说是250G的,然后我拿到手,安装到电脑上的时候,发现才230多G

最终才知道厂家的换算单位和我们的换算单位是不一致的,我们是按照1024来算的,人家是按照1000来算的。

5、不同数据类型相互赋值

讲这个问题之前,首先来分析一波原码、反码、移码和补码的内容,这里针对的是整数而言的,不涉及到小数内容。

首先需要知道一个整数类型:short int ,int,long int的最大值和最小值。以前上学的时候这块都把我给我整哭了。

原码:符号码,也叫绝对值码;最高位是0,表示该数是一个正数;最高位是1,表示该数是一个负数;其余位表示的是该数的绝对值对应的二进制;

不使用原码来表示整数的原因:1、对于0来说,没有准确的表示;2、存在加减乘除四种运算(有人肯定会问,难道其他没有吗?),计算起来复杂,增加难度;

补码:用来解决整数的存储。

编码问题的核心所在就是在于数据在计算机中用什么样的01数据来进行表示。这才是核心!更多的不应该去关注其他问题。

再补充下如何将十进制的数字转换成二进制的01

对正整数转二进制:正整数不断除以2,直到商为0,余数倒叙排序;

对于负整数转二进制:先将负整数转换成正整数对应的二进制的01补码,然后将所有位取反,末尾+1,不够位数时(根据数据类型),左边补1;

例子:

int i = -3;

第一步:首先将-3转换成3,对应的二进制的补码:00000000 00000000 00000000 00000011

第二步:将所有位取反,11111111 11111111 11111111 11111100

第三步:末尾+1,11111111 11111111 11111111 11111101

第四步:不够32位(int类型占4个字节,32位),11111111 11111111 11111111 11111101

这个二进制数对应的十六进制,二进制转十六进制,00001111,范围是在015之间,那么即可表示16进制,所以每四位进制一个转换;

对应的二进制是:0XFFFFFFFD

在编译器中模拟一下:

# include <stdio.h>
int main(void)
{
	int j = -3;
	printf("%#X\n",j);
	return 0;
}

控制台输出:

0XFFFFFFFD

说明了上面的负整数对应的补码是正确的。

再来个例子说明:

int i = -1;

第一步:取出-1对应的绝对值1,然后补码是:00000000 00000000 00000000 00000001

第二步:全部取反,11111111 11111111 11111111 11111110

第三步:末尾+1,11111111 11111111 11111111 11111111

对应的十六进制是:0XFFFFFFFF

# include <stdio.h>
int main(void)
{
	int j = -1;
	printf("%#X\n",j);
	return 0;
}

控制台输出:

0XFFFFFFFF

再次对应上!

再来个例子,我想到了一个更好的方法,不用每次都写这么多的01代码了。

如:

int i = -62;

第一步:先找出绝对值62对应的补码,这次不写前面的0了,111110;

第二步:全部取反,000001

第三步:末尾+1,000010

第四步:前面补充1,11111111 11111111 11111111 11000010

对应的十六进制是:0XFFFFFFC2

程序运行:

# include <stdio.h>
int main(void)
{
	int j = -62;
	printf("%#X\n",j);
	return 0;
}

控制台输出结果:

0XFFFFFFC2

0的二进制都是0,所以就没有了正负之分了,看下面的计算出来的案例。

二进制转换到十进制:

对面的转换规则都是要基于补码:

规则:

如果首部第一个数字是0,正常算法;如果首部第一个数字是1,那么首先全部取反,末尾+1,然后得到的绝对值,前面加上一个负号即可;

如果全是0,那么十进制是0;

那么测试一下一个 short int类型所能够表示的数的范围:

0000 0000------------->0
0111 1111------------->1+2+4+8+16+32+64=127
1000 0000------------->取反,+1,加负号,0111 1111----->1000 0000 ----->-128
1111 1111------------->取反,+1,加负号,0000 0000----->0000 0001------>-1    

所以对于一个short int类型来说,最终的取值范围是-128~127

同样如此,可以计算出来int,long int对应的数据范围。

那么来分析一波:

char ch= 128;

因为char占用一个字节,那么就应该是8位,对应的二进制就是:1000 0000,可以看到对应的是一个负数。但是计算机存储的是补码,那么首先会将这个数字转换成补码来进行存储,取反后0111 1111,末尾+1,对应的补码就是:1000 0000,存储在对应的内存空间中去;

正常来说,我们写一个正整数,应该是01000 0000,但是计算机按照规则来进行存储,因为char占用一个字节,所以截取了后八位!但是这种写法在C语言中可以,在java中编译就出问题了,不会通过。

所以java编程稍微简单一点,能够较早的规避掉这些问题。而C语言是强大的,比较灵活的地方。允许程序员进行自定义的操作。

所以在long int 在赋值给short int的时候,就可能存在着对应的问题。

如:

long int 对应的一个二进制补码是:00000000 00000000 00000000 00000000 00000000 10000000 00000000 00000000 00000000,很明显是一个正整数
将其赋值给short int时,对应的是:10000000 00000000 00000000 00000000
这个值在进行存储的时候,转换成对应的补码存入进去,那么就是对应的十进制就是-2^31,但是在long int进行存储的不会出现问题,在这里出现了问题。    

定义了字符之后,然后将参考着ASCII码表,将存储的数据以十进制的格式将01代码进行输出。

但是在asci码表中没有对应的-128对应的字符,然后就直接输出了-128。

# include <stdio.h>
int main(void)
{
	char ch = 128;
	printf("%d\n",ch);
	return 0;
}

那么可以看到最终返回的就是对应的十进制的值;但是我们输入的明显是一个正整数,但是输出的时候是一个负数,这里就存在了溢出的问题

那么根据补码来操作对应的数据类型转换,就可以看见对应的操作。

6、ASCII码

ASCII码是一种规定,规定了字符在计算机中用哪个整数来进行存储。具体的可以参考具体的ASCII码表。

# include <stdio.h>
int main(void){
	char ch = 'a';
    // ch = 'ab';不可以,只能够存储一个字符
    // ch = "a"; 不可以,只能够存储一个字符,而不是字符串。对于这个双引号来说,C语言中是这样的:"a\0",\0作为字符串的结束的标志。
    // ch = ''; // 这样也是不可以的,因为是得有值的
	printf("%d\n",ch);
	return 0;
} 

控制台输出对应的值:

97

再看一种:

# include <stdio.h>
int main(void){
	char ch = ' '; // 这里什么都没有写,但是仍然是可以的,因为空格在ASCII码表中也有对应
	printf("%d\n",ch);
	return 0;
} 

查看控制台输出:

32

但是看一种特殊的:

# include <stdio.h>
int main(void){
    // 这里是有两个字符的。但是C语言中是扩展的字符,因为有些键盘上的有些特殊字符无法在计算机中表示,所以利用了这些特殊的字符拼接来表示
	char ch = '\n';
	printf("%d\n",ch);
	return 0;
}

控制台输出:

0

7、总结

内存占用和释放的含义

填充字的含义

数据类型和数据类型转换

变量为什么需要初始化

常量

ASCII码表

十进制<--->二进制--->其他进制转换

posted @ 2021-08-02 20:49  写的代码很烂  阅读(530)  评论(0编辑  收藏  举报