指针?地址?内存?

简介: 指针?地址?内存?

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

目录
相关文章
|
3月前
|
存储 C语言
指针和动态内存分配
指针和动态内存分配
94 0
|
7天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
25 4
|
1月前
链表指针的传参,传值和传地址
本文讨论了链表操作中指针传参的问题,特别是指针的传值与传地址的区别,并提供了修正代码,以确保链表插入操作能正确地修改指针指向的地址。
16 1
链表指针的传参,传值和传地址
|
22天前
|
存储 Rust C#
内存指针解引用
【10月更文挑战第14天】
30 1
|
1月前
|
C++
析构造函数就是为了释放内存,就是在局部指针消失前释放内存,拷贝构造函数就是以构造函数为模块,在堆里面新开一块,同一个变量在堆里面的地址
本文讨论了C++中构造函数和析构函数的作用,特别是它们在管理动态内存分配和释放中的重要性,以及如何正确地实现拷贝构造函数以避免内存泄漏。
37 2
|
1月前
|
存储 安全 NoSQL
driftingblues9 - 溢出ASLR(内存地址随机化机制)
driftingblues9 - 溢出ASLR(内存地址随机化机制)
35 1
|
3月前
|
存储 安全 Go
Go 中的指针:了解内存引用
Go 中的指针:了解内存引用
|
4月前
|
运维
开发与运维数组问题之指针的加减法意义如何解决
开发与运维数组问题之指针的加减法意义如何解决
45 7
|
4月前
|
存储 C++ 运维
开发与运维数组问题之指针的定义语法如何解决
开发与运维数组问题之指针的定义语法如何解决
35 6
|
4月前
|
编译器
8086 汇编笔记(六):更灵活的定位内存地址的方法
8086 汇编笔记(六):更灵活的定位内存地址的方法