【进阶C语言】进阶指针--学会所有指针类型

news2024/11/20 2:46:19

 本节内容大致目录:

1.字符指针

2.指针数组(数组)

3.数组指针 (指针)--比较重要

4.函数指针--比较重要

5.函数指针数组--用的较少

6.指向函数指针数组的指针--只需要了解就可以

需要掌握每一种类型的符号和用处。


一、字符指针

前言:字符指针是一种指针,是众多指针类型中的一种。

1.字符指针的三种形式

(1)指向单个字符

#include<stdio.h>
int main()
{
	char ch = 'w';
	char* pc = &ch;
	printf("%c\n", *pc);
	printf("%d\n",*pc);
	return 0;
}

(2)指向一个字符数组

#include<stdio.h>
int main()
{
	char arr[] = "abcdef";
	char* pc = arr;
	printf("%s\n",pc);
	return 0;
}

(3)直接指向一个字符串

#include<stdio.h>
int main()
{
	char* pc = "abcdef";
	printf("%s\n",pc);
	return 0;
}

这里是把整个字符串放到pc的指针变量里面了吗?其实并不是,字符串跟数组一样,首元素就是首地址。只是把字符串第一个字符的地址存入指针变量中,通过首地址就可以找到整个字符串。

 

2.指向字符串和指向字符数组指针的区别 

我们看下面的代码:

#include <stdio.h>
int main()
{
  char str1[] = "abcdef.";//指向字符数组
  char str2[] = "abcdef.";//的字符指针
  const char *str3 = "abcdef.";//直接指向字符串
  const char *str4 = "abcdef.";//的字符指针
  if(str1 ==str2)
printf("str1 and str2 are same\n");
  else
printf("str1 and str2 are not same\n");
  
  if(str3 ==str4)
printf("str3 and str4 are same\n");
  else
printf("str3 and str4 are not same\n");
  
  return 0;
}

结果:

第一组:

比较的是地址,虽然数组str1和数组str2中的内容一模一样,但是他们的地址不一样,也就是所,地址所指向的内存不是同一块

 第二组:

1.C/C++会把常量字符串存储到单独的一个内存区域,当
几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。

2.str3和str4比的是地址,地址是存放别人的地址,也就是字符串的地址

 

二、指针数组

指针数组是数组,里面存放的数组都是指针类型---前面已经提到过

格式:

int* arr1[5];
char* arr2[6];

指针数组的应用:模拟二维数组、存放字符串

1.指针数组传参

当指针数组传参时,形参写成二级指针的形式。

三、数组指针

1.数组指针的定义

(1)数组指针,是指向数组的一种指针。

(2)数组指针的表示形式

#include<stdio.h>
int main()
{
	int arr1[10] = { 0 };
	int(*p1)[10] = &arr1;

	char* arr2[5] = { 0 };
	char* (*p2)[5] = &arr2;

	return 0;
}

数组名不就是首地址吗?为什么还需要取地址?因为&arr和arr的意义不同!我们通过下面的代码验证。
数组指针初始化必须指定数组大小
#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	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;
}

结果展示:

我们知道,指针的类型决定了指针+1或者-1的时候跳过多少字节。

比如:整形指针+1会跳过4字节,而数组指针的类型是数组,那么+1肯定是需要跳过一个数组(上述一个数组为40字节)。

因为&arr的含义是取出整个数组的地址,而arr只是数组首元素的地址,+1只会跳过一个数组类型。

 2.数组指针的使用

前言:常用于二维数组传参

(1)了解二维数组的特性

1.二维数组==一维数组的数组

2.二维数组的数组名==二维数组中第一行的地址

(2)二维数组的传参问题--引出数组指针

1)形参是二维数组的形式

