C语言:指针(二)

目录

  • 1.数组名的理解
  • 2.使用指针访问数组
  • 3.一维数组传参的本质
  • 4.二级指针
  • 5.指针数组
  • 6.字符指针变量
  • 7.数组指针变量
  • 8.二维数组传参的本质
  • 9.函数指针变量
  • 10.函数指针数组
  • 11.回调函数
  • 12.qsort函数
  • 13.使用回调函数模拟实现qsort函数

1.数组名的理解

int main() {int arr[] = { 1,2,3 };printf("%p\n", &arr[0]);printf("%p\n", arr);return 0;
}

在这里插入图片描述
从结果可以看出,&arr[0] == arr,这是用为数组名就是地址,而且是首元素的地址
但是,arr作为数组名在两种情况下不表示首元素的地址:

  • sizeof(数组名),sizeof中单独存放数组名表示求整个数组的大小,单位是字节
  • &arr,&arr是&后面直接加上一个数组名,它表示整个数组的地址。(&arr在数值上可能与&arr[0]相同,但是本质上是不一样的,即&arr[0] == arr != &arr )
    除此之外,数组名都表示首元素的地址。
    在这里插入图片描述
    其实arr与&arr的区别不在于直接打印的数值上,而在于运算上,
int main() {int arr[] = { 1,2,3 };printf("%p\n", &arr[0]);printf("%p\n", &arr[0]+1);printf("%p\n", arr);printf("%p\n", arr+1);printf("%p\n", &arr);printf("%p\n", &arr+1);return 0;
}

在这里插入图片描述
可以看出,arr和&arr[0]加一的结果都是跳过4个字节,而&arr则跳过12个字节(E8-F4 =(15 * 16 ^ 1 + 4 * 16 ^ 0)- ( 14*16^1 + 8 *16 ^0) =12).
&arr表示整个数组,加一表示跳过整个数组,数组一共3个int类型的数据,所以一次跳过12个字节。

2.使用指针访问数组

数组可以使用下标引用操作符来访问,比如:

int ret = arr[9];

既然数组名相当于地址,那么还可以这样访问:

int* p = &arr[9];
int ret = *p;

3.一维数组传参的本质

以前将数组作为参数传递给函数时需要将数组的数据个数一起传递给函数,那么,不传递数据个数可以吗?

void test(int arr[]) {int sz2 = sizeof(arr) / sizeof(arr[0]);printf("sz1 = %d\n", sz2);
}
int main() {int arr[] = {1,2,3,4,5};int sz1 = sizeof(arr) / sizeof(arr[0]);printf("sz1 = %d\n", sz1);test(arr);return 0;
}

在这里插入图片描述
从结果来看是不行的,
其实,将数组作为参数是将数组的首元素的地址作为参数传递给函数
所以,在函数内部使用sizeof(arr)实际上计算的是一个地址的大小。正因为参数部分的本质是指针,所以不能不传数据个数

4.二级指针

指针变量也是变量,是变量就有地址,所以二级指针是用来存放一级指针变量的地址的
在这里插入图片描述
画图说明:
在这里插入图片描述
对于二级指针的运算:

  • *ppa通过对ppa中的地址解引用,找到的是pa,*ppa访问的就是p
  • **ppa先对ppa解引用得到pa然后对pa解引用得到a

5.指针数组

存放整型的数组叫做整型数组
存放字符的数组叫做字符数组
那么存放指针的数组就叫做指针数组

在这里插入图片描述

6.字符指针变量

在指针的类型中有一种指针类型叫做字符指针char*
在这里插入图片描述
还有一种使用方法:

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

这种表示方法相当于将字符串看作为一个字符数组,指针变量p存放的不是字符串而是首字符’a‘的地址。
如果两个指针指向同一个字符串,那么不会开辟两个空间来存放,也就是说两个指针指向的是同一个内存

7.数组指针变量

存放整型数据地址的指针叫做整型指针
存放字符型数据的指针叫做字符指针
那么存放数组的地址的指针叫做数组指针

指针数组是数组,而数组指针是变量

int (*P)[5];

p先于*结合说明p是一个指针,再于【】和 int结合说明他是一个数组指针,数组元素类型是int。

数组自指针的初始化:

int main() {int arr[10] = { 0 };int(*p)[10] = &arr;return 0;
}

在这里插入图片描述
&arr == p

8.二维数组传参的本质

当我们将二位数组作为参数传递给函数时,其实传递的是第一行的地址(不是第一个元素的地址)

void test(int(*p)[3], int r, int c) {for (int i = 0; i < r; i++) {for (int j = 0; j < c; j++) {printf("%d ", *(*(p + i) + j));}printf("\n");}
}
int main() {int arr[2][3] = { {1,2,3},{2,3,4} };test(arr, 2, 3);return 0;
}

