当前位置: 首页>前端>正文

指针?地址?内存?

1.地址和地址上存储的值:

  1. 地址:在计算机内存中,每个字节都有一个唯一的地址,用来标识它在内存中的位置。这个地址通常以十六进制表示,并且在程序运行时是固定的。指针就是用来存储和操作这些地址的变量,它们存储的是内存中某个对象或变量的地址。
  2. :内存地址中存储的实际数据,即在特定地址处存储的内容。这个值可以是任何数据类型,比如整数、字符、浮点数等。

所以当我们说“解引用一个指针”时,我们实际上是在获取该指针所指向地址处存储的值

通俗的讲,内存中的每个字节是实际存在的,我们用地址标识这些字节。而地址本身没有意义,只有解引用地址才能得到该地址对应的字节数据

并不是说一个地址只标识一个字节,
int *P,char *Q,对P保存的地址解引用*P可以得到一个8字节整型,对Q保存的地址解引用*Q可以得到一个4字节字符
。因此,我们可以通过强制转换指针的类型,修改它标识字节的范围!

刚才我们使用了变量P和Q的值也就是他们保存的地址,当然也可以变量P进行修改:P = Q,这时我们修改了内存里一个8字节的数据,但是我们没有使用地址,这是怎么做到的呢?

当我们执行 int *P; 这样的语句时,系统会为变量 P 分配一块内存,然后将其地址与标识符 P 关联起来。所以在实际使用时,我们不需要显式地使用地址,编译器会自动帮你完成地址的管理。

P = Q; 这样的语句中,编译器会知道 P 的地址,并将 Q 的值存储到该地址中。这个过程是由编译器隐式地完成的。你不需要手动获取 PQ 的地址,因为编译器已经知道P和Q的地址,可以直接将值存储到那个地址中。

也就是说我们虽然没有显式地解引用地址,然后对实际的字节进行修改,实际上标识符P已经和若干字节的地址关联起来了,所以我们在使用变量P和Q时,实际上就是通过解引用地址来操作实际的内存字节

只不过指针为我们提供了更灵活更强大的操作内存的方式,感谢指针!

2.二级指针进阶用法深入理解地址和内存的关系

二级指针也是指针,保存的也是地址,不论几级指针,只要是指针,就保存地址

二级指针的类型是确定的,是一个指针,一级指针的类型不确定

我们之前谈到了,改变指针类型,可以修改它标识内存中字节的范围,因此也就修改了这个指针解引用的结果

二级指针解引用的结果是内存中的8个字节,一级指针解引用的结果由它的类型确定,比如int是4字节,char是1字节,等等

说了这么多,将一级指针转换成二级指针有什么用呢?

举一个例子

typedef struct task_s { 
    void *next;  // 指向下一个task
    int a;
}task_t;

这是一个结构体,大小是8+4=12字节

下面我们将一级指针转换成二级指针:

01:  task_t *task = (task_t *)malloc(sizeof(task_t)); // 创建一个task_t对象
02:  task_t *p = (task_t *)malloc(sizeof(task_t)); // 创建另一个task_t对象
03:
04:  task->next = p; // 等价于(*task).next = p;
05:  *(task **)task = p; //将一级指针转换成二级指针,并解引用, 结果是next指针

注意,04行和05行是完全等价的,你看出来二级指针的用法了吗?

一级指针task和二级指针task指向的地址是同一个task_t对象的首地址

而它们的区别在于解引用的结果:

1.一级指针解引用的结果是12个字节也就是整个task_t对象,操作其中的next成员:task->next或(*task).next

2.二级指针解引用的结果不再是整个对象,而是task_t对象的前8个字节,刚好是next在内存中的位置,所以*(task **)task等价于next

为什么会有这样的区别?

答案在前文中提到过两次:改变指针类型,可以修改它标识内存中字节的范围,因此也就修改了这个指针解引用的结果

将一级指针转换成二级指针,指针所标识的内存中的字节的范围从12字节变为8字节,解引用的结果也因此改变

推荐学习https://xxetb.xetslk.com/s/p5Ibb


https://www.xamrdz.com/web/2jw1948196.html

相关文章: