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

C语言---------深入理解指针

目录

一、字符指针

二、指针数组:

三、数组指针:

1、定义:

2、&数组名和数组名区别:

3、数组指针的使用:

四、数组参数,指针参数:

1、一维数组传参:

2、二维数组传参:

3、一级指针传参:

4、二级指针传参:

五、函数指针:

1、定义:

2、函数名和&函数名:

3、函数指针的调用:

六、函数指针数组:

七、指向函数指针数组的指针


一、字符指针

定义:字符指针就是指向一个字符串的指针。指针类型为char*

int main()
{char c = 'z';char* pc = &c;printf("%c\n", c);return 0;
}

或者:

int main()
{const char* pc = "abcdef";printf("%s\n", pc);return 0;
}

这里因为是个常量字符串,所以可以用const修饰来达到后面不会被修改的问题。

上面const char* pc = "abcdef"这里并不是将abcdef赋给pc的指针变量里面,而是将字符串的首字符的地址放在pc中。

接下来看一个经典题目:

#include <stdio.h>
int main()
{char str1[] = "hello ppr.";char str2[] = "hello ppr.";char *str3 = "hello ppr.";char *str4 = "hello ppr.";if(str1 ==str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if(str3 ==str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

这串代码的意思是:

将hello ppr.给到数组str1和str2中,判断二者是否相等。

将hello ppr.的地址给到str3和str4中,判断二者是否相等。

那么为什么会出现这样的结果呢?

对于str1和str2,这两个是开辟了两块不同的空间,比较数组名就是比较首元素的地址,毕竟空间不同肯定比较出来就会不同。

对于str3和str4

首先要知道这是一个常量字符串是不能够被修改的,所以在内存就没有必要存储两份了,那么这两个字符指针就都会指向那同一块空间。

二、指针数组:

定义:存放指针的数组就是指针数组。

int main()
{int arr1[5] = { 1,2,3,4,5 };int arr2[5] = { 2,3,4,5,6 };int arr3[5] = { 3,4,5,6,7 };int arr4[5] = { 0,0,0,0,0 };int* arr[4] = {arr1,arr2,arr3,arr4};for (int i = 0; i < 4; i++){for (int j = 0; j < 5; j++){//printf("%d ", arr[i][j]);printf("%d ", *(*(arr + i) + j));}printf("\n");}return 0;
}

如上代码所示:

arr先和右边的[]结合,就是一个数组,然后里面每一个元素都是int* ,这就是一个存放整形指针的数组。

例如:

int* arr1[10]//这是一个整形指针的数组,里面有10个元素。

char* arr2[10]//这是一个这是一个字符指针的数组,里面有10个元素。

char** arr3[10]这是一个二级字符指针的数组,里面有10个元素。

三、数组指针:

1、定义:

定义:这是一个指针,是能够指向数组的指针。

写法:int* p1[10]       or        int (*p2)[10]

这两种写法是哪一种呢?

很显然是后面的,因为前一个和指针数组一样。

因为p2和[]结合的优先级高于*,所以需要用小括号来改变其结合的优先顺序。

2、&数组名和数组名区别:

例如:

对于int arr[10];

arr和&arr分别有啥区别?

同样从代码入手:

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("arr = %p\n", arr);printf("(arr+1) = %p\n", arr + 1);printf("&arr = %p\n", &arr);printf("(&arr + 1) = %p\n", &arr + 1);return 0;
}

如上图所示,如果只看arr和&arr的话,二者是相等的,但是如果对二者都进行指针运算+1后,arr跳过了增加了4,跳过了一个整型,而&arr增加了28(十六进制),换算成十进制为40,所以就是跳过了10个整型,也就是这个数组。

故有结论:arr是这个数组的首元素的地址

                &arr是这整个数组的地址,所以+1是跳过这个数组的大小

3、数组指针的使用:

数组指针大多时候是在二维数组的传参中使用的,在一维数组中的作用不大。

依然从代码入手:

void print_arr1(int arr[3][5], int row, int col)
{int i = 0;for (i = 0; i < row; i++){for (int j = 0; j < col; j++){printf("%d ", arr[i][j]);}printf("\n");}
}
void print_arr2(int(*arr)[5], int row, int col)
{int i = 0;for (i = 0; i < row; i++){for (int j = 0; j < col; j++){printf("%d ", arr[i][j]);}printf("\n");}
}
int main()
{int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 };print_arr1(arr, 3, 5);printf("\n");//数组名arr,表示首元素的地址//但是二维数组的首元素是二维数组的第一行//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址//可以数组指针来接收print_arr2(arr, 3, 5);return 0;
}

如上代码所示:

这里用了自己创建的两个打印二维数组的函数,在其中唯一不同的是形参的接收不同,在print_arr1中,用传统的二维数组来接收,而在print_arr2中,用数组指针(指向数组的指针)来接收是一样的效果。

拓展:int (*parr[10])[5] 这是个啥?

首先parr和[10]结合成为一个数组,所以parr是个数组,在这个数组里面有10个元素,

其中每个元素的类型是int(*)[5]这个数组指针

四、数组参数,指针参数:

1、一维数组传参:

#include <stdio.h>
void test(int arr[])
{}
void test(int arr[10])
{}
void test(int* arr)
{}
void test2(int* arr[20])
{}
void test2(int** arr)
{}
int main()
{int arr[10] = { 0 };int* arr2[20] = { 0 };test(arr);test2(arr2);
}

如上代码中,所有传参均是可以的,用数组接收或者用指针接收。

2、二维数组传参:

3、一级指针传参:

当一个函数的参数部分为一个一级指针的时候,函数能接收的参数有:

void test(int* a)
{}int main()
{int a = 10;int* pa = &a;int arr[10];test(&a);test(pa);test(arr);return 0;
}

4、二级指针传参:

当一个函数的参数部分为一个二级指针的时候,函数能接收的参数有:

void test(int** a)
{}int main()
{int** a ;int* pa ;int* arr[10];test(a);test(&pa);test(arr);return 0;}

五、函数指针:

1、定义:

类比一下:

数组指针是指向数组的指针,函数指针就是指向函数的指针。

(类比于数组指针)

那么函数指针该怎么写呢?

例如有一个函数int Add(int x,int y);

假设我要定义一个函数指针变量名为ppr只想Add函数

那么首先要是一个指针就需要用()来进行:(*ppr)

然后需要是个函数里面写形参的类型就是:(*ppr)(int,int)

最后看返回值:int (* ppr)(int,int)= &Add;        

2、函数名和&函数名:

函数名和&函数名在C语言中大多数情况下都是等效的,都表示函数的地址。然而,使用&可以使代码更加清晰明确。

3、函数指针的调用:

int Add(int x, int y)
{return x + y;
}int main()
{//int (*pf)(int, int) = &Add;int (* pf)(int, int) = Add;//int ret = Add(2, 3);int ret = pf(2, 3);int ret = (*pf)(2, 3);printf("%d\n", ret);return 0;
}

如上代码所示:

将Add赋给pf后pf就可以像Add函数一样地调用了,也可以对pf解引用后调用,语法上可以这样理解。

六、函数指针数组:

存放函数指针的数组就是函数指针数组。

写法:

在函数指针的基础上改变:

如上所示:

int (* ppr)(int,int)这是一个函数指针,

int (* ppr[5])(int,int)此时ppr会和[5]先结合,就是一个数组,去掉数组名和个数剩下的

int (*)(int,int)这个就是函数指针类型。

应用场景:转移表

七、指向函数指针数组的指针

我们从函数指针数组入手:

int (* pr[5])(int,int)这是一个函数指针,

现在我想要一个指向函数指针数组的指针,所以就可以将ppr与一个*结合使其成为指针

int (* (*ppr)[5])(int,int) = &pr;

这个就成为了 指向函数指针数组的指针

这个的函数指针类型就是去掉 (*ppr)

这个int (* [5] )(int,int)就是一个函数指针类型

接下来画个图来理解:


 

相关文章:

C语言---------深入理解指针

目录 一、字符指针 二、指针数组&#xff1a; 三、数组指针&#xff1a; 1、定义&#xff1a; 2、&数组名和数组名区别&#xff1a; 3、数组指针的使用&#xff1a; 四、数组参数&#xff0c;指针参数&#xff1a; 1、一维数组传参&#xff1a; 2、二维数组传参&am…...

C++ 算法教程

归并排序 #include<iostream> using namespace std; template <class T> void Merge(T data[],int start,int mid,int end) {int len1 mid - start 1, len2 end - mid;int i, j, k;T* left new int[len1];T* right new int[len2];for (i 0; i < len1; i)…...

【支持向量机】问题梳理

学完支持向量机后我有些地方不太清楚&#xff0c;故做如下梳理&#xff1a; 1.为什么支持向量机模型认为一个点划分正确的标志是y(wxb)>1呢&#xff0c;为什么不是y(wxb)>0&#xff0c;比如y为1&#xff0c;wxb为0.5&#xff0c;大于0&#xff0c;则预测正确。 2.所以意思…...

车载网络安全指南 网络安全框架(二)

返回总目录->返回总目录<- 目录 一、概述 二、网络安全组织管理 三、网络安全活动 四、支撑保障 一、概述 汽车电子系统网络安全活动框架包含汽车电子系统网络安全活动、组织管理以及支持保障。其中,网络安全管理活动是框架的核心,主要指汽车电子系统生命周期各阶段…...

元数据、数据元、数据字典、数据模型及元模型的区别详解

在数据管理和分析领域&#xff0c;有许多相似的概念&#xff0c;如元数据、数据元、数据字典、数据模型和元模型。这些概念的定义和应用往往容易混淆。 数据元 数据元是通过一系列属性描述的数据单元&#xff0c;包括定义、标识、表示以及允许值等。这些属性帮助我们理解和使用…...

【百度智能体】零代码创建职场高情商话术助手智能体

一、前言 作为一个程序猿&#xff0c;工科男思维&#xff0c;走上职场后&#xff0c;总会觉得自己不会处理人际关系&#xff0c;容易背锅说错话&#xff0c;这时候如果有个助手能够时时刻刻提醒自己该如何说话如何做事情就好了。 而我们现在可以通过百度文心智能体平台构建各…...

实战项目: 负载均衡

0. 前言 这个项目使用了前后端,实现一个丐版的LeetCode刷题网站,并根据每台主机的实际情况,选择对应的主机,负载均衡的调度 0.1 所用技术与开发环境 所用技术: C STL 标准库 Boost 准标准库 ( 字符串切割 ) cpp- httplib 第三方开源网络库 ctemplate 第三方开源前端网…...

运维监控系统

做监控系统集成&#xff0c;持续更新ing 1.Prometheus k8s安装prometheusdocker部署prometheusthanos实现prometheus高可用部署 2.Grafana docker安装grafanagrafana的admin密码忘记了grafana使用mysql远程存储 3.Alertmanager 4.Consul 5.夜莺系统 6.时序数据库 6.1 …...

第3章 Unity 3D着色器系统

3.1 从一个外观着色器程序谈起 新建名为basic_diffuse.shader的文件&#xff0c;被一个名为basic_diffuse.mat的材质文件所引用&#xff0c;而basic_diffuse.mat文件则被场景中名为Sphere的game object的MeshRenderer组件所使用。 basic_diffuse.shader代码文件的内容如下所示…...

Qt项目天气预报(1) - ui界面搭建

ui中部 效果演示 ui效果 显示效果 控件列表 配合右图查看 居中对齐-label 设置label居中对齐(别傻傻的空格对齐了) 间距配置 widget03 外围的widget对象: 包含label 和 widget0301&#xff0c;如下图 widget0301 内围的widget对象&#xff0c;如下图 样式表 widget03 …...

一、从C语言到C++(一)

一、从C语言到C&#xff08;一&#xff09; C介绍C语言和C的联系C介绍 头文件命名空间定义命名空间使用命名空间中的名称使用using声明或指令命名空间与C语言的对比给命名空间起别名注意事项std 标准输入输出std::endl使用std::cout进行输出使用std::cin进行输入格式化输出 C介…...

MySQL(5)

聚合函数 GROUP BY 的使用 需求&#xff1a;查询各个部门的平均工资&#xff0c;最高工资SELECT department_id,AVG(salary),SUM(salary)FROM employeesGROUP BY department_id;需求&#xff1a;查询各个job_id的平均工资SELECT job_id,AVG(salary)FROM employeesGROUP BY jo…...

区块链之快照

定义 区块链快照是区块链技术中一个非常重要的概念,它可以帮助区块链系统提高性能和数据管理效率。 什么是区块链快照 区块链快照是指在某个时间点对整个区块链的状态进行保存和备份的过程。 快照会记录区块链上所有账户的余额、合约状态等信息,并将其序列化存储起来。 这样…...

自学前端第一天

HTML标签 ’HTML‘全程是‘hypertext Markup langage(超文本标记语言) HTML通过一系列的’标签&#xff08;也称为元素&#xff09;‘来定义文本、图像、链接。HTML标签是由尖括号包围的关键字。 标签通常成对存在&#xff0c;包括开始标签和结束标签&#xff08;也称为双标签…...

SQL Server几种琐

SQL Server 中的锁类型主要包括以下几种&#xff0c;它们用于控制并发访问和数据一致性&#xff1a; 1. 共享锁&#xff08;Shared Lock&#xff0c;S 锁&#xff09;&#xff1a; - 用于读取操作&#xff08;如 SELECT 语句&#xff09;。 - 允许多个事务同时读取同一资…...

redis 一些笔记1

redis 一、redis事务二、管道2.1 事务与管道的区别 三、主从复制3.13.2 权限细节3.3 基本操作命令3.4 常用3.4.1 一主几从3.4.2 薪火相传3.4.3 反客为主 3.5 步骤3.6 缺点 一、redis事务 放在一个队列里&#xff0c;依次执行&#xff0c;并不保证一致性。与mysql事务不同。 命…...

【计网复习】应用层总结(不含HTTP和错题重点解析)

应用层总结&#xff08;不含HTTP和错题重点解析&#xff09; 应用层简介 应用层的主要功能常见的应用层协议小林对于应用层通常的解释 网络应用模型 客户端-服务器模型&#xff08;Client-Server Model, C/S&#xff09; 特点优点缺点应用场景 对等网络模型&#xff08;Peer-to…...

carbondata连接数优化

一&#xff0c;背景 carbondata的入库采用arbonData Thrift Server方式提供&#xff0c;由于存在异常的入库segments但是显示状态是success&#xff0c;所以每天运行另一个博客中的脚本&#xff0c;出现连接超时&#xff0c;运行不正常&#xff0c;排查是每天连接数太多&#x…...

云和运维(SRE)的半生缘-深读实证02

这个标题不算太夸张&#xff0c;云计算和很多IT岗位都有缘&#xff0c;但是和运维&#xff08;SRE&#xff09;岗位的缘分最深。 “深读实证”系列文章都会结合一些外部事件&#xff0c;点明分析《云计算行业进阶指南》书中的内容。本次分享介绍了下列内容&#xff1a; 我以运维…...

java基础操作5——java自定义获取任意年、月、日的起始和结束时间

在实际项目开发过程中&#xff0c;获取任意时间的起始和结束时间是常用操作&#xff0c;尤其对于统计业务来说&#xff0c;更是必要操作&#xff0c;理解了时间自定义的规律&#xff0c;对于开发人员的效率提升是大有裨益的。 一.获取任意年的起始和结束时间 1.获取任意年的起…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

vscode(仍待补充)

写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh&#xff1f; debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

逻辑回归暴力训练预测金融欺诈

简述 「使用逻辑回归暴力预测金融欺诈&#xff0c;并不断增加特征维度持续测试」的做法&#xff0c;体现了一种逐步建模与迭代验证的实验思路&#xff0c;在金融欺诈检测中非常有价值&#xff0c;本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

Vue3 PC端 UI组件库我更推荐Naive UI

一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用&#xff0c;前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率&#xff0c;还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库&#xff08;Naive UI、Element …...