资讯专栏INFORMATION COLUMN

指针,数据存储与调试《一探内存究竟》

不知名网友 / 2605人阅读

摘要:有关内存文章目录有关内存前言一指针初阶指针大小指针类型解引用野指针成因未初始化越界指针指向的空间释放规避局部地址检查有效性指针运算整数指针指针关系运算时允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置

有关内存



前言

大体把握C语言后,要清楚C语言是一门偏底层的语言,因此就非常有必要来探究一下内存深处的样子,修炼我们的内功,对此有更深层次的理解.


一、指针初阶

指针就是变量,用来存放地址的变量。(存放在指针中的值被当做地址处理)

#include int main(){ int a = 10;//在内存中开辟一块空间 int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。   //将a的地址存放在p变量中,p就是一个之指针变量。 return 0;}

1.指针大小

对于32位机器,有32根地址线,即有2^32个地址,可以操作4g的空间,笔者在下文对此有详细的介绍
C语言整体把握

2.指针类型

既然不管何种类型指针大小都由32位与64位决定,那不同的指针类型有什么作用呢?

char  *pc = NULL;int   *pi = NULL;short *ps = NULL;long  *pl = NULL;float *pf = NULL;double *pd = NULL;

解引用 *

*(解引用操作符)可以对地址(指针)变量 解引用通过地址对其内容做出修改

*pc = 10
#include int main(){ int n = 10; char *pc = (char*)&n; int *pi = &n;  printf("%p/n", &n); printf("%p/n", pc); printf("%p/n", pc+1); printf("%p/n", pi); printf("%p/n", pi+1); return  0;}

经过调试可以发现int类型一次跳过四个字节,char类型一次跳过一个字节,即地址变量类型决定了指针移动的距离

3.野指针

位置不可只的指针,使用起来极其危险也没有意义

1.成因

1.未初始化

随意定义指针

#include int main(){  int *p;//局部变量指针未初始化,默认为随机值    *p = 20; return 0;}

2.越界

#include int main(){    int arr[10] = {0};    int *p = arr;    int i = 0;    for(i=0; i<=11; i++)   {        //当指针指向的范围超出数组arr的范围时,p就是野指针        *(p++) = i;   }    return 0;}

3.指针指向的空间释放

规避

初始化,不越界,释放置为空( NULL),局部变量地址慎用,检查有效性

局部地址

尽量不要在子函数中直接返回指针,由于作用域与生命周期的限定,出函数时指针所指向的空间已经被销毁.

检查有效性
if(*p != NULL)//断言,需引头文件//条件不为真时报错,对程序员十分友好assert(*p)

4.指针运算

1.±整数

结合上文,常用于指针与数组——函数等的结合

2.指针-指针

可以得到两地址之间的元素个数

关系运算时:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

可以与数组越界后一位判定目的是否成立

5.二级指针

存放地址的指针

int c= 0int *p = &c;int **p = *p;

6.指针数组

存放指针的数组

arr数组里存放五个元素,每个元素都是int*类型int* arr[5]

有关栈帧

引图可以当做引言,但不可视为栈帧,下期详讲

二、数据存储

1.数据类型

char //字符数据类型short //短整型int //整形long //长整型long long   //更长的整形float //单精度浮点数double //双精度浮点数

构造类型

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

指针类型

int* p;char* p;float* p;void* p;

2.整形在内存中的存储

整形分配四个字节

00000000 00000000 00000000 00000000

内存中,对整形,存储并使用的是他的二级制位的补码

原码,反码,补码

对于整数

原码,反码,补码相同,都是它所对应的二进制

对于负数

原码是其所对应二进制且最高位为1

10000000 00000000 00000000 00000000
  • 二进制中最高位为符号位,0正1负
  • 原码:最高位符号位为1
  • 反码:符号位不变,其他位按位取反
  • 补码:反码+1即为补码
    原码与补码的转化方法相同
    符号位不变 取反加一

2.大小端

大(小)端字节序

  • 大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
  • 小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地址中。
  • 图示十六进制排序应该为00 00 00 01,01却在低地址中,即为小端

3.浮点数在内存中的存储

