【嵌入式C语言】多级指针

news/2024/12/26 3:30:01 标签: c语言, 开发语言

多级指针

  • 概述
    • 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)的需要。

让我们从基础开始:

  1. 普通指针:一个普通指针保存的是另一个变量的地址。例如:
int a = 10;
int *p = &a; // p 是指向 int 类型的指针,它保存了变量 a 的地址。
  1. 二级指针:是指向指针的指针。这意味着这个指针保存的是另一个指针的地址。例如:
int **pp = &p; // pp 是指向指针 p 的指针,它保存了指针 p 的地址。

在这个例子中,pp是一个二级指针,因为它指向了一个一级指针p

  1. 更高级别的指针:理论上你可以有任意级别的指针,如三级、四级等等。例如:
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 这次后面给了几个参数,下面就跟着输出了几个参数

系统就把这几个字符放在了这样的空间

在这里插入图片描述

hello
123
456
world

构造连续的参数表示

如果我们想构造一种视觉上看起来“连续”的参数表示,可以通过流程图或图解的方式来展示。然而,在实际内存布局中,argv 中的字符串并不是物理上连续存储的;它们是由多个独立分配的字符串组成的,只是通过指针数组 argv 连接在一起。
现在这几个参数不知道是不是连续的!
我们想给它构造成连续的!

在这里插入图片描述

效果图如下:

Column 1
Column 2
Column 3
hello
123
456
world
P0
P1
P2
P3
指针

如果不知道个数,可以使用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 函数的参数 argcargv 提供了访问命令行参数的方式。
  • 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]

http://www.niftyadmin.cn/n/5799731.html

相关文章

【蓝桥杯每日一题】分糖果——DFS

分糖果 蓝桥杯每日一题 2024-12-24 分糖果 DFS 题目描述 两种糖果分别有 9 个和 16 个&#xff0c;要全部分给 7 个小朋友&#xff0c;每个小朋友得到的糖果总数最少为 2 个最多为 5 个&#xff0c;问有多少种不同的分法。糖果必须全部分完。 只要有其中一个小朋友在两种方案中…

YoloDotNet 图像分类

文章目录 1、前期准备安装依赖环境:获取模型并转换格式:创建项目和添加依赖包:2、代码实现步骤引入必要的命名空间:初始化 Yolo 对象用于分类任务:加载图像并进行分类:处理分类结果:3、优化和注意事项1、前期准备 安装依赖环境: 确保安装了合适版本的 CUDA(如 CUDA v…

攻防世界web第一题

最近开始学习网络安全的相关知识&#xff0c;开启刷题&#xff0c;当前第一题 题目为攻防世界web新手题 这是题目 翻译&#xff1a;在这个训练挑战中&#xff0c;您将了解 Robots_exclusion_standard。网络爬虫使用 robots.txt 文件来检查是否允许它们对您的网站或仅网站的一部…

探秘Xss:原理、类型与防范全解析

一、Xss究竟是什么&#xff1f; &#xff08;一&#xff09;Xss的定义简述 Xss全称是跨站脚本攻击&#xff08;Cross Site Scripting&#xff09;&#xff0c;为了不和层叠样式表&#xff08;Cascading Style Sheets&#xff0c;CSS&#xff09;的缩写混淆&#xff0c;故将其…

React:前端开发领域的璀璨之星

亲爱的小伙伴们&#x1f618;&#xff0c;在求知的漫漫旅途中&#xff0c;若你对深度学习的奥秘、Java 与 Python 的奇妙世界&#xff0c;亦或是读研论文的撰写攻略有所探寻&#x1f9d0;&#xff0c;那不妨给我一个小小的关注吧&#x1f970;。我会精心筹备&#xff0c;在未来…

WebRTC服务质量(12)- Pacer机制(04) 向Pacer中插入数据

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

React 生命周期完整指南

React 生命周期完整指南 1. 生命周期概述 1.1 React 16.3 之前的生命周期 初始化阶段 constructorcomponentWillMountrendercomponentDidMount 更新阶段 componentWillReceivePropsshouldComponentUpdatecomponentWillUpdaterendercomponentDidUpdate 卸载阶段 componentWil…

使用 OpenCV 绘制线条和矩形

OpenCV 是一个功能强大的计算机视觉库&#xff0c;它不仅提供了丰富的图像处理功能&#xff0c;还支持图像的绘制。绘制简单的几何图形&#xff08;如线条和矩形&#xff09;是 OpenCV 中常见的操作。在本篇文章中&#xff0c;我们将介绍如何使用 OpenCV 在图像上绘制线条和矩形…