C语言数据在内存中的存储详解

目录
  • 文章摘要
  • 一、C语言的数据类型
    • 数据类型基本分为:
  • 二、隐式类型转换
    • 1.什么是隐式类型转换
    • 2.整型提升
    • 3.类型转换
  • 三、机器大小端
    • 1.什么是大小端
    • 2.大小端在截断的应用
    • 3.判断当前机器的字节序是大端还是小端
  • 四、整型在内存中的存储
    • 1.原码、反码、补码
    • 2.举例实践整型数据在内存的存储
  • 总结

文章摘要

本文通过内存底层原理,帮你透彻了解数据存储进内存与从内存中读取的区别以及不同数据类型下数据计算、赋值的变化情况
要透彻理解这些,必须知道隐式类型转换以及机器大小端的概念,本文会对此做简单介绍(这两个概念对C语言数据的深度理解非常重要)

一、C语言的数据类型

数据类型基本分为:

1.整性

char  //字符本质上是整型,只是char类型值截断开辟一个字节
	unsigned char
	signed char
short  //2字节
	unsigned short [int]
	signed short [int]
int  //4字节
	unsigned int
	signed int
long  //4字节
	unsigned long [int]
	signed long [int]

2.浮点型

float
double

3.构造类型

数组类型
结构体类型 struct
枚举类型 enum
联合类型 union

4.指针类型

int* pi;
char* pc;
float* pf;
void* pv;
...

5.空类型

void

二、隐式类型转换

1.什么是隐式类型转换

在C语言中,隐式类型转换是编译器自发的行为,它往往是从小到大的转换,在数据类型上表现是少字节数据类型,转换成多字节数据类型,保证数据的完整性;(面向对象语言也有该概念,并且对于类也会有隐式类型转换)一般来说,隐式类型转换大体分为两种:整性提升类型转换

2.整型提升

1.定义: C的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的 字符(char类型1字节) 和 短整型(short int类型2字节) 操作数在使用之前被转换为 普通整型(int类型4字节) ,这种转换称为整型提升。

通俗来说:无论数据类型是否为 char 、short int 、…,其在读取到CPU进行计算时,都会先通过整性提升到32位计算,而结算结果的读取位数取决于读取的数据类型,若为char类型,则截断取8位(bit).

【这里注意:通常CPU在计算时,用的数据是源码已翻译后的补码来计算】

2.整性提升是按照变量的数据类型(指自身类型,而不是数值类型)的符号位来提升

//eg1.负数的整性提升
char a = -1;  //char类型默认为有符号类型
//其二进制源码为:1 000 0001
//        补码为:1 111 1111
//整性提升时,由于8bit的char类型数据中符号位为1;
//故提升为32位后 11111111 11111111 11111111 11111111;(补码)

//eg2.正数的整性提升
char a = 1;
//其二进制源码为:0 000 0001
//   补码=源码为:0 000 0001
//整性提升时,由于8bit的char类型数据中符号位为0;
//故提升为32位后 00000000 00000000 00000000 00000001;(补码)

3.截断的具体体现:

//eg3.
char c = -129;
printf("%d",c);

结果为:127

原因是:-129源码为:1000 0000 0000 0000 0000 0000 1000 0001

在内存中的补码为:1111 1111 1111 1111 1111 1111 0111 1111

而字符变量c 只截断8bit位 即c变量保存的是:0111 1111(补码)

输出d%位整型,且符号位为0

整型提升为0000 0000 0000 0000 0000 0000 0111 1111(补码)

转为源码即为127

注意这里的截断原则与机器大小端有关,且截断是在内存上对补码进行操作

3.类型转换

1.概念:操作符两边的操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行;而这种转换就是类型转换(编译器自发)

2.从下至上,自动转换

long double
double
float
unsigned long int
long int
unsigned int
int

3.【注意】这种类型转换只是建立在运算操作符之间,不然会出现不合理问题

