【C语言进阶】纳尼?这样学数据在内存中的存储竟然如此简单(1)?

简介: 【C语言进阶】纳尼?这样学数据在内存中的存储竟然如此简单(1)?

前言

今天更新一篇关于数据在内存中存储的内容,博主在学到这个地方的时候,曾经绞尽脑汁百思不得其解,希望今天的内容能帮助你加深对数据在内存中存储的理解


一.数据类型介绍

1.基本内置类型

在C语言中,分为以下几种内置类型

char型 字符数据类型

short型 短整型

int型 整型

long 长整型


long long 更长的整型

float 单精度浮点数

double 双精度浮点数

注意:


上面的数据类型单位都是字节,其中long的长度>=int的长度(根据操作系统的不同有所不同,要根据实际系统具体分析)

2.整型

整型家族有以下几位成员:


[signed] short 有符号短整型

unsigned short 无符号 短整型

[signed] int 有符号基本整型

unsigned [int] 无符号 基本整型

[signed] long 有符号长整型

unsigned long 无符号 长整型

[signed] long long 有符号双长整型(C99新增)

unsigned long long 无符号 双长整型(C99新增)

[signed]char 有符号字符型

[unsigned]char 无符号字符型


字符储存的时候,存储的是ASCLL码值,是整型,所以也可以归为整型一族


3.浮点型

float 单精度浮点型

double 双精度浮点型

4.指针类型

int *p int型指针

char *p char型指针

float *p float型指针

void *p 空类型指针

结构体指针

5.空类型

void表示空(无)类型

通常用于函数的返回类型,函数的参数,指针类型

6.自定义类型

即我们自己定义的类型,比如


数组类型

结构体类型 struct

枚举类型 enum

联合类型 union

二.整型数据在内存中的存储

1.基本存储形式

整型在内存中是以二进制的形式存储的,而内存中存储的是补码,并且是倒着存的。

下面来具体解释一下上面这段话。


(1)二进制表示的三种形式

二进制在内存中表示形式分别是原码,反码,补码。

以下面的代码说明


int main()
{
  int num  = 10;//创建一个整型变量,叫num,这时num向内存申请4个字节来存放数据
  //4个字节-32比特位
  //00000000000000000000000000001010-原码
  //00000000000000000000000000001010-反码
  //00000000000000000000000000001010-补码
  int num2 = -10;//
  //10000000000000000000000000001010 - 原码
  //11111111111111111111111111110101 - 反码
  //11111111111111111111111111110110 - 补码
  return 0;
}

十进制数据的二进制表现形式就是原码,原码最左边的一个数字就是符号位,0为正,1为负。

对于正数而言,原码反码补码三者相同,对于负数而言,反码为原码符号位不变,其他位取反(1变为0,0变为1)

对于负数而言,补码为反码+1。


2.大(小)端字节序存储

(1)大(小)端字节序存储的定义

什么是大(小)端字节序存储?

其中,字节序是以字节为单位,讨论存储顺序的。

大端存储:是将数据的低位字节放到高地址处,高位字节放到低地址处。

小端存储:是将数据的低位字节放到低地址处,高位字节放到高地址处。


数据的高位是数据的左边位置的数,数据的低位是数据右边位置的数,数据的高位和低位又称高字节和低字节。


例如 1234中,1是高位,4是低位。

为了便于管理存储地址,给地址进行编号,值较大的地址是高地址,值较小的地址是低地址。


例如 1234 4321 其中4321为高地址,1234为低地址。

大端存储和小端存储记忆时,可以理解为判断低位字节放到大端还是小端?大端存储就是将低位字节放到高地址,小端就是将低位字节放到低地址。


(2)写个程序来判断当前系统是大端还是小端

代码如下:

int main()
{
  int n = 1; // 0x0000 0001
  //如果是大端 低位字节放到高地址,高位字节放到低地址
  //00 00 00 01
  //如果是小端 高位字节放到高地址,低位字节放到低地址
  //01 00 00 00
  char* p = (char*)&n; //取n的地址,把它强制类型转换为char*类型,目的是让字符指针只读取第1个字节。
  //如果是大端存储,则p读取的值是0
  //如果是小端存储,则p读取的值是1
  if (*p == 1)
    printf("小端\n");
  else
    printf("大端\n");
  return 0;
}

运行结果

dede94c2b5c844bebea72b050dbbe0be.png


三.在编程中因为不理解数据在内存中的存储而出现的几种常见错误

1.因不注意是有符号还是无符号数而出现的错误

试分析以下代码

#include <windows.h>
int main()
{
  unsigned int i;
  for(i = 9; i >= 0; i--)
  {
    printf("%u\n", i);//以十进制的形式打印i
    Sleep(1000);//使程序睡眠,单位是毫秒
  }
  return 0;
}

运行结果

57da924ff1b7419498c6d9a571a48f94.png


为什么会出现以上结果呢?


1.注意上面的代码中,i的数据类型是无符号整型,也就是说,在i中是不存在负数的。

当i<0时,后面的运行结果是什么?


2.由于i没有符号位,当i<0时,输出的是此时二进制补码的十进制数

//10000000 00000000 00000000 00000001 -1原码
//11111111 11111111 11111111 11111110  反码
//11111111 11111111 11111111 11111111  补码

