C语言 指针进阶

目录

数组指针

指针数组访问数组元素

再次讨论数组名

数组指针访问一维数组(但是这样会很别扭)

访问二维数组元素

非数组指针访问

数组指针访问

 数组传参Demo

一维数组传参

二维数组传参

指针数组指针

字符指针

函数指针

函数指针调用时可以不用写*解引用

函数作为回调传参

解释: (*(void (*)())0)();

解释: void (*signal(int, void (*)(int)))(int);

函数指针数组指针


 

数组指针

数组指针——>指向数组的指针

    int ary[10] = {0};int p = ary;int *p1[10];         // p1是指针数组int(*p2)[10] = &ary; // p2是数组指针,(&ary取的是整个数组的地址)p2可以指向一个数组,该数组有10个元素,每个元素是int类型,数组指针的类型是int (*)[10]

指针数组访问数组元素

    int arr1[] = {1, 2, 3, 4, 5};int arr2[] = {2, 3, 4, 5, 6};int arr3[] = {3, 4, 5, 6, 7};int *parr[3] = {arr1, arr2, arr3}; // 指针数组for (int i = 0; i < 3; i++){for (int j = 0; j < 5; j++){/* *(p+i) ——> p[i] */// printf("%d ", *(parr[i] + j));printf("%d ", parr[i][j]);}printf("\n");}

再次讨论数组名

数组名通常表示的都是数组首元素的地址
但是有2个例外:
   1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小
   2. &数组名,这里的数组名表示的依然是整个数组,所以&数组名取出的是整个数组的地址

 例

    int arr[10] = {0};printf("%p\n", arr);     // 0000003199fff960printf("%p\n", arr + 1); // 0000003199fff964 加4个字节printf("%p\n", &arr[0]);     // 0000003199fff960printf("%p\n", &arr[0] + 1); // 0000003199fff964 加4个字节printf("%p\n", &arr);     // 0000003199fff960printf("%p\n", &arr + 1); // 0000003199fff988 加40个字节