eg4.
float f = 3.14;
int num = f;//隐式转换,会有精度丢失

赋值情况下导致在高位的float类型转为低位的int类型,导致精度丢失

三、机器大小端

1.什么是大小端

小端(存储)模式,是指数据的底位(低权值)保存在内存的底地址中,而数据的高位(高权值),保存在内存高地址中;

大多数机器都采用小端模式

大端(存储)模式,是指数据的底位(低权值)保存在内存高的地址中,而数据的高位,保存在内存低地址中;

2.大小端在截断的应用

上文的eg3.中出现了截断,即字符c截断整型数值-129

//eg3.
char c = -129;

我们将代码中的整型a变量在内存的地址储存数据显示出来,从内存地址可以看出,序列从高到低递增
a:补码为 1111 1111 1111 1111 1111 1111 0111 1111

转为16进制后即为 ff ff ff 7f;(权值左边最高,右边最低

再将字符变量c内存的地址储存数据显示出来,可以看出,由于char类型只有一字节,会优先从四字节a中截断地址最低的一字节

由图看出它截断了低地址里的数据7f,而7f也是低权值。
故,在vs2013中,采用的是小端原则

3.判断当前机器的字节序是大端还是小端

#include<stdio.h>
#include<Windows.h>
#pragma warning(disable:4996)

int check_sys()
{
	int i = 1;
	return (*(char*)&i);//注意,发生数据类型转换
}

int main()
{
	int ret = check_sys();
	if (ret)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}

	system("pause");
	return 0;
}

【注意】(*(char*)&i);

这里是对指针的解引用时,从内存所取的字节大小由其指向的数据类型决定。 说白了就是 i 的地址从int *被强转为char *,再解引用时,其指向的数据类型从int变为char,因此显示的数据会发生截断;

由上面的截断方式我们可以知道,1在内存是以32位存储的,按一字节来说,其高权值位为0、低权值位为1.故可以通过return传参的1或0判断大小端。

四、整型在内存中的存储

1.原码、反码、补码

一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1.

比如,十进制中的数 +3 ,计算机字长为8位,转换成二进制就是00000011;

如果是 -3 ,就是 10000011

在C语言中,整型在计算机的储存情况是按原反补的规则储存,即对于整型来说,数据存放在内存中其实是补码。

计算机采用这种规则可以使数据运算时的+ - * / 运算都通过加法解决,这样设计的计算机只需设计出加法模块,大大节省成本。

具体规则如下:

1.正数

正数的原、反、补码都相同,与原码一样

2.负数

原码:该数的机器数,最高位为符号位

反码:原码除符号位不变,其余位按位取反

补码:反码+1

2.举例实践整型数据在内存的存储

//例1.尝试判断输出结果是什么
int main()
{
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d,b=%d,c=%d\n", a, b, c);

	system("pause");
	return 0;
}

结果:

例1解析:

-1在内存的补码:1111 1111 1111 1111 1111 1111 1111 1111

char a 、signed char b 、unsigned char c 存放时发生截断,其在内存的补码均为:1111 1111