3.1415926
1e10

int main(){ int n = 9; float *pFloat = (float *)&n; printf("n的值为:%d/n",n); printf("*pFloat的值为:%f/n",*pFloat); *pFloat = 9.0; printf("num的值为:%d/n",n); printf("*pFloat的值为:%f/n",*pFloat); return 0;}


与思考有所不同,不得不介绍浮点数在内存中的存储规则
///
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:

  • (-1)^S * M * 2^E
  • (-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。
  • M表示有效数字,大于等于1,小于2。
  • 2^E表示指数位。
    举例来说:
    十进制的5.0,写成二进制是 101.0 ,相当于 1.01×2^2 。那么,按照上面V的格式,可以得出s=0,M=1.01,E=2。十进制的-5.0,写成二进制是 -101.0 ,相当于 -1.01×2^2 。那么,s=1,M=1.01,E=2。
  • 对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。

  • 对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。

    素材来源:比特科技B站开源素材
    存储时,由于m大于1小于2,即必为1,存储时将1省略,读取时加入即可

E

科学计数法中,e可以为负数,也就有了e存入内存时其真实值必须加一个中间数,8位为127,11位为1023
例:2^10 E为10,存储时 10+127=137
10001001

E的特殊情况

1.不全为0或不全为一

正常情况,只需减去中间值127(1023)得到真实值,存储时m正常补齐即可

2.全为0

E等于1-127(或1023)即为真实值
表示一个无穷小的数
将M直接还原为0.xxxxxxx的小数

3.全为1

2的高次幂,是一个非常大的数,表示±无穷

补充

无符号的char所对应图示可以用于E的二进制展示

调试

探究底层时离不开调试,调试是一个优质程序员必须掌握的技巧。文中多处引入调试图例

调试常用
最常使用的几个快捷键:

  • F5
    启动调试,经常用来直接跳到下一个断点处。
  • F9
    创建断点和取消断点
    断点的重要作用,可以在程序的任意位置设置断点。
    这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去。
  • F10
    逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。
  • F11
    逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这是
    最长用的)。
  • CTRL + F5
    开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用。

算数转换

long doubledoublefloatunsigned long intlong intunsigned intint

不同算数类型进行算数操作时,数据由低到高进行算数转换
注意:转换为char类型时要在二进制位低八位处进行截断,转回时要将高位补为符号位.


总结

重点在于数据存储,浮点数的存储,算数转换.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/125658.html

相关文章

  • 《Java编程思想》笔记01------一切都是对象

    摘要:一引用操纵对象在的世界里,一切都被视为对象。特点创建程序时,需要知道存储在栈内所有数据的确切生命周期,以便上下移动堆栈指针。因为,指向同一块内存空间除了通过对象引用静态变量,我们还可以通过类直接引用静态变量 一、引用操纵对象 在Java的世界里,一切都被视为对象。操纵的标识符实际上是对象的引用, 例如:遥控器与电视的关系。 可以在没有对象关联的情况下,拥有一个引用。没有电视机,也可以拥...

    yuanxin 评论0 收藏0
  • 【PHP源码学习】2019-03-18 复习前面的内容

    摘要:调用函数时,它将用户释放的内存块连接到空闲链上。这个联合体共占用字节。是数字,且顺序递增位置固定,如访问是的元素,即,就直接访问数组的第个位置即可即,这样就不需要前面的索引数组。 baiyan 全部视频:https://segmentfault.com/a/11... 原视频地址:http://replay.xesv5.com/ll/24... 本笔记中部分图片截自视频中的片段,图片版...

    lindroid 评论0 收藏0
  • c语言中常见的一些坑和一些细节(建议收藏)

    摘要:博主接下来将会整理一些语言中常见的问题和坑,再看博主解释的时候可以自己思考一下变量的声明和定义有什么区别答变量的定义为变量分配地址和存储空间,变量的声明不分配地址。指针操作超过了变量的作用域范围如返回局部变量的地址。 博主接下来将会整理一些语言中常见的问题和坑,再看博主解释的时候可以自己...

    jhhfft 评论0 收藏0

发表评论

0条评论

不知名网友

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<