因为传递的是第一行元素的地址,那么该地址就该用函数指针来接收。
在这里插入图片描述

9.函数指针变量

函数也有地址,那么该地址就可以使用指针变量来接受,那么该指针变量就是函数指针变量。

int (*p)(int, int);

在这里插入图片描述
可以看出函数名就是函数的地址,也可以使用&函数名来获得函数的地址。
要将函数的地址存放起来,就可以使用函数指针变量
在这里插入图片描述
可以使用函数指针变量来调用函数

void test() {printf("haha\n");
}
int main() {int(*p)() = test;p();return 0;
}

在这里插入图片描述

10.函数指针数组

函数指针是用来存放函数的地址的,那么有很多函数,要将它们的地址存放在一起,那么就可以使用函数指针数组。

int (*p[10])(int, int);

p先于【】结合说明是一个数组,那么数组的内容就是int (*)().

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;
}
void Menu() {printf("**************************\n");printf("*****1.加法    2.减法*****\n");printf("*****3.乘法    4.除法*****\n");printf("*****0.退出          *****\n");printf("**************************\n");
}
int main() {int n = 0;int num1 = 0, num2 = 0;int(*p[5])(int, int) = { 0,Add,Sub,Mul,Div };do {Menu();printf("请输入要选择的功能:>");scanf("%d", &n);if (n >= 1 && n <= 4) {printf("\n请输入两个数用于计算:>");scanf("%d%d", &num1, &num2);printf("\n计算结果:%d\n", (*p[n])(num1, num2));}else if (n == 0) {printf("已退出\n");break;}else {printf("选择错误,重新选择:>");}} while (n);return 0;
}

这个代码实现了一个简单的计算器。
int( * p[5])(int, int) = { 0,Add,Sub,Mul,Div };
这段代码就是将四个函数的地址放在一个函数指针数组里面
( * p[n])(num1, num2);
这段代码函数指针数组来调用函数n表示想调用哪个函数,num1和num2就是传递给函数的参数

11.回调函数

回调函数就是一个通过函数指针调用的函数
如果将函数的指针作为一个参数传递给另一个函数,但这个指针被用来调用其所指的函数时,被调用的函数就是回调函数。

void test2() {printf("hahaha\n");
}
void test1(void (*test2)()) {printf("haha\n");test2();
}
int main() {test1(test2);return 0;
}

在这里插入图片描述
在main函数中,将test2的地址传递给test1,然后test1用函数指针变量来接收,然后调用test2函数,那么test2函数就是回调函数。

12.qsort函数

qsort函数的功能是将任意类型的数据排列,底层原理是快速排序。

#include<stdlib.h>void com_arr(const void* p1, const void* p2) {return *(int*)p1 - *(int*)p2;
}void print(int arr[], int sz) {for (int i = 0; i < sz; i++) {printf("%d ", arr[i]);}printf("\n");
}
int main() {int arr[] = { 5,2,1,7,3,4,6,14,8 };int sz = sizeof(arr) / sizeof(arr[0]);print(arr, sz);qsort(arr, sz, 4, com_arr);print(arr, sz);return 0;
}

在这里插入图片描述
qsort函数需要4个参数,第一个参数是数组首元素的地址,第二个参数是数组中元素的个数,第三个参数是数组中数据类型的大小,第四个参数是一个函数,作用是实现排序这种数据的方法。
在上述代码中排序整型数组中的数据,那么第四个参数就可以让两个整数相减,返回负数表示第一个元素小于第二个元素,反之大于。

13.使用回调函数模拟实现qsort函数

我们可以采用冒泡排序的方式来实现qsort函数

int Method(const void* p1, const void* p2) {//用以判断两个数时候该交换return (*(int*)p1 - *(int*)p2);
}void Swap(void* p1, void* p2 ,int sz) {for (int i = 0; i < sz; i++) {char tmp = *((char*)p1 + i);*((char*)p1 + i) = *((char*)p2 + i);*((char*)p2 + i) = tmp;}
}
void Maopao(void* arr, int sz, size_t num, int (*method)(void*, void*)) {for (int i = 0; i < sz - 1; i++) {for (int j = 0; j < sz - 1 - i; j++) {if (method((char*)arr + j * num, (char*)arr + (j+1) * num) > 0){Swap((char*)arr + j * num, (char*)arr + (j+1) * num, num);}}}
}
int main() {int arr[] = { 4,2,6,1,7,5,9,8 };int sz = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i < sz; i++) {printf("%d ", arr[i]);}printf("\n");Maopao(arr, sz, sizeof(arr[0]), Method);for (int i = 0; i < sz; i++) {printf("%d ", arr[i]);}return 0;
}

