多级指针
- 概述
- bilibili
- GPT
- 举例
- C 语言 `main` 函数的入口参数
- 构造连续的参数表示
- 参数传递机制
- end
- 指针链表
概述
bilibili
int **p;
多级指针:存放地址的地址空间
在内存的表现形式
应用的图示:
把毫不相关的内存组合成线性的关系
二维指针在使用的时候一般使用p[0]
,p[1]
,p[2]
……p[n]
这个n有多大呢?
在二维指针中,有个不成文的规定,假设p[m] == NULL
,这时候二维指针就结束了!
GPT
多级指针(multi-level pointers)是C和C++等编程语言中的一个概念,它们指的是指向其他指针的指针。理解多级指针的关键在于理解每一层间接性,并且要清楚每增加一级指针就增加了一次解引用(dereferencing)的需要。
让我们从基础开始:
- 普通指针:一个普通指针保存的是另一个变量的地址。例如:
int a = 10;
int *p = &a; // p 是指向 int 类型的指针,它保存了变量 a 的地址。
- 二级指针:是指向指针的指针。这意味着这个指针保存的是另一个指针的地址。例如:
int **pp = &p; // pp 是指向指针 p 的指针,它保存了指针 p 的地址。
在这个例子中,pp
是一个二级指针,因为它指向了一个一级指针p
。
- 更高级别的指针:理论上你可以有任意级别的指针,如三级、四级等等。例如:
int ***ppp = &pp; // ppp 是指向二级指针 pp 的指针。
使用多级指针时,你可能需要多次使用解引用来访问最底层的数据。在上面的例子中,如果你想通过ppp
来获取a
的值,你需要这样做:
printf("%d", ***ppp); // 解引用三次以访问 a 的值。
多级指针通常用于以下几种情况:
- 当函数需要修改传入的指针本身时(即改变指针所指向的位置),这时就需要传递指针的地址,也就是二级指针。
- 动态分配二维或更高维度的数组时,每个维度的指针都需要指向下一个维度的指针。
- 在某些复杂的数据结构中,比如树形结构或链表中,节点之间可能需要通过多级指针来连接。
然而,多级指针也增加了代码的复杂度,可能导致难以调试的问题,因此应该谨慎使用。如果可以避免使用多级指针,尽量选择更简单的设计。
举例
C 语言 main
函数的入口参数
看看main函数的入口参数
#include <stdio.h>
int main(int argc, char **argv[])
{
int i;
for (i = 0; i < argc; i++)
{
printf("argv[%d] = %s\n", i, argv[i]);
}
}
输出结果如下:
PS E:\CProject> cd 'e:\CProject\output'
PS E:\CProject\output> & .\'main.exe'
argv[0] = E:\CProject\output\main.exe
PS E:\CProject\output> .\main.exe hello 123 456 world
argv[0] = E:\CProject\output\main.exe
argv[1] = hello
argv[2] = 123
argv[3] = 456
argv[4] = world
PS E:\CProject\output>
PS E:\CProject\output> .\main.exe hello 123 456 world 这次后面给了几个参数,下面就跟着输出了几个参数
系统就把这几个字符放在了这样的空间
构造连续的参数表示
如果我们想构造一种视觉上看起来“连续”的参数表示,可以通过流程图或图解的方式来展示。然而,在实际内存布局中,argv
中的字符串并不是物理上连续存储的;它们是由多个独立分配的字符串组成的,只是通过指针数组 argv
连接在一起。
现在这几个参数不知道是不是连续的!
我们想给它构造成连续的!
效果图如下:
如果不知道个数,可以使用while循环,检测argv[i] != NULL
#include <stdio.h>
int main(int argc, char **argv[])
{
int i = 0;
while (argv[i] != NULL)
{
printf("argv[%d] = %s\n", i, argv[i]);
i++;
}
// for (i = 0; i < argc; i++)
// {
// printf("argv[%d] = %s\n", i, argv[i]);
// }
return 0;
}
输出:
PS E:\CProject\output> .\main.exe hello world! lalala 123 456 XXX
argv[0] = E:\CProject\output\main.exe
argv[1] = hello
argv[2] = world!
argv[3] = lalala
argv[4] = 123
argv[5] = 456
argv[6] = XXX
PS E:\CProject\output>
这段代码确保即使在某些环境下 argc
可能不准确的情况下也能正确打印所有参数。
输出也正常!
在 C 语言中,main
函数是程序的入口点。它的定义可以接受两个参数:
int argc
: 表示命令行参数的数量(包括程序名本身)。char *argv[]
或者等价地char **argv
: 是一个指向字符串数组的指针,每个字符串代表命令行中的一个参数,最后一个参数总是以NULL
结束。
实际上,argv
是一个指针数组,每个元素是指向一个字符数组(即字符串)的指针。因此,argv[0]
指向程序名,而 argv[1]
到 argv[argc-1]
分别指向命令行输入的各个参数。
参数传递机制
当你在命令行运行程序时,操作系统会将你提供的命令行参数解析为字符串,并把这些字符串的地址填充到 argv
数组中。argc
的值会被设置为这个数组的有效元素个数。
end
main
函数的参数argc
和argv
提供了访问命令行参数的方式。argv
是一个指针数组,每个元素是指向命令行参数字符串的指针。- 在内存中,这些参数不是连续存储的,而是通过指针连接起来的。
- 使用
while
循环结合argv[i] != NULL
可以遍历所有命令行参数,即使argc
的值不确定。
指针链表
示例
#include <stdio.h>
struct ListNode
{
int Val;
struct ListNode*next;
};
int main() {
struct ListNode a, b, c, d, e;
a.Val = 1;
b.Val = 2;
c.Val = 3;
d.Val = 4;
e.Val = 5;
a.next = &b;
b.next = &c;
c.next = &d;
d.next = &e;
e.next = NULL;
struct ListNode*head = &a;
while (head)
{
printf("Val=[%d] adress=[%p] next=[%p]\n",head->Val,head,head->next);
head = head->next;
}
return 0;
}
结果
Val=[1] adress=[00FEFBC8] next=[00FEFBB8]
Val=[2] adress=[00FEFBB8] next=[00FEFBA8]
Val=[3] adress=[00FEFBA8] next=[00FEFB98]
Val=[4] adress=[00FEFB98] next=[00FEFB88]
Val=[5] adress=[00FEFB88] next=[00000000]