但是三位在以%d(整型)输出时,会发生整型提升,由原来的8位整型提升到32位,而整型提升时高位补0还是补1需看数据自身类型(有符号类型补符号位,无符号类型直接补0

char a 与 signed char b 均属于有符号型,且符号位为1,补24位1

内存数值为:1111 1111 1111 1111 1111 1111 1111 1111;输出%d时反向推回原码,答案即为 -1

unsigned char c 属于无符号型,补24位0

内存数值为:0000 0000 0000 0000 0000 0000 1111 1111;输出%d时反向推回原码,答案即为 255

//例2.尝试判断输出结果是什么
int main()
{
	char a = 128;
	char b = -128;
	printf("a=%u,b=%u\n", a,b);

	system("pause");
	return 0;
}

结果:

例2解析:

128在内存的补码:0000 0000 0000 0000 0000 0000 1000 0000

-128的内存补码: 1111 1111 1111 1111 1111 1111 1000 0000

char a 、char b 存放时发生截断,其在内存的补码均为:1000 0000

%u(无符号整型)输出时,会发生整型提升,由原来的8位整型提升到32位

char a 与 char b 均属于有符号型,且符号位为1,补24位1

内存数值均为:1111 1111 1111 1111 1111 1111 1000 0000;输出%u时反码直接当原码,

答案即为 :

//例3.尝试判断输出结果是什么
int main()
{
	int i = -20;
	unsigned int j = 10;
	printf("i+j = %d\n", i + j);

	system("pause");
	return 0;
}

结果:

例3解析:

-20在内存的补码:1111 1111 1111 1111 1111 1111 1110 1100

10在内存的补码:0000 0000 0000 0000 0000 0000 0000 1010

int i 与 unsigned int j 都是四字节类型变量故存储时不会发生截断,

但 ·i + j = 表达式会发生类型转换,int 会自动转换为 unsigned int 类型计算

CPU中将两变量补码进行相加得到:1111 1111 1111 1111 1111 1111 1111 0110

计算结果以%d(整型)输出,反向推回原码:1000 0000 0000 0000 0000 0000 0000 1010

答案即为 -10

//例4.尝试判断输出结果是什么
int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n", i);
	}

	system("pause");
	return 0;
}

结果:

例4解析:

由于 i 变量时 unsinged int 类型,因此其无符号位,

且 ·i >= 0 表达式会发生类型转换,int 0 会自动转换为 unsigned int 类型计算

故其比较结果永远为真,因为无符号类型第32bit位(符号位)永远为0

for 循环条件永远满族,答案即为死循环