在这里插入图片描述

/考研势在必行/

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

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

相关文章

ASCII码

ASCII码 概念 ASCII(American Standard Code for Information Interchange)的缩写&#xff08;美国标准信息交换代码&#xff09;&#xff0c;已被国际标准化组织ISO采纳&#xff0c;作为国际通用的信息交换标准代码。 诞生背景 计算机对数据的识别、运算和存储都建立在二进…

SpringBoot+Vue实现el-table表头筛选排序(附源码)

&#x1f468;‍&#x1f4bb;作者简介&#xff1a;在笑大学牲 &#x1f39f;️个人主页&#xff1a;无所谓^_^ ps&#xff1a;点赞是免费的&#xff0c;却可以让写博客的作者开心好几天&#x1f60e; 前言 后台系统对table组件的需求是最常见的&#xff0c;不过element-ui的el…

FPGA之加法逻辑运算

由于FPGA需要被反复烧写&#xff0c;它实现组合逻辑的基本结构不可能像ASIC 那样通过固定的与非门来完成&#xff0c;而只能采用一种易于反复配置的结构。查找表可以很好地满足这一要求&#xff0c;目前主流FPGA都采用了基于SRAM 工艺的查找表结构。LUT本质上就是一个RAM。它把…

sparse transformer 常见稀疏注意力

参考&#xff1a; https://zhuanlan.zhihu.com/p/259591644 主要就是降低transformer自注意力模块的复杂度 复杂度主要就是 Q K^T影响的&#xff0c;稀疏注意力就是在Q点乘K的转置这模块做文章 下列式一些sparse transformer稀疏注意力方法 a、transformer原始的 &#xff0…

【Python实战】——Python+Opencv是实现车牌自动识别

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

MYSQL---日志

1.日志的概述 日志是MySQL数据库的重要组成部分。日志文件中记录着MySQL数据库运行期间发生的变化&#xff1b;也就是说用来记录MySQL数据库的客户端连接状况、SQL语句的执行情况和错误信息等。当数据库遭到意外的损坏时&#xff0c;可以通过日志查看文件出错的原因&#xff0…

Ambari动态给YARN分配计算节点

1.前言 YARN可用的计算节点数量并不总是等于 Hadoop集群节点数量&#xff0c;可以根据业务需求分配 YARN计算节点数量。 这里首先介绍一些前置知识&#xff1a; YARN中 ResourceManager 和 NodeManager是两个核心组件&#xff0c;其中 ResourceManager负责集群资源的统一管理…

2023下半年主品牌锋芒依旧,江南布衣打破既定天花板?

在过去的2023年里&#xff0c;服装板块令人意外的领涨消费大盘&#xff0c;国家统计局数据显示&#xff0c;上半年服装零售额同比增长12.8%&#xff0c;远超商品零售大盘的增速6.8%。 整体表现强劲的同时&#xff0c;“局部”表现是否也尽如人意。近日&#xff0c;作为时尚服装…

Arduino与processing之间的通信——进阶版

本次需要实现Arduino获取板子的偏转角度并通过串口发送给processing&#xff0c;processing部分根据传输过来的各个轴的偏转角度建立对应偏转角度的3D模型。 这就涉及了两个轴正负方向的偏转&#xff0c;我的实现思路是使用串口传输 字母数字 格式的信息&#xff0c;字母用来判…

Java和JavaScript之间的主要区别与联系

目录 概况 主要区别 联系 总结 概况 Java和JavaScript&#xff0c;尽管名字相似&#xff0c;但它们在编程世界中却扮演着截然不同的角色。Java&#xff0c;一种强类型、面向对象的编程语言&#xff0c;广泛应用于企业级应用和安卓应用开发。它的设计理念是一次编写&#x…

HLS的硬件加速器设计

完整可点击跳转 目录 硬件加速器的设计方法高层次综合HLSHLS与电路地对应关系HLS的设计规范HLS优化延迟优化降低单个循环的延迟循环展开(Unroll)循环展平(Flatten)多个循环的并行化循环合并循环函数化数据流执行(Dataflow)吞吐量优化循环/函数流水线数据流优化调试硬件加…

每日一练:LeeCode-203. 移除链表元素 【链表+虚拟头结点】

每日一练&#xff1a;LeeCode-203. 移除链表元素 【链表虚拟头结点】 思路设置虚拟头结点 本文是力扣 每日一练&#xff1a;LeeCode-203. 移除链表元素 【链表虚拟头结点】 学习与理解过程&#xff0c;本文仅做学习之用&#xff0c;对本题感兴趣的小伙伴可以出门左拐LeeCode-20…