#include <stdio.h>
void test(int arr[3][5],int r,int c)
{
	int i = 0;
	for (i=0;i<r;i++)
	{
		int j = 0;
		for (j=0;j<c;j++)
		{
			printf("%d ",arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	test(arr,3,5);
	return 0;
}

这是二维数组传参,形参部分也是二维数组的形式(行号可以省略,但是列不能省)

2)形参是数组指针的形式

#include <stdio.h>
void test(int (*arr)[5], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	test(arr, 3, 5);
	return 0;
}

1.二维数组的数组名是第一行元素的地址

2.第一行是一个一维数组,传的是一维数组的地址

3.是地址,所以是指针;是一维数组,要求类型是数组类型;结合起来就是数组指针(的类型)。(类比:整形变量传地址,形参需要用整形指针的类型来接收)

数组指针的一个作用:作为二维数组传参的形式参数


四、函数指针

前言:函数指针是指向函数的指针,也属于指针类型的一种

1.函数指针的定义

(1)了解函数名

&函数名与函数名的区别

#include<stdio.h>
void test()
{
	printf("hhhh\n");
}
int main()
{
	test();
	printf("%p\n",test);
	printf("%p\n",&test);
	return 0;

运行结果:

1.函数名就是函数的地址

2.&函数名与函数名没有区别,都是函数的地址

(2)函数指针变量

函数指针变量就是存放函数的地址,跟正常的指针变量一样

函数指针的格式:

#include<stdio.h>
void test()
{
	printf("hhhh\n");
}
int main()
{
	test();
	void (*p)() = &test;函数指针
	return 0;
}

图解:

1.函数指针的格式与数组指针的格式及其相似

2.去掉变量的名字,剩下的就是变量的类型

下面来认识函数指针的初步使用:

(3)通过函数指针调用函数

第一种:

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*p)(int,int) = &Add;
	int ret = 0;
	ret = (*p)(3, 5);//第一种
	printf("%d\n",ret);
	return 0;
}

 若通过*解引用操作,必须有括号(*号可以有很多个或者没有,对结果没有影响)

第二种:

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*p)(int,int) = &Add;
	int ret = 0;
	ret = p(3, 5);//第二种
	printf("%d\n",ret);
	return 0;
}

通过函数的地址(与Add(3,5)直接调用类似)调用函数

五、函数指针数组

1.函数指针数组定义

1.首先,是一个数组

2.是一个存放函数指针的数组,也就是存放函数地址的数组。

(1)代码定义:

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int Sub(int x,int y)
{
	return x - y;
}
int main()
{
	int (*p1)(int, int) = &Add;
	int (*p2)(int,int) = &Sub;
	//p1、p2是两个函数指针
	int (*p[4])(int, int) = {Add,Sub};
	//p就是函数指针数组
	return 0;
}

(2)图解:

2.函数指针数组的用途(转移表)

应用:做一个计数器

先看没有使用函数指针数组前的代码:

#include<stdio.h>
void menu()
{
	printf("****************************\n");
	printf("***  1. add      2. sub  ***\n");
	printf("***  3. mul      4. div  ***\n");
	printf("***  0. exit             ***\n");
	printf("****************************\n");
}
//加法
int Add(int x, int y)
{
	return x + y;
}
//减法
int Sub(int x, int y)
{
	return x - y;
}
//乘法
int Mul(int x, int y)
{
	return x * y;
}
//除法
int Div(int x, int y)
{
	return x / y;
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("ret = %d\n", ret);
			break;
		case 2:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("ret = %d\n", ret);
			break;
		case 3:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("ret = %d\n", ret);
			break;
		case 4:
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("ret = %d\n", ret);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误, 重新选择\n");
			break;
		}
	} while (input);

	return 0;
}

1.该计数器可以实现的功能:加减乘除

2.我们可以发现上面的代码很冗长,很多啰嗦的代码

观察发现:每个功能函数的返回值类型、函数参数类型及个数都一样,属于同类型函数,这就可以联想到数组。存放函数地址的数组,自然而然就是函数指针数组。

改进后:

#include<stdio.h>
void menu()
{
	printf("****************************\n");
	printf("***  1. add      2. sub  ***\n");
	printf("***  3. mul      4. div  ***\n");
	printf("***  0. exit             ***\n");
	printf("****************************\n");
}
//加法
int Add(int x, int y)
{
	return x + y;
}
//减法
int Sub(int x, int y)
{
	return x - y;
}
//乘法
int Mul(int x, int y)
{
	return x * y;
}
//除法
int Div(int x, int y)
{
	return x / y;
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		//函数指针数组 - 转移表
		int (*pfArr[])(int, int) = {NULL, Add, Sub, Mul, Div};
		// 为了与菜单选项对应上      0     1     2   3    4
		if (0 == input)
		{
			printf("退出计算器\n");
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入2个操作数:");
			scanf("%d %d", &x, &y);
			ret = pfArr[input](x, y);//函数指针数组的使用
			printf("ret = %d\n", ret);
		}
		else
		{
			printf("选择错误,重新选择!\n");
		}
	} while (input);
	return 0;
}

