# 理解内存与指针
# 内存与内存地址
# 什么是内存
内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大。
内存也被称为 内储存器 和 主存储器,其作用是用于暂时存放CPU中的运算数据,以及和硬盘等外部存储器交换的数据。下面是CPU的组成和工作原理:
在计算机的组成结构中,有一个很重要的部分是储存器。
存储器是用来存储程序和数据的部件,对于计算机来说,有了存储器,才有记忆功能,才能保证正常工作。存储器的种类很多,按其用途可分为主存储器和辅助存储器,主存储器又称内存储器(简称内存)。
内存就是暂时存储程序以及数据的地方,相对于外存(硬盘是外存的一种)而言,我们在电脑上输入文字、字符的时候,它就会被存入内存中,只有当你选择保存文件的时候,内存中的数据才会被存入硬盘。
内存是CPU能够直接寻址的存储空间,特点是存取速率特别快,临时存放数据,不能永久保存数据。
外存是计算机的存储器的一种,主要用来存放暂时不用的程序和数据。特定是容量大、价格低,但是存取速度慢。外存和内存之间尝尝频繁的交换信息。
# 什么是内存地址
内存地址是用于软硬件等不同层级中的数据概念,用来访问内存中的数据。
可以通过WiKi或者百度百科进行更加深入的了解。
# 指针、地址与引用
# 引用
引用是一个变量的别名,为什么引入别名?是因为我们想定义一个变量,由这个变量来共享另一个变量的内存空间,所以使用别名是一个很好的选择。
变量是什么? 是一个内存空间的名字,如果我们给这个内存空间再另外起一个名字,就是能共享这个内存了,引用(别名)由此而来。
比如:
var obj = { a: 1, b: 2 };
var obj1 = obj;
2
这里我们就可以说变量obj1是变量obj的一个引用;(因为在javascript中,object类型的赋值,是引用地址赋值)
# 指针
指针,指向另一个内存空间的变量,我们可以用个它来索引另一个内存空间中的内容,而指针本身有自己的内存空间。
如下图所示:
# 两者区别
引用是一个变量的别名,本身不单独分配自己的内存空间,而指针有自己的内存空间。
引用在开始的时候就绑定到了一个内存空间(开始必须赋初值), 所以它只能是这个内存空间的名字, 而不能改成其他的, 当然, 我们可以改变这个内存空间的值。
eg:
var obj = { a: 1, b: 2 };
var obj1 = obj;
obj.b = 3;
// {a: 1, b: 3 }
obj1.a = 4;
// {a: 4, b: 3 }
obj1 = { a:1, c:2 };
console.log(obj1);
// { a:1, c:2 };
console.log(obj);
// {a: 4, b: 3 }
2
3
4
5
6
7
8
9
10
11
12
13
14
这段代码的过程如下:
# 指向变量的指针
一个变量的地址指出了变量的存储单元在内存中的具体位置,能对变量进行存取操作。那这个变量就是指向变量的指针。
可以参考上面的 “图二” 可以很好的理解指向变量的指针。
# 指向指针的指针
如果一个指针变量存放的又是另一个指针变量的地址,则称这个变量为指向指针的指针变量或指向指针的指针。
#include <stdio.h>
// 入口主函数
void main () {
int a = 5;
// 这里&a就是变量a的内存地址
int *p = &a;
// 这里是打印输出变量a的值
printf("%d\n", *p);
}
2
3
4
5
6
7
8
9
10
# 指向函数的指针
指向函数的指针变量被称为函数指针。本身是指针变量,只不过该指针变量指向函数。
有了函数指针之后,就可以用该指针变量来调用函数。
函数指针有两个用途:
调用函数
做函数的参数
// 调用函数
function max (x, y) {
return x > y ? x : y;
}
max(1, 2);
// 做函数的参数
const a = [1,5,3,4,78,96,54,1,2,3,4];
function sum(total, num) {
return total + Math.round(num);
}
a.reduce(sum);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
以上例子中 max 和 sum 都是指向函数的指针变量。
# 多级指针
多级指针,是指 “指向地址的指针”;
# 一级指针
#include <stdio.h>
// 入口主函数
void main () {
int i = 10;
// 这里&a就是变量a的内存地址
int *p1 = &a;
// 这里是打印输出变量a的值
printf("%d\n", *p1);
}
2
3
4
5
6
7
8
9
10
在这里的 *p1 就是一级指针
这里同样参考 “图二” 来理解
指针变量p可以通过 *p1 来获取到变量i的值,也可以进行修改操作,总之 *p1 可以操作变量i的内存空间,所以 * 是一个操作内存空间的一个符号。
*p也就被称为一级指针。
# 二级指针
同上面讲的一级指针原理一致。
#include <stdio.h>
// 入口主函数
void main () {
int i = 10;
// 这里&a就是变量a的内存地址
int *p1 = &a;
int **p2 = &p1;
}
2
3
4
5
6
7
8
9
**p2 即为二级指针。
其余多级指针依旧同理:
int ***p3 = &p2;
int ****p4 = &p3;
2
# 动态内存分配
内存 | 描述 | 特性 |
---|---|---|
栈区 | 是一个确定的常数(1~2M) 不同平台会有不同大小,超出会提示stackoverflow | 自动分配 , 自动释放 |
堆区 | 用于动态内存分配 | 手动分配和释放 , 可占用80%内存 |
全局区或静态区 | 在程序中明确被初始化的全局变量、静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量) | 只初始化一次 |
程序代码区 | 代码区指令根据程序设计流程依次执行,对于顺序指令,则只会执行一次(每个进程),如果反复,则需要使用跳转指令,如果进行递归,则需要借助栈来实现。 | 代码区的指令中包括操作码和要操作的对象(或对象地址引用) |
理解指针的操作,更加有助于我们更快的理解数据结构和算法。
# 致谢
感谢大家阅读我的文章,如果对我感兴趣可以点击页面右上角,帮我点个star。
作者:前端小然子
链接: https://xiaoranzife.com/guide/arithmetic/0.pointer.html
来源:前端小然子的博客
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。