【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永远为真,该代码陷入死循环。


总结

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


目录
相关文章
|
25天前
|
存储 程序员 编译器
C 语言中的数据类型转换:连接不同数据世界的桥梁
C语言中的数据类型转换是程序设计中不可或缺的一部分,它如同连接不同数据世界的桥梁,使得不同类型的变量之间能够互相传递和转换,确保了程序的灵活性与兼容性。通过强制类型转换或自动类型转换,C语言允许开发者在保证数据完整性的前提下,实现复杂的数据处理逻辑。
|
24天前
|
存储 编译器 程序员
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
在C语言中,内存布局是程序运行时非常重要的概念。内存布局直接影响程序的性能、稳定性和安全性。理解C程序的内存布局,有助于编写更高效和可靠的代码。本文将详细介绍C程序的内存布局,包括代码段、数据段、堆、栈等部分,并提供相关的示例和应用。
39 5
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
|
24天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
54 6
|
26天前
|
存储 数据管理 C语言
C 语言中的文件操作:数据持久化的关键桥梁
C语言中的文件操作是实现数据持久化的重要手段,通过 fopen、fclose、fread、fwrite 等函数,可以实现对文件的创建、读写和关闭,构建程序与外部数据存储之间的桥梁。
|
28天前
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
46 6
|
29天前
|
存储 数据建模 程序员
C 语言结构体 —— 数据封装的利器
C语言结构体是一种用户自定义的数据类型,用于将不同类型的数据组合在一起,形成一个整体。它支持数据封装,便于管理和传递复杂数据,是程序设计中的重要工具。
|
1月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
122 13
|
29天前
|
大数据 C语言
C 语言动态内存分配 —— 灵活掌控内存资源
C语言动态内存分配使程序在运行时灵活管理内存资源,通过malloc、calloc、realloc和free等函数实现内存的申请与释放,提高内存使用效率,适应不同应用场景需求。
|
1月前
|
存储 编译器 数据处理
C 语言结构体与位域:高效数据组织与内存优化
C语言中的结构体与位域是实现高效数据组织和内存优化的重要工具。结构体允许将不同类型的数据组合成一个整体,而位域则进一步允许对结构体成员的位进行精细控制,以节省内存空间。两者结合使用,可在嵌入式系统等资源受限环境中发挥巨大作用。
62 11
|
29天前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。