1.经过对比,明显改进后的更加简洁

2.函数指针数组的使用:直接通过数组下标的引用,再传参即可

六、指向函数指针数组的指针(了解)

1.给出定义(类比)

(1)指向整形指针数组的指针

#include<stdio.h>
int main()
{
	int a = 1;
	int b = 2;
	int c = 3;
	//整形指针数组
	int* arr[3] = {&a,&b,&c};
	//指向整形指针数组的指针
	int* (*p)[3] = &arr;
    return 0;
}

p为指向整形指针数组的指针变量,*说明p是指针。把*p去掉,剩下就是该指针变量指向的类型。

(2)指向函数指针数组的指针


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1059245.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Google ProtoBuf介绍

一、背景 前段时间了解到有公司用gRPC、Pulsar、Nacos、SkyWalking、OpenTelemetry、Prometheus、Envoy、Grafana、Sonar、PowerJob、Apollo 这些技术&#xff0c;也是Java路线的&#xff0c;很惭愧&#xff0c;这些我几乎都不了解&#xff0c;从13年以来玩Android、玩Python、…

Java 多输入框查询需求实现

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 多输入框查询 需求分析 任意一个输入框&#xff0c;输入内容点击搜索都可以精准搜索到对应的内容 代码实现 Controller接口编写 PostMapping("merchant/manage&…

文献阅读:RLAIF: Scaling Reinforcement Learning from Human Feedback with AI Feedback

文献阅读&#xff1a;RLAIF: Scaling Reinforcement Learning from Human Feedback with AI Feedback 1. 文章简介2. 方法介绍 1. 整体方法说明 3. 实验结果 1. RLHF vs RLAIF2. Prompt的影响3. Self-Consistency4. Labeler Size的影响5. 标注数据的影响 4. 总结 & 思考 文…

SpringCloudGateway网关中各个过滤器的作用与介绍

文章目录 RemoveCachedBodyFilterAdaptCachedBodyGlobalFilterNettyWriteResponseFilterForwardPathFilterRouteToRequestUrlFilterWebSocketRoutingFilterNettyRoutingFilterForwardRoutingFilterDispatcherHandler 是什么&#xff1f;⭐如何确定最后的路由路径&#xff1f;下…

一文拿捏Spring之AOP

Spring 1.Spring的理解 1.狭义上 指SpringFramework&#xff0c;特别的控制反转、依赖注入、面向切面、等特性 2.广义上 Spring家族的一系列产品&#xff0c;像SpringMVC、SpringBoot、SpringCloud等 2.aop &#x1f31f;面试题(aop): 简单介绍一下AOP&#xff1f; aop…

【userfaultfd】2021强网杯notebook

程序分析 老规矩&#xff0c;看下启动脚本 开启了 smep、smap、kaslr 保护&#xff0c;当然 kvm64 默认开启 kpti 保护 文件系统初始化脚本 #!/bin/sh /bin/mount -t devtmpfs devtmpfs /dev chown root:tty /dev/console chown root:tty /dev/ptmx chown root:tty /dev/tty…

SAAJ:SOAP with Attachments API for Java

介绍 支持带附件的SOAP消息Java接口&#xff08;SAAJ&#xff1a;SOAP with Attachments API for Java&#xff09;&#xff0c;定义了一套API&#xff0c;使开发者可以产生、消费遵从SOAP 1.1, SOAP 1.2, 和SOAP Attachments Feature的消息。 2019年SAAJ更改为新的名字&…

MIP精确算法的关键——确定界

目录 1.界是什么&#xff1f; 2. 如何更新上下界 2.1 以分支定界为框架的一系列算法 2.2 benders分解 MIP精确算法包含&#xff0c;分支定界、分支切割、分支定价还有benders分解等等。前者是以分支定界为框架的一类算法&#xff1b;后者是以分解为框架的一类算法。甚至还包…

Springboot学习笔记——1

Springboot学习笔记——1 一、快速上手Springboot1.1、Springboot入门程序开发1.1.1、IDEA创建Springboot项目1.1.2、官网创建Springboot项目1.1.3、阿里云创建Springboot项目1.1.4、手工制作Springboot项目 1.2、隐藏文件或文件夹1.3、入门案例解析1.3.1、parent1.3.2、starte…

