# 理解内存与指针

# 内存与内存地址

# 什么是内存


内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大。

内存也被称为 内储存器 和 主存储器,其作用是用于暂时存放CPU中的运算数据,以及和硬盘等外部存储器交换的数据。下面是CPU的组成和工作原理:

图一

在计算机的组成结构中,有一个很重要的部分是储存器。

存储器是用来存储程序和数据的部件,对于计算机来说,有了存储器,才有记忆功能,才能保证正常工作。存储器的种类很多,按其用途可分为主存储器和辅助存储器,主存储器又称内存储器(简称内存)。

内存就是暂时存储程序以及数据的地方,相对于外存(硬盘是外存的一种)而言,我们在电脑上输入文字、字符的时候,它就会被存入内存中,只有当你选择保存文件的时候,内存中的数据才会被存入硬盘。

内存是CPU能够直接寻址的存储空间,特点是存取速率特别快,临时存放数据,不能永久保存数据。

外存是计算机的存储器的一种,主要用来存放暂时不用的程序和数据。特定是容量大、价格低,但是存取速度慢。外存和内存之间尝尝频繁的交换信息。

# 什么是内存地址


内存地址是用于软硬件等不同层级中的数据概念,用来访问内存中的数据。

可以通过WiKi或者百度百科进行更加深入的了解。

# 指针、地址与引用

# 引用


引用是一个变量的别名,为什么引入别名?是因为我们想定义一个变量,由这个变量来共享另一个变量的内存空间,所以使用别名是一个很好的选择。

变量是什么? 是一个内存空间的名字,如果我们给这个内存空间再另外起一个名字,就是能共享这个内存了,引用(别名)由此而来。

比如:

var obj = { a: 1, b: 2 };
var obj1 = obj;
1
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 }
1
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);
}
1
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);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

以上例子中 maxsum 都是指向函数的指针变量。

# 多级指针

多级指针,是指 “指向地址的指针”;

# 一级指针


#include <stdio.h>

// 入口主函数
void main () {
    int i = 10;
    // 这里&a就是变量a的内存地址
    int *p1 = &a;
    // 这里是打印输出变量a的值
    printf("%d\n", *p1);
}
1
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;
}
1
2
3
4
5
6
7
8
9

**p2 即为二级指针。


其余多级指针依旧同理:

int ***p3 = &p2;
int ****p4 = &p3;
1
2

# 动态内存分配

内存 描述 特性
栈区 是一个确定的常数(1~2M) 不同平台会有不同大小,超出会提示stackoverflow 自动分配 , 自动释放
堆区 用于动态内存分配 手动分配和释放 , 可占用80%内存
全局区或静态区 在程序中明确被初始化的全局变量、静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量) 只初始化一次
程序代码区 代码区指令根据程序设计流程依次执行,对于顺序指令,则只会执行一次(每个进程),如果反复,则需要使用跳转指令,如果进行递归,则需要借助栈来实现。 代码区的指令中包括操作码和要操作的对象(或对象地址引用)

理解指针的操作,更加有助于我们更快的理解数据结构和算法。

# 致谢

感谢大家阅读我的文章,如果对我感兴趣可以点击页面右上角,帮我点个star。

作者:前端小然子

链接: https://xiaoranzife.com/guide/arithmetic/0.pointer.html

来源:前端小然子的博客

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

上次更新: 2019-11-11 1:41:29 ├F10: PM┤