[C语言]数据是如何存储的(整型篇)?(一)

新手小白写的第一篇博客,记录自己的学习过程,希望我写的文章能给你们帮助。如果有不足的地方欢迎在评论区留言交流。

  • 首先我要介绍数据分为哪些类型。
  • 其次介绍整型在数据中是怎样存储的。
  • 最后介绍浮点型在内存中的存储。

一,数据类型

基本的内置类型:

char //字符数据类型 ,大小是1Bytes。
short //短整型,大小是2Bytes。
int //整形 ,大小是4Bytes。
long //长整型 ,对于32位机器,大小是4Bytes;对于64位机器,大小是8Bytes.
long long //更长的整形,大小是8Bytes.
float //单精度浮点数 ,大小是4Bytes。
double //双精度浮点数 ,大小是8Bytes。
c语言中没有字符串类型string

1.1数据类型归类

整型家族,浮点型家族,构造类型(也叫自定义类型),指针类型和空类型。

整型家族:

char
unsigned char
signed char

short
unsigned short
signed short

int
unsigned int
signed int

long
unsigned long
signed long

long long
unsigned long long
signed long long

注意:

1,除了char类型外,其他的基本内置类型都相当于有符号类型。如:int = signed int,signed通常不写出来。
2,对于char类型,char是signed char还是unsigned char,标准是未定义的,取决于编译器的实现。
3,对于有符号类型,其数据二进制位的最高位是符号位,0表示正,1表示负,剩余的都是有效位;而对于无符号类型,其数据二进制位没有符号位,全是有效位。
4, char之所以被归类为整型家族,是因为char在内存中存储的是ASCII值,如果以 %c 输出,会根据 ASCII码表转换成对应的字符,如果以 %d 输出,那么还是整数。


浮点型家族:

float

double

只要表示小数就可以用浮点型。
float精度低,存储的数值范围较小,一般小数点后6位;double精度高,存储的数据范围更大,一般小数点后15位。


构造类型:

  1. 数组类型:
    int arr1[5]; //数据类型为:int [5]
    int arr2[10]; //数据类型为:int [10]
    char arr3[5]; //数据类型为:char [5]
  1. 结构体类型struct
  1. 枚举类型enum
  1. 联合类型union

指针类型:

int *
char *
float*
void*


空类型

void

void可用于函数的传参,当函数不需要传参的时候,可以传void;void可用于函数的返回值,当函数不返回值的时候,函数的返回类型为void。void还可以用于指针类型。

二、整型在内存中的存储

一个变量的创建是要在内存中开辟空间的,而空间的大小是根据不同的数据类型决定的。
而数据则存储于变量之中。当数据的长度小于变量的长度时,在存储过程中,数据的长度会提升为变量的类型的长度,也就是发生整型提升;当数据的长度大于变量的长度时,在存储过程中,数据的长度会截断为变量的类型的长度,也就是发生整型截断;另外,当做算数运算时,表达式中的长度可能小于int长度的整型值,都必须先转化为int或者unsigned int,然后才能被CPU运算。

当数据的长度小于变量的长度时,如下图:数据补齐内存低地址,高地址位全补0。

image.png image.png

当数据的长度大于变量的长度时,如下图:数据低位补齐内存低地址,数据其他位被截断未被存储!

image.png image.png

当做算数运算时,如下图:

image.png image.png

那么整型提升是具体如何操作的呢?

对于有符号的类型:
如:char a1 = -1;//11111111 a1的二进制补码(内存中存储的是补码)
高位补充符号位,即1,提升之后的结果是://11111111111111111111111111111111
又如:char a2 = 1;//00000001 a2的二进制补码
高位补充符号位,即0,提升之后的结果是://00000000000000000000000000000001
对于无符号类型: 高位直接补0即可。

下面我来介绍整形在内存中的大小端存储。

2.1大小端存储

什么是大小端存储?

大端存储:是指数据的低位存储在内存高地址,数据的高位存储在内存低地址;
小端存储:是指数据的低位存储在内存低地址,数据的高位存储在内存高地址。

以下以0x11223344的存储为例,来探讨大小端是如何存储的:
在这里插入图片描述

我们很好的发现:小端存储是把一个数据的低位存放在低地址,高位存放在高地址;大端存储是把一个数据的低位存放在高地址,高位存放在低地址。
不同的编译器存储模式是不一样的。我们常用的X86结构是小端存储,运用于嵌入式开发的KEIL C51则是大端存储。很多的ARM, DSP都为小端存储。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

练习:设计一个程序来判断当前机器的字节序。