嵌入式软件架构基础设施设计方法

大家好&#xff0c;今天分享一篇嵌入式软件架构设计相关的文章。 软件架构这东西&#xff0c;众说纷纭&#xff0c;各有观点。在我看来&#xff0c;软件架构是软件系统的基本结构&#xff0c;包含其组件、组件之间的关系、组件设计与演进的规则&#xff0c;以及体现这些规则的基…

Nginx高级 第一部分:扩容

Nginx高级 第一部分&#xff1a;扩容 通过扩容提升整体吞吐量 1.单机垂直扩容&#xff1a;硬件资源增加 云服务资源增加 整机&#xff1a;IBM、浪潮、DELL、HP等 CPU/主板&#xff1a;更新到主流 网卡&#xff1a;10G/40G网卡 磁盘&#xff1a;SAS(SCSI) HDD&#xff08;机械…

【C/C++笔试练习】——常量指针和指针常量、结构体内存分配、统计输入的字母个数、排序子序列、倒置字符串

文章目录 C/C笔试练习1.常量指针和指针常量&#xff08;1&#xff09;常量指针和指针常量的定义&#xff08;2&#xff09;判别常量指针和指针常量&#xff08;3&#xff09;常量指针和指针常量的特性 2.结构体内存分配&#xff08;4&#xff09;计算结构体大小&#xff08;5&a…

【计算机】CPU,芯片以及操作系统概述

1.CPU 什么是CPU? CPU&#xff08;Central Processing Unit&#xff09;是计算机系统的运算和控制核心&#xff0c;是信息处理、程序运行的最终执行单元&#xff0c;相当于系统的“大脑”。 CPU的工作流程&#xff1f; CPU 的工作流程分为以下 5 个阶段&#xff1a;取指令…

C++ -- 学习系列 关联式容器 set 与 map

一 关联式容器是什么&#xff1f; c 中有两种容器类型&#xff1a;关联式容器与序列式容器&#xff08;顺序容器&#xff09; 关联式中的容器是按照关键字来存储与访问的&#xff0c;序列式容器&#xff08;顺序容器&#xff09;则是元素在容器中的相对位置来存储与访问的。…

C++标准模板(STL)- 类型支持 ()

对象、引用、函数&#xff08;包括函数模板特化&#xff09;和表达式具有称为类型的性质&#xff0c;它限制了对这些实体所容许的操作&#xff0c;并给原本寻常的位序列提供了语义含义。 附加性基本类型及宏 实现定义的空指针常量 NULL 定义于头文件 <clocale> 定义于…

2.类与对象 拜访对象村

2.1 椅子大战 在图形接口画出正方形、圆形与三角形。当用户选点图形时&#xff0c;图形需要顺时针转360并依据形状的不同播放播放不同的AIF文件。胜者可以坐上名贵宝椅。 面向过程&#xff1a; rotate(shapeNum) {//旋转360 } playSound(shapeNum) {//查询播放哪个AIF文件//播…

嵌入式Linux应用开发-驱动大全-同步与互斥②

嵌入式Linux应用开发-驱动大全-同步与互斥② 第一章 同步与互斥②1.3 原子操作的实现原理与使用1.3.1 原子变量的内核操作函数1.3.2 原子变量的内核实现1.3.2.1 ATOMIC_OP在 UP系统中的实现1.3.2.2 ATOMIC_OP在 SMP系统中的实现 1.3.3 原子变量使用案例1.3.4 原子位介绍1.3.4.1…

pyqt5使用经验总结

pyqt5环境配置注意&#xff1a; 安装pyqt5 pip install PyQt5 pyqt5-tools 环境变量-创建变量名&#xff1a; 健名&#xff1a;QT_QPA_PLATFORM_PLUGIN_PATH 值为&#xff1a;Lib\site-packages\PyQt5\Qt\plugins pyqt5经验2&#xff1a; 使用designer.exe进行设计&#xff1…

Maven - MacOS 快速安装

配置信息 Maven 版本&#xff1a;3.6.3Maven 地址&#xff1a;Index of /dist/maven/maven-3IDEA&#xff1a;2023 Tips&#xff1a;Maven 版本最好不要超过 3.8.0&#xff0c;最新版 Maven 会不兼容一些配置信息。上面的 Maven 地址里可以选择自己想下载的版本&#xff08;这…