ca26b0273d7d4df59813131274fb937f.png


例二

#include <stdio.h>
int main()
{
  char a = -1;
  //10000000000000000000000000000001
  //11111111111111111111111111111110
  //11111111111111111111111111111111-截断(语法规定,在截断时从右往左截取。char类型只占一个字节,即八个比特位)
  //11111111 a
  //11111111111111111111111111111111 补码  有符号数在整型提升时前面补1
  //11111111111111111111111111111110 反码
  //10000000000000000000000000000001-->输出时为原码  -1
  signed char b = -1;//同a
  unsigned char c = -1;
  //10000000000000000000000000000001
  //11111111111111111111111111111110
  //11111111111111111111111111111111
  //11111111 c
  //00000000000000000000000011111111 无符号数在整型提升时补0
  //
  printf("a=%d,b=%d,c=%d", a, b, c);
  //%d - 十进制的形式打印有符号整型整数
  return 0;
}

结果

cf4a85313c8845c6afaeee518c206394.png

2.因不注意整型数据大小导致的错误

(1)char型

在举例之前,先简单解释一下char类型范围为什么是-128-127.这对理解下面的例子能起很大帮助 首先要注意的一点是符号位除了是符号位以外,也是数值位

//对char型范围是-128-127的说明
//我们先将0-127这些十进制数用2进制表示出来
//0000 0000 ->0
//0000 0001 ->1
//......
//0111 1111 ->127
//对于一个char整型来说,在内存中占一个字节即八个比特位,且首位为符号位
//1000 0000 ->-128
//1000 0001 ->-127
//......
//1111 1111 ->-1此时再加1,符号为正,符号位变为0
//0000 0000 ->0依次循环
>

好了,有了上面的知识,我们可以举出以下例子

- #include <string.h>
int main()
{
  char a[1000];
  int i;
  for (i = 0; i < 1000; i++)
  {
    a[i] = -1 - i;
  }
  printf("%d", strlen(a));//stelen用来求字符串长度,其中遇到"\0"停止,"\0"的ASCLL码值为0
  return 0;
}
- 

当看到上面这段代码,很多人想当然的认为该字符串的长度为1000,但当我们仔细阅读该代码时,才会发现此时字符串的真正长度

该数组是从-1开始的,随着i的增加而不断减小。而根据我上述对char型数据的范围是-128-127的说明可知认为读出的长度为1000可谓大错特错。下面是正确的分析: 1.当i的值减至-128时,下一次再减会使符号位也发生变化(即从1000 0000 (-128)变为0111 1111 (127))。而此时接着从127往下递减,当减至0时,由于"\0"的ASCLL码值为0,读取停止,此时字符串的正确长度应该为255(127+128)。


结果展示

5a10f14099cb4a0282fb5f6e1ef54619.png

(2)unsigned char型

unsinged char型数据大小的范围是0-255,与上述char型大小范围分析相同,这里不再缀叙。


示例代码如下:

#include <stdio.h>
unsigned char i = 0;//0~255
int main()
{
  for (i = 0; i <= 255; i++)
  {
    printf("hello world\n");
  }
  return 0;
}
//对于该代码而言,i<=255永远为真,该代码陷入死循环。


总结

以上就是今天要讲的所有内容啦,以后还会继续更新有关其他类型的数据在内存中如果存储的内容的。 因个人水平不足导致文章中出现的错误欢迎在评论区或者私信我指出,如果有什么问题想要与我讨论也欢迎评论或者私信我哦,博主看到后会第一时间回复的。


目录
相关文章
|
1天前
|
程序员 编译器 C语言
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(下)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
5 0
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(下)
|
1天前
|
C语言 C++
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(中)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
9 0
|
1天前
|
编译器 数据库 C语言
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(上)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
9 0
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(上)
|
1天前
|
C语言 C++
C语言进阶⑭(内存函数_以字节操作)momcpy+mommove+memcmp+memset
C语言进阶⑭(内存函数_以字节操作)momcpy+mommove+memcmp+memset
6 0
|
1天前
|
存储 C语言
C语言进阶⑩(数据的存储)数据类型_介绍+存储_大小端(知识点+笔试题)(下)
C语言进阶⑩(数据的存储)数据类型_介绍+存储_大小端(知识点+笔试题)
9 0
|
1天前
|
存储 编译器 C语言
C语言进阶⑩(数据的存储)数据类型_介绍+存储_大小端(知识点+笔试题)(中)
C语言进阶⑩(数据的存储)数据类型_介绍+存储_大小端(知识点+笔试题)
9 0
|
1天前
|
存储 小程序 编译器
C语言进阶⑩(数据的存储)数据类型_介绍+存储_大小端(知识点+笔试题)(上)
C语言进阶⑩(数据的存储)数据类型_介绍+存储_大小端(知识点+笔试题)
7 0
|
存储 程序员 C语言
程序员之路:C语言中存储类别
程序员之路:C语言中存储类别
117 0
|
5天前
|
C语言
C语言—内存函数的实现和模拟实现(内存函数的丝绸之路)
C语言—内存函数的实现和模拟实现(内存函数的丝绸之路)
18 0
|
5天前
|
C语言
C语言—字符函数与字符串函数(字符问题变简单的关键之技)
C语言—字符函数与字符串函数(字符问题变简单的关键之技)
6 0