数组指针访问一维数组(但是这样会很别扭)

    int ARR[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};int(*P)[10] = &ARR;int SZ = sizeof(ARR) / sizeof(ARR[0]);for (int i = 0; i < SZ; i++){/* 1 2 3 4 5 6 7 8 9 10 */printf("%d", *(*P + i)); // P指向数组的人,*P其实就相当于数组名,数组名又是数组首元素的地址。所以*p本质上就是数组首元素的地址}printf("\n");

思考一下:与其 int(*P)[10] = &ARR 这么麻烦,为什么不直接使用 int *p = arr 呢?这是因为数组指针的更适合和二维数组相结合,如下

访问二维数组元素

非数组指针访问

    int ARRAY[3][5] = {1, 2, 3, 4, 5,2, 3, 4, 5, 6,3, 4, 5, 6, 7};void printf1(int arr[3][5], int r, int c){for (int i = 0; i < r; i++){for (int j = 0; j < c; j++){printf("%d", arr[i][j]);}printf("\n");}}printf1(ARRAY, 3, 5);

数组指针访问

    int ARRAY[3][5] = {1, 2, 3, 4, 5,2, 3, 4, 5, 6,3, 4, 5, 6, 7};void printf2(int (*p)[5], int r, int c) // p指向第一个数组的地址{for (int i = 0; i < r; i++){for (int j = 0; j < c; j++){/*p+i 是每个一维数组的地址,*(p + i)解引用得到每个数组arr,arr又是每个数组的首元素    地址,进一步找到每个数组的每个元素的地址arr+j ——>  *(p + i) + j*/// printf("%d", *(*(p + i) + j)); // p每次加1,跳过 20个字节,即以每个数组为单位,每次加1跳过 5 个intprintf("%d", p[i][j]); // 同上面一致}printf("\n");}}

 数组传参Demo

一维数组传参

        void test(int arr[])//ok{}void test(int arr[10])//ok{}void test(int *arr)//ok{}void test2(int *arr[20])//ok{}void test2(int **arr)//ok{}int main(){int arr[10] = {0};int *arr2[20] = {0};test(arr);test2(arr2);}

二维数组传参

        void test(int arr[3][5]) ok{}void test(int arr[][]) err 形参的二维数组,行可以省略,列不能省略{}void test(int arr[][5]) ok{}总结:二维数组传参,函数形参的设计只能省略第一个,因为对一个二维数组,可以不知道有多少行,但是必须知道多少列void test(int *arr) err 传过来的是arr第一个数组的地址,不是一个整形的地址{}void test(int *arr[5]) err 这里表示指针数组,arr是五个指针的数组{}void test(int (*arr)[5]) ok{}void test(int **arr) ok{}int arr[3][5] = {0};test(arr);

指针数组指针

指向用于存放指针的数组的

    char *Arr[5] = {0};    // 指针数组char *(*Pc)[5] = &Arr; // Pc可以指向一个数组,该数组有5个元素,每个元素是char*类型,数组指针的类型是char* (*)[5]

字符指针

字符指针——>指向字符的指针

    char *p = "abcdef"; // 把字符串首字符a的地址,赋值给了pprintf("%c\n", *p); // a
    char *p1 = "abcdef";char *p2 = "abcdef";char arr1[] = "abcdef";char arr2[] = "abcdef";if (p1 == p2){printf("p1 == p2\n"); // p1 == p2}else{printf("p1 != p2\n");}if (arr1 == arr2){printf("arr1 == arr2\n");}else{printf("arr1 != arr2\n"); // arr1 != arr2}

函数指针

函数指针是指向函数的指针变量。函数指针可以让你像普通变量一样操作函数,也可以将函数作为参数传递给其他函数

 函数也有地址

int Add(int x, int y)
{return x + y;
}
printf("%p\n", &Add);       // 00007ff7f9ff1591
printf("%p\n", Add);        // 00007ff7f9ff1591

函数指针调用时可以不用写*解引用

    int (*pf)(int, int) = &Add; //  pf是一个函数指针,参数类型是(int, int) 返回类型为int的函数int res = (*pf)(2, 3);      // 调用函数指针所指向的函数/*函数指针调用时可以不用写*解引用int (*pf)(int, int) = Add 等同于 int *p = &num; p == &numint res = Add(2, 3);int res = pf(2, 3);*/int resp = pf(2, 3); // 可以不用写*printf("%d\n", res); // 5

函数作为回调传参

void calc(int (*pf)(int, int))
{int a = 3;int b = 5;int res = (*pf)(a, b);// int res = pf(a, b);printf("%d\n", res); // 8
}
/*
调用calc函数*/
calc(Add);

解释: (*(void (*)())0)();

  •     void (*)() 表示一个函数指针,指向一个无返回值的函数,并且该函数没有参数。
  •     (void (*)())0 将整数 0 强制转换为一个函数指针
  •     将这个函数指针解引用,这意味着它试图调用指向地址 0 的函数。

解释: void (*signal(int, void (*)(int)))(int);

    typedef unsigned int uint;typedef void (*pf_t)(int);void (*signal(int, void (*)(int)))(int);pf_t signal(int, pf_t); // 和上式相同
  • typedef unsigned int uint; 这句代码定义了一个新的数据类型 uint,它是无符号整型的别名,方便在程序中使用无符号整型时进行声明。
  • typedef void (*pf_t)(int); 这句代码定义了一个新的数据类型 pf_t,它是一个指向参数为整型、无返回值的函数指针类型。这样的定义可以方便地声明类似这种类型的函数指针变量。
  • void (*signal(int, void (*)(int)))(int); 这行代码声明了一个名为 signal 的函数,它接受两个参数:一个整型和一个指向参数为整型、无返回值的函数指针。它的返回类型是一个指向参数为整型、无返回值的函数指针。这种类型的函数通常用于信号处理。
  • pf_t signal(int, pf_t); 这行代码是函数 signal 的声明,它使用了之前定义的 pf_t 类型。它表明函数 signal 接受两个参数,一个整型和一个指向参数为整型、无返回值的函数指针,然后返回一个指向参数为整型、无返回值的函数指针。

函数指针数组指针

指向函数指针数组的指针

&先看函数指针数组:函数指针也是指针,把函数和指针放在数组中,其实就是函数指针数组

    int (*pf)(int, int) = Add;                      // pf是函数指针int (*arr[4])(int, int) = {Add, Sub, Mul, Div}; // arr就是函数指针的数组for (int i = 0; i < 4; i++){// int res = arr[i](2, 1); // 可以直接用arr[i]是因为,比如int (*pf)(int, int) = Add; 指针函数pf的类型是int (*)(int, int) 那么Add == pfint res = (*arr[i])(2, 1); // 也可以解引用,因为arr[i]指向的是函数地址 Add等函数地址printf("%d\n", res);       // 3, 1, 2, 2}

&指向函数指针数组的指针

    int (*pArr[])(int, int) = {0, Add, Sub, Mul, Div}; // 函数指针数组int (*(*pArr)[5])(int, int) = &pArr;               // *pArr是指针,指向数组个数为5的 函数指针类型 数组的地址

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

秒懂!用这10款思维导图软件,让头脑风暴如虎添翼!

世界上最糟糕的感觉之一就是忘记了一个伟大的点子。原本你只需把它记下来&#xff0c;但你当时确信自己绝不会忘记如此引人入胜的事物。然而&#xff0c;当这个想法从你的脑海彻底消失时&#xff0c;分分钟会让人崩溃。 如果你的想法有很多组成部分&#xff0c;比如一个大项目…

ARMday02(汇编语法、汇编指令)

汇编语法 汇编文件中的内容 1.伪操作&#xff1a;在汇编程序中不占用存储空间&#xff0c;但是可以在程序编译时起到引导和标识作用 .text .global .glbal .if .else .endif .data .word.... 2.汇编指令&#xff1a;每一条汇编指令都用来标识一个机器码&#xff0c;让计算机做…

深度学习之基于YoloV5的火灾检测系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 火灾检测系统基于YoloV5的介绍 火灾检测是一项重要的安全任务&#xff0c;它旨在及时发现和报警火灾风险。基于深度…

MyBatis-plus超神用法--一文带你玩转MP

前言 MyBatis-Plus是一个基于MyBatis的增强工具&#xff0c;提供了很多便捷的功能和增强的功能&#xff0c;以下是一些MyBatis-Plus的超神用法&#xff1a; 通用Mapper&#xff1a;MyBatis-Plus提供了通用Mapper的功能&#xff0c;可以通过继承BaseMapper接口&#xff0c;实现…

Gradle笔记 四 Gradle的核心 TASK

文章目录 Task任务入门任务的行为任务的依赖方式任务执行常见的任务&#xff08;*&#xff09;项目报告相关任务调试相关选项性能选项守护进程选项日志选项其它(*) 任务定义方式任务类型任务的执行顺序动态分配任务任务的关闭与开启任务的超时任务的查找任务的规则任务的 onlyI…

【RabbitMQ】常用消息模型详解

文章目录 AMQP协议的回顾RabbitMQ支持的消息模型第一种模型(直连)开发生产者开发消费者生产者、消费者开发优化API参数细节 第二种模型(work quene)开发生产者开发消费者消息自动确认机制 第三种模型(fanout)开发生产者开发消费者 第四种模型(Routing)开发生产者开发消费者 第五…

3.2-Docker Image概述

常用docker命令&#xff1a; 查看docker image有哪些 docker image ls Image的获取方式

docker 安装 minio (单体架构)

文字归档&#xff1a;https://www.yuque.com/u27599042/coding_star/qcsmgom7basm6y64 查询 minio 镜像 docker search minio拉取镜像 docker pull minio/minio创建启动 minio 容器 用户名长度至少为 3&#xff0c;密码长度至少为 8 docker run \ -p 9000:9000 \ -p 9090:909…

51单片机-中断

文章目录 前言 前言 #include <reg52.h> #include <intrins.h>sbit key_s2P3^0; sbit flagP3^7;void delay(unsigned int z){unsigned int x,y;for(xz;x>0;x--)for(y114;y>0;y--); }void int_init(){EA1;EX11;IT11;}void main(){int_init();while(1){if (key…

zabbix监控安装-linux

zabbix6.4中文文档1. 简介 (zabbix.com) Zabbix 是一个企业级的开源分布式监控解决方案。 1.zabbix结构体系 Server&#xff1a; server 是存储所有配置、统计和操作数据的中央存储库。 Proxy&#xff1a; zabbix proxy可以代替 Zabbix server 收集性能和可用性数据。p…

引用类型;强引用;软引用;弱引用和虚引用

概述 平时在编写代码的时候内存都是由jvm管理&#xff0c;对象的回收也是jvm在管理&#xff1b; 但是有些时候jvm无法回收对象&#xff0c;最后就会抛出oom异常. 那么那些回收不了的对象肯定有区别于能回收的对象&#xff1b; 先上一波引用类型介绍 强引用 比如平常我们直…

【pyspider】爬取ajax请求数据(post),如何处理python2字典的unicode编码字段?

情景&#xff1a;传统的爬虫只需要设置fetch_typejs即可&#xff0c;因为可以获取到整个页面。但是现在ajax应用越来越广泛&#xff0c;所以有的网页不能用此种爬虫类型来获取页面的数据&#xff0c;只能用slef.crawl()来发起http请求来抓取数据。 直接上例子&#xff1a; 可以…

大数据学习之Spark性能优化

文章目录 Spark三种任务提交模式宽依赖和窄依赖StageSpark Job的三种提交模式 Shuffle机制分析未优化的Hash Based Shuffle优化后的Hash Based ShuffleSort-Based Shuffle Spark之checkpointcheckpoint概述checkpoint与持久化的区别checkPoint的使用checkpoint源码分析 Spark程…

SSM之spring注解式缓存redis

&#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Linux》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一定基础的程序员&#xff0c;这…

【Sql】sql server数据库提示:执行Transact-SQL语句或批处理时发生了异常。 无法打开数据库msdb,错误:926。

【问题描述】 打开sql server2008r2数据库的时候&#xff0c; 系统提示执行Transact-SQL语句或批处理时发生了异常。 无法打开数据库msdb&#xff0c;错误&#xff1a;926。 【概念理解】 首先MSDB数据库是的作用&#xff1a; 用于给SQL Server代理提供必要的信息来运行调度警…

项目实战:组件扫描(4)-筛选带有RequestMapping注解的bean实例

1、ControllerDefinition package com.csdn.mymvc.core; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; //假设有一个uri是&#xff1a;/fruit…

PTA_乙级_1001_C++

思路&#xff1a;使用判断语句即可&#xff0c;使用while进行循环&#xff0c;终止条件是n不等于1&#xff0c;然后用if-else判断奇数偶数 #include <iostream> using namespace std;int main(){int n;int count0;cin>>n;while(n!1){if(n%20){n/2;}else{n3*n1;n/2…

技术分享 | web自动化测试-PageObject 设计模式

为 UI 页面写测试用例时&#xff08;比如 web 页面&#xff0c;移动端页面&#xff09;&#xff0c;测试用例会存在大量元素和操作细节。当 UI 变化时&#xff0c;测试用例也要跟着变化&#xff0c; PageObject 很好的解决了这个问题。 使用 UI 自动化测试工具时&#xff08;包…

【python基础】时间模块的time的下面的方法使用解析

文章目录 一、time三种表现形式二、time模块常用的方法1.时间戳&#xff08;timestamp&#xff09;表现方式time.time()time.mktime() 2.时间元组&#xff08;struct_itme&#xff09;表现方式time.localtime()time.gmtime()time.strptime(str,format) 3.默认时间字符串&#x…

『MySQL快速上手』-③-库的操作

文章目录 1.创建数据库2.创建数据库案例3.字符集和校验规则3.2 校验规则对数据库的影响3.2.1 进行查询3.2.2 进行排序 4.字符集和检验规则的作用5.操纵数据库5.1 查看数据库5.2 显示创建语句5.3 修改数据库5.4 数据库删除 6.备份与恢复6.1 备份6.2 还原6.3 注意事项 7.查看数据…