//例5.尝试判断输出结果是什么
int main()
{
	char a[1000];
	int i;
	for (i = 0; i <1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d\n", strlen(a));

	system("pause");
	return 0;
}

结果:

例5解析:

char a[1000]数组的每一位元素都是1字节的 char 类型,有字符位,故其保存的数值范围:[-128,127];

-1-i 范围从 -1到 -1000,但在循环体 a[i] = -1 - i中每次赋值都会发生截断,由下图可知,char类型保存的数值依次递减时,-1 继续减到 -128 ,128 减一位到 127,127 继续减到0,0再减一位到 -1,继续下一轮循环;

而该题的输出时数组字符长度,strlen遇 ‘\0'(等价于数值0),而在初始化后的char a[1000]数组中,数值第一次出现0在a[255];

故答案为255

//例6.尝试判断输出结果是什么
int main()
{
	unsigned char i = 0;
	for (i = 0; i <= 255; i++)
	{
		printf("%d ", i);
		Sleep(30);
	}

	system("pause");
	return 0;
}

结果:0-255无限循环

例6解析:

由例5解析的图可知:unsigned char 类型的变量 i 的取值范围:[-128,127],永远小于255;

而%d输出时,无符号类型直接整型提升补24位0:

0000 0000 (0)转为 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000(补),补码转原码:符号位为0,原码与补码一样,值为0;

1000 0000(-128)转为0000 0000 0000 0000 0000 0000 0000 0000 1000 0000(补),补码转原码:1000 0000 0000 0000 0000 0000 1000 0000,值为128;

0111 1111(127)转为0000 0000 0000 0000 0000 0000 0000 0111 1111(补),补码转原码:符号位为0,原码与补码一样,值为127

由此可知,无符号字符类型变量整型提升后再%d输出没有负数

故答案为:0-255循环

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • C语言编程数据在内存中的存储详解

    目录 变量在计算机中有三种表示方式,原码反码,补码 原码 反码 补码 总结一下 浮点数在内存的储存 C语言中,有几种基本内置类型. int unsigned int signed int char unsigned char signed char long unsigned long signed long float double 在内存中创建变量,会在内存中开辟空间,并为其赋值. int a=10; 在计算机中,所有数据都是以二进制的形式存储在内存中. 变量在计算机中有三种表示方式,原码反

  • 关于C语言中数据在内存中的存储详解

    前言 1. 数据类型详细介绍 2. 整形在内存中的存储:原码.反码.补码 3. 大小端字节序介绍及判断 4. 浮点型在内存中的存储解析 一.数据类型介绍 1.类型的基本归类 1.整形家族 char unsigned char signed char short unsigned short [int] signed short [int] int unsigned int signed int long unsigned long [int] signed long [int] 2.浮点型家族

  • C语言动态内存函数详解

    目录 动态开辟空间的原因 1.malloc函数 2.free函数 3.calloc函数 4.realloc函数 总结 动态开辟空间的原因 静态开辟空间是固定的,数组在申明的时候必须指定数组的长度,在编译的时候分配内存,但是我们在实际情况中对于内存空间的需求,不仅仅是上述的情况,有时候我们需要的空间只有在运行之后才能知道,所以需要开辟一个动态内存空间,满足更多需求. 1.malloc函数 void* malloc (size_t size); malloc函数是向内存申请一块连续的空间,并返回指向

  • C语言编程C++动态内存分配示例讲解

    目录 动态内存管理 为什么存在动态内存分配 动态内存函数的介绍 malloc申请空间和free释放空间 有借有还 free释放内存 calloc申请内存 realloc调整动态内存的大小 realloc使用的注意事项 当然realloc也可以直接开辟空间 常见的动态内存错误 1.对NULL指针的解引用操作 2.对动态开辟空间的越界访问 3.对非动态开辟内存使用free释放 4.使用free释放一块动态内存开辟的一部分 5.对同一块动态内存多次释放 6.动态开辟内存忘记释放(内存泄漏) 几个面试题

  • C语言 数据存储方式知识点详解

    C语言 数据存储方式 一.源码 一个数的原码(原始的二进制码)有如下特点: 最高位做为符号位,0表示正,为1表示负 其它数值部分就是数值本身绝对值的二进制数 负数的原码是在其绝对值的基础上,最高位变为1 下面数值以1字节的大小描述: 十进制数 原码 +15 0000 1111 -15 1000 1111 +0 0000 0000 -0 1000 0000 注:原码表示法简单易懂,与带符号数本身转换方便,只要符号还原即可,但当两个正数相减或不同符号数相加时,必须比较两个数哪个绝对值大,才能决定谁减

  • C语言数据(整数、浮点数)在内存中的存储

    本篇主要讨论:整数.浮点数在内存中是怎么保存的! 数据类型详细介绍 在前面C语言基础概览中,已经提到过了基本的C语言内置类型,但C语言的数据类型有无数种~ 但是可以把这些类型分为几个大类: 类型的归类: 存整数的 char,short,int,long,long long及所配套的unsigned,int*,int[]- 2.存浮点数的 float,double,float[]- 结构体(结构体在内存中的存储后面在进行讨论~) 整数在内存种的存储: 1.字节序 2.补码 内存窗口 调试模式下的内

  • C语言数据在内存中的存储详解

    目录 文章摘要 一.C语言的数据类型 数据类型基本分为: 二.隐式类型转换 1.什么是隐式类型转换 2.整型提升 3.类型转换 三.机器大小端 1.什么是大小端 2.大小端在截断的应用 3.判断当前机器的字节序是大端还是小端 四.整型在内存中的存储 1.原码.反码.补码 2.举例实践整型数据在内存的存储 总结 文章摘要 本文通过内存底层原理,帮你透彻了解数据存储进内存与从内存中读取的区别以及不同数据类型下数据计算.赋值的变化情况 要透彻理解这些,必须知道隐式类型转换以及机器大小端的概念,本文会对

  • C语言数据在内存中的存储流程深入分析

    目录 前言 类型的基本分类 整型 浮点数 自定义类型 整型在内存中的存储 原码.反码.补码 大端和小端 如何判断编译器是大端还是小端 浮点数在内存中的存储 总结 前言 C语言中有char.short.int.long.long long.float和doubole这些数据类型.这些数据类型也叫内置类型. 所占存储空间的大小: 数据类型 所占存储空间的大小 char 1个字节 int 4个字节 short 4个字节 long 4个字节 long long 32位平台下占4个字节 ,64位平台下占8

  • C++中整形与浮点型如何在内存中的存储详解

    目录 1 数据类型 1.1 类型的基本归类 2 整形在内存中的存储 2.1 二进制的三种形式 2.2 大小端字的介绍 3 浮点数在内存中的存储 3.1 浮点数存储规则 1 数据类型 前面我们已经知道了基本的内置类型: 类型的意义: 1. 使用这个类型开辟内存空间的大小(大小决定了使用范围). 2. 如何看待内存空间的视角. 1.1 类型的基本归类 整形家族: char unsigned char signed char short unsigned short [int] signed shor

  • C++浮点数在内存中的存储详解

    目录 前言: 浮点数的表示形式 浮点数存储模型 有效数字M 指数E 例题讲解 总结 前言: 我们在码代码的时候,经常遇到过以整数形式存入,浮点数形式输出:或者浮点数形式存入整数形式输出.输出的结果往往让人意想不到,那么,为什么会发生这样的变化,又是什么导致发生变化,接下来,就让我们从存储内部结构出发,带你深度解刨! 我们以一个例子来说明一切 #include<stdio.h> int main() { int n = 9; float *pFloat = (float *)&n; pr

  • C语言详细分析不同类型数据在内存中的存储

    目录 数据类型的介绍 类型的基本归类 整形在内存中的存储 大小端介绍 一道笔试题 浮点数在内存中的存储 浮点数存储规则 剖析题目 数据类型的介绍 在我们之前的学习当中我们已经介绍了基本的内置类型 char 字符数据类型 short 短整型 int 整形 long 长整型 long long 更长的整形 float 单精度浮点数 double 双精度浮点数 这些类型的意义是: 1.使用这个类型开辟内存空间的大小,大小决定了使用范围 2.如何看待内存空间的视角. 类型的基本归类 整形 整形中分为有符

  • C++浅析数据在内存中如何存储

    目录 一.数据类型 二.原码反码补码 三.大小端 整型提升 一.数据类型 数据类型有7种: char            字符型     short          短整型    int               整型   long            长整型   long long    更长整型   float            单精度浮点数    double        双精度浮点数 二.原码反码补码 计算机中的整数有三种2进制表示方法,即原码.反码和补码. 三种表示方法均

  • C语言数据结构中树与森林专项详解

    目录 树的存储结构 树的逻辑结构 双亲表示法(顺序存储) 孩字表示法(顺序+链式存储) 孩子兄弟表示法(链式存储) 森林 树的遍历 树的先根遍历(深度优先遍历) 树的后根遍历(树的深度优先遍历) 树的层序遍历(广度优先遍历) 森林的遍历 先序遍历森林 中序遍历森林 树的存储结构 树的逻辑结构 树是n(n≥0)个结点的有限集合,n=0时,称为空树,这是一种特殊情况.在任意--棵非空树中应满足: 1)有且仅有一个特定的称为根的结点. 2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集合T

  • Android中的存储详解

    目录 1.存储在App内部 2.SD卡外部存储 3.SharedPreferences存储 4.使用SQLite数据库存储 4.1 自己完成一个BaseDao类 4.2 使用Google写的API处理 4.3 事务使用 总结 1.存储在App内部 最简单的一种.在尝试过程中发现,手机中很多文件夹都没有权限读写.我们可以将我们需要写的文件存放到App中的files文件夹中,当然我们有权限在整个App中读写文件 可以通过API获取一个file对象,这里的this就是MainActivity类 //

随机推荐