思路:选一个较为简单的数来实现,如:int a = 1; 其二进制表达为:0x00 00 00 01
1,通过取地址,若首地址内容是1则为小端存放;若首地址内容是0则为大端存放。
2,注意:取出a的地址类型为int*,解引用*a访问4个字节,但是我们需要访问1个字节,此时需要强制类型转换为char*

int main()
{
	int a = 1;
	if(*((char*)&a) == 1)
		printf("小端存储\n");
	else
		printf("大端存储\n");
	return 0;
}

执行结果:小端存储

2.2整型存储的一些练习题

以下再给几个练习来巩固对整型存储的学习:

//输出什么?
#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存储到字符型a中会发生整形截断:

//-1
//10000000 0000000 0000000 00000001–原码
//111111111 11111111 11111111 111111110–反码
//111111111 11111111 11111111 111111111–补码
// 11111111–截断后的补码存储到 a

2.%d-以十进制的形式打印有符号的整型整数要整型提升,高位补符号位1:
//1111111 11111111 11111111 1111111–a发生整型提升
//1111111 11111111 11111111 1111110–反码
//1000000 00000000 00000000 0000001–>原码 -1

3.由于char和signed char是一样的,所以b的处理和a一样。
4.对于unsigned char的处理,在整型提升的时候高位全补0:

//0000000 00000000 00000000 1111111 --整型提升
//0000000 00000000 00000000 1111111
//0000000 00000000 00000000 1111111–>正数的原反补都相同
5.最终打印的结果就是经过原码换算的结果:a = -1, b = -1, c = 255

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

解题步骤:
1.首先普通整型-128存储到字符型a中会发生整形截断:

//-128
//10000000 00000000 00000000 10000000–原码
//11111111 11111111 11111111 01111111–反码
//11111111 11111111 11111111 10000000–补码
// 10000000–截断后的补码

2.%u-以十进制的形式打印无符号的整型整数要整型提升,高位补符号位1:
//11111111 11111111 11111111 10000000–整型提升–补码
//11111111 11111111 11111111 10000000–原码–正数的原码,反码和补码都相等
3.经过计算得十进制为: 4294967168

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

解题步骤:
1.首先普通整型128存储到字符型a中会发生整形截断:

//128
//00000000 00000000 00000000 10000000–原码,反码,补码
// 10000000–截断后的补码
2.%u-以十进制的形式打印无符号的整型整数要整型提升,高位补符号位1:
//11111111 11111111 11111111 10000000–整型提升–补码
//11111111 11111111 11111111 10000000–原码–正数的原码,反码和补码都相等
3.经过计算得十进制为: 4294967168

int i = -20;
unsigned int j = 10;
printf("%d\n", i + j);
//按照补码的形式进行运算,最后格式化成为有符号整数(发生算术转换)

解题步骤:
1.-20和10只需要计算出补码即可,无需进行整型截断与提升:

//-20
//1000000000000000000000000010100
//1111111111111111111111111101011
//1111111111111111111111111101100–补码
//10
//0000000000000000000000000001010–补码
//i + j
//1111111111111111111111111110110–补码
//1111111111111111111111111110101–反码
//1000000000000000000000000001010–原码
2.结果: -10

unsigned int i;
for(i = 9; i >= 0; i--)
{
	printf("%u\n",i);
}

解题步骤:
1.注意unsigned int的范围: i ∈[0, 2^32 - 1].
2.前10个数的打印都没问题,分别是9,8,7,6,5,4,3,2,1,0。当0 - 1时,其结果应该是i的上限,即2^32 - 1=4,294,967,295,这是由于i的范围决定的,而且4,294,967,295满足i >=0, 之后一直死循环打印。
还可以这样理解(写出-1的补码):
//0 - 1 = -1
//1000000000000000000000000000001–原码
//1111111111111111111111111111110–反码
//1111111111111111111111111111111–补码–>4,294,967,295

int main()
{
	char a[1000];
	int i;
	for(i = 0; i<1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d", strlen(a));
	return 0;
}

解题步骤:
1.注意char的范围: i ∈[-128,-1]∪[0, 127].
2.前256项的打印结果依次是:-1 -2 -3 -4 …-128 127 126 125 … 3 2 1 0 ,-128 - 1的值一定是127.
3.strlen()这个函数求字符串长度求的是'\0'前面的字符的个数
4.因此,打印的结果是255.

#include <stdio.h>
unsigned char i = 0;
int main()
{
	for(i = 0;i<=255;i++)
	{
		printf("hello world\n");
	}
	return 0;
}

解题步骤:
1.注意unsigned char的范围: i ∈[0, 255].
2.i的执行结果:0,1,2,3,…,254,255,0,1,2,3,…,254,255,…
3.所以最终会死循环打印hello world

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值