C语言笔记15

指针2

1.数组名的理解

int arr[ 10 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 };
int *p = &arr[ 0 ];17391692786

arr是数组名,数组名是首元素地址,&arr[0]就是取出首元素的地址放在指针变量p中。

#include <stdio.h>
int main()
{int arr[2][10] = { 1,2,3,4,5,6,7,8,9,10 };//一行为10个元素,一共两行;//printf("&arr[0] = %p\n", &arr[0]);//printf("arr = %p\n", arr);printf("%p\n", &arr[0]);//arr[0]是第一行一维数组的数组名,&数组名,打印第一行数组的地址(十六进制)  printf("%p\n", arr[0]);//arr[0]是第一行的数组名,数组名是首元素的地址,打印第一行数组第一个元素的地址printf("%p\n", *arr);//arr是二维数组的数组名,*arr代表取出二维数组的首元素 这个二维数组由两个一维数组组成,即*arr代表arr[0],*arr==*(arr+0)==arr[0]printf("%d\n", *arr);//地址打印为十进制printf("==============\n");printf("%p\n", &arr[1]); //arr[1]是第二行一维数组的数组名,&数组名,打印第二行数组的地址printf("%p\n", arr[1]);//arr[1]是第二行的数组名,数组名是首元素的地址,打印第二行数组第一个元素的地址printf("%p\n", *(arr + 1));//arr是二维数组的数组名,arr + 1代表访问第二行元素,*(arr + 1)获取第二行元素数组的地址  *(arr + 1)==arr[1]printf("%d\n", *(arr + 1));//地址打印为十进制printf("==============\n");printf("%p\n", *(arr + 1)+1);//*(arr + 1)获取第二行元素数组的地址   *(arr + 1)+1获取第二行第二个元素的地址printf("%p\n", &arr[1][1]);//获取第二行第二个元素的地址printf("==============\n");printf("%d\n", sizeof(arr));//特例:sizeof(数组名)表示计算arr数组的大小(数组空间内存)printf("%d\n", sizeof(arr[0]));//特例:sizeof(数组名)表示计算arr数组的大小(数组空间内存) arr[0]是第一行一维数组的数组名printf("%d\n", sizeof(arr[1]));//特例:sizeof(数组名)表示计算arr数组的大小(数组空间内存) arr[1]是第二行一维数组的数组名printf("%p\n", &arr);//特例:&数字名  表示整个二位数组的地址printf("==============\n");printf("%p\n", &arr[0] + 1);//&arr[0]获取第一行数组的地址  &arr[0] + 1获取第二行数组的地址printf("%p\n", &arr + 1);//&arr获取二维数组的地址  &arr+ 1 跳过此二维数组到下一个地方(跳过20*4=80Byte)return 0;
}

其实数组名就是数组⾸元素(第⼀个元素)的地址是对的,但是有两个例外:

sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩, 单位是字节
&数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素 的地址是有区别的)
除此之外,任何地⽅使⽤数组名,数组名都表⽰⾸元素的地址。
2.一维数组传参的本质
#include < stdio.h>
void test(int arr[])
{int sz2 = sizeof(arr) / sizeof(arr[0]);printf("sz2 = %d\n", sz2);}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz1 = sizeof(arr) / sizeof(arr[0]);printf("sz1 = %d\n", sz1);test(arr);return 0;
}
//运行结果可以发现 sz1=10,sz2=1;

为什么这个函数的结果不对呢?

这就要搞清楚函数中传的参数是啥,也就是函数中传的参数是啥,从上方代码可test(arr);arr是一个一维数组,也是个数组名,刚从1.数组名的理解知道除了两个例外,任何地⽅使⽤数组名,数组名都表⽰⾸元素的地址。所以说传给函数test是数组的地址,那么sizeof(地址)=4或8(要看在32位系统还是64位系统),int sz2 = sizeof(arr) / sizeof(arr[0]);这个arr和arr[0]都是地址,所以sz2=1;

再看一个:

void test1(int arr[])//参数写成数组形式,本质上还是指针
{printf("%d\n", sizeof(arr));
}
void test2(int* arr)//参数写成指针形式
{printf("%d\n", sizeof(arr));//计算⼀个指针变量的⼤⼩}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };test1(arr);test2(arr);return 0;
}
//运行结果可以发现 sizeof(arr)=4;说明一维数组传参传的就是地址
总结:
数组名是数组⾸元素的地址;那么在数组传参 的时候,传递的是数组名,也就是说本质上: 一维数组传参传递的是数组⾸元素的地址 ⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。
3.二级指针
指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪⾥?
这就是 ⼆级指针
二级指针通俗来讲就是指针的指针,存放指针的指针。
其实三级指针就是存放 ⼆级指针地址的指针;依次往后推。
int main()
{int a = 100;int* pa = &a;int** pb = &pa;printf("a的地址是:%p\n", pa);printf("pa的地址是:%p", pb);return 0;
}

画个图描述一下

4.指针数组
先说下整形数组:存放整形的数组 int arr1[5] ={1,2,3,4,5}      5个int类型

字符数组:存放字符的数组char arr1[5]={'a','b','c','d','e'}      5个char类型                                          那么指针数组就是存放指针的数组呗, 那就有整形指针数组,字符指针数组等等;                     int* arr1[5]={指针1,指针2,指针3,指针4,指针5}                     5个 int* 类型   

        指针数组的每个元素都是⽤来存放地址(指针)的,⼜可以指向⼀块区域。用以下代码说一下指针数组。

int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };//数组名是数组⾸元素的地址,类型是int*的,就可以存放在parr数组中int* parr[3] = { arr1, arr2, arr3 };int i = 0;int j = 0;for (i = 0; i < 3; i++){for (j = 0; j < 5; j++){printf("%d ", parr[i][j]);}printf("\n");}return 0;}

        parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型⼀维数组,parr[i][j]就是整型⼀维数 组中的元素。 上述的代码模拟出⼆维数组的效果,实际上并⾮完全是⼆维数组,因为每⼀⾏并⾮是连续的。
5.数组指针
前面介绍过整形指针类型 int*,字符指针 类型 char*,那么数组指针 类型该如何表示呢
数组指针意思就是 数组的指针,又叫做数组的地址。
整形数组指针:int (*arr)[5];
字符数组指针:char(*arr)[5];
解释:
p先和*结合,说明p是⼀个指针变量,然后指针指向的是⼀个⼤⼩为5个整型的数组。所以p是
⼀个指针,指向⼀个数组,叫 数组指针。
这⾥要注意:[ ]的优先级要⾼于*号的,所以必须加上()来保证p先和*结合
见以下代码所示:
#include <stdio.h>//整形数组指针
//int arr[10] = {0};
//&arr;//得到的就是数组的地址
//找个变量来接收找个数组地址 → int(*p)[10]=&arr
// int (*p) [10] = &arr;
// |     |    |
// |     |    |
// |     |    p指向数组的元素个数
// |     p是数组指针变量名
// p指向的数组的元素类型int main()
{int arr[5] = { 1, 2, 3, 4, 5 };  // 定义一个整形数组int(*p)[5];  // 定义一个指向包含5个整形元素的数组的指针p = &arr;  // 将指针指向数组//int(*p)[5]=&arr;// 通过指针访问数组中的元素for (int i = 0; i < 5; i++){//printf("arr[%d] = %d\n", i, (*p)[i]);printf("arr[%d] = %d\n", i, p[0][i]);}return 0;
}//字符数组指针
int main1() 
{char str[20] = "Hello, world!";  // 定义一个字符数组char(*p)[20];  // 定义一个指向包含20个字符的数组的指针p = &str;  // 将指针指向数组// 通过指针访问数组中的元素printf("String: %s\n", *p);//printf("String: %p\n", p);//printf("String: %p\n", *p);//printf("String: %p\n", p[0]);//printf("String: %c\n", **p);printf("String: %c\n", *p[0]);//**p== *p[0]return 0;
}
6.⼆维数组传参的本质
对于一维 数组传参的本质 传递的是数组⾸元素的地址,上面已经介绍过了,接下来说一下 ⼆维数组传参的本质。
       ⾸先我们再次理解⼀下⼆维数组,⼆维数组其实可以看做是每个元素是⼀维数组的数组,也就是⼆维数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组。
就是可以把第一行看作第一个元素记作 arr[0]
                  第二行看作第二个元素记作 arr[1]
                  第三行看作第三个元素记作 arr[2]
根据数组名是首元素的地址规则,那么对于二维数组的数组名也是首元素的地址,首元素就是第一行,那么就是第一行的地址,arr[0]就是第一行的数组名。 第⼀⾏的⼀维数组的类型就是 int [5] ,所以第⼀⾏的地址的类 型就是数组指针类型 int(*)[5],一维 数组传参的本质 传递的是数组⾸元素的地址,那么二维数组传递的也是首元素地址只不过这个首元素是个一维数组,也就是传递时第一行一维数组的地址(arr[0]的地址), 那么形参也是可以写成指针形式的
总体来说:
一维数组传参的本质传递的是数组⾸元素的地址;
二维数组传参的本质传递的是第一行一维数组的地址。
void test(int a[3][5], int r, int c)
{int i = 0;int j = 0;for (i = 0; i < r; i++){for (j = 0; j < c; j++){printf("%d ", a[i][j]);}printf("\n");}
}
//这是之前的传参写法,我们知道了二维数组传参的本质,可以修改如下
void test1(int(*p)[5], int r, int c)
{int i = 0;int j = 0;for (i = 0; i < r; i++){for (j = 0; j < c; j++){//printf("%d ", p[i][j]);printf("%d ", *(*(p + 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);test1(arr, 3, 5);return 0;
}

7.函数指针

前面说过数组指针,是代表数组的指针,那么 函数指针就是函数的指针(地址),则 函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的。 
       整形数组的指针类型表示为int(*)[5],那么函数指针类型该如何表示呢,类比来看:
int(*)(int )(int )。
如何创建函数指针变量,如下:
void test()
{printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)() = test;int Add(int x, int y)
{return x + y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//函数名就是函数的地址&Add==Add 都可以获取函数的地址,x和y写上或者省略都是可以的 int (*pf3) (int x, int y)
|     |     ------------
|     |       |
|     |       pf3指向函数的参数类型和个数的交代
|     函数指针变量名
pf3指向函数的返回类型int (*) (int x, int y) //pf3函数指针变量的类型

ps:函数名就是函数的地址,可以用函数名来代表函数的地址,也可以用 &函数名 来表示

函数指针变量的使⽤, 见一下代码
#include <stdio.h>// 一个简单的函数,接收两个整数并返回它们的和
int add(int x, int y)
{return x + y;
}int main()
{// 声明一个函数指针,指向接收两个int参数并返回int的函数//int (*pf)(int, int);// 将函数指针指向具体的函数add//pf = add;int (*pf)(int, int) = add;  // int (*pf)(int, int)=&add;// 使用函数指针调用函数int result = pf(10, 20);int result1 = (*pf)(10, 30);  //注意一定是加上()不能是 *pf(10, 30)进行调用printf("Result: %d\n", result);  // 输出:Result: 30printf("Result1: %d\n", result1);  // 输出:Result: 40return 0;
}

7.函数指针数组

       数组是⼀个存放相同类型数据的存储空间,我们已经学习了指针数组, 函数指针数组,是把函数的指针存放在数组里,一般情况是定义多个函数,把多个函数的地址存放在数组中,方便调用。
函数指针数组的定义:int (*parr1[ 3 ])(int ,int );
parr1 先和 [ ] 结合,说明 parr1是数组,数组的内容是 int (*)( int ,int ) 类型的函数指针
函数指针数组应用:设计一个计算器
//转移表  (设计一个简易计算器)
#include <stdio.h>
#include <stdlib.h>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. add    2. sub  *****\n");printf("******   3. mul    4. diV  *****\n");printf("******   0. exit           *****\n");printf("********************************\n");
}
int main()
{int input = 0;int (*pf[5])(int,int) = {0, add, sub, mul, diV};do{  menu();int x, y = 0;scanf("%d", &input);if (input > 0 && input < 5){printf("请输入操作数:");scanf("%d %d", &x, &y);int ret = pf[input](x, y);printf("%d\n", ret);}else if (input == 0){printf("退出计算器");}else{printf("输入错误,重新输入\n");}} while(input);return 0;
}

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

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

相关文章

【激活函数--中】激活函数和阶跃函数的可视化及对比

文章目录 一、Python中绘制阶跃函数的图形二、实现和可视化Sigmoid函数2.1 Python实现2.2 可视化Sigmoid函数 三、比较Sigmoid函数与阶跃函数3.1 Sigmoid函数与阶跃函数的差异3.2 Sigmoid函数与阶跃函数的共同点 一、Python中绘制阶跃函数的图形 在Python中实现阶跃函数的代码…

NodeJS编写后端接口

技术栈 1.express&#xff1a;Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建 各种 Web 应用&#xff0c;和丰富的 HTTP 工具&#xff0c;使用 Express 可以快速地搭建一个完整功能的网站。 2.mysql&#xff1a;用于操作MySQL数据库 3.bod…

高校普法|基于SSM+vue的高校普法系统的设计与实现(源码+数据库+文档)

高校普法系统 目录 基于SSM&#xff0b;vue的高校普法系统的设计与实现 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 3律师功能模块 4学生功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获…

2024中国(厦门)国际医用消毒及感控设备展览会

2024中国&#xff08;厦门&#xff09;国际医用消毒及感控设备展览会 2024 China (Xiamen) International Medical Disinfection And Infection Control Exhibition 致力于打造医用消毒及感控设备产业采购一站式平台 时 间&#xff1a;2024年11月1-3日 November 1-3, 2024 …

揭秘奇葩环境问题:IDEA与Maven版本兼容性解析

1.问题描述 最近在实现通过Java爬虫获取网页源码&#xff0c;然后紧接着将源码转换为图片上传到OSS服务器&#xff0c;其中探索了很多办法&#xff0c;但是在实现过程中遇到一个奇葩问题&#xff0c;就是我无论下载任何Maven依赖&#xff0c;都无法正常下载&#xff0c;简直是…

零基础HTML教程(32)--HTML5语义化标签

文章目录 1. div时代2. div的缺点3. 语义化标签4. 语义化标签有哪些5. 实战演练6. 小结 1. div时代 我是2009年开始学习网页开发的&#xff0c;那时候HTML里面到处是div。 这么说吧&#xff0c;那时候div就是网页的骨架&#xff0c;支撑着网页的主结构。 2. div的缺点 div作…

vulhub靶机struts2环境下的s2-032(CVE-2016-3081)(远程命令执行漏洞)

影响范围 Struts 2.3.19至2.3.20.2、2.3.21至2.3.24.1和2.3.25至2.3.28 当用户提交表单数据并验证失败时&#xff0c;后端会将用户之前提交的参数值使用OGNL表达式%{value}进行解析&#xff0c;然后重新填充到对应的表单数据中。 漏洞搭建 没有特殊要求&#xff0c;请看 (3…

C#知识|上位机UI设计-详情窗体设计思路及流程(实例)

哈喽,你好啊,我是雷工! 上两节练习记录了登录窗体和主窗体的实现过程,本节继续练习内容窗体的实现,以下为练习笔记。 01 详情窗体效果展示: 02 添加窗体并设置属性 在之前练习项目的基础上添加一个Windows窗体,设置名称为:FrmIPManage.cs 设置窗体的边框和标题栏的外…

Vision Mamba 代码调试---Pycharm+AutoDL

《AutoDL使用手册》 1. 服务器租用与配置 先上项目链接&#xff1a; GitHub - hustvl/Vim: Vision Mamba: Efficient Visual Representation Learning with Bidirectional State Space Model 1.1 服务器租用与配置 根据环境要求&#xff0c;去租一个服务器&#xff1a;AutoDL算…

基于FPGA的音视频监视器,音视频接口采集器的应用

① 支持1路HDMI1路SDI 输入 ② 支持1路HDMI输出 ③ 支持1080P高清屏显示实时画面以 及叠加的分析结果 ④ 支持同时查看波形图&#xff08;亮度/RGB&#xff09;、 直方图、矢量图 ⑤ 支持峰值对焦、斑马纹、伪彩色、 单色、安全框遮幅标记 ⑥ 支持任意缩放画面&#xff0c;支…

深入解析RedisJSON:在Redis中直接处理JSON数据

码到三十五 &#xff1a; 个人主页 JSON已经成为现代应用程序之间数据传输的通用格式。然而&#xff0c;传统的关系型数据库在处理JSON数据时可能会遇到性能瓶颈。为了解决这一问题&#xff0c;Redis推出了RedisJSON模块&#xff0c;它允许开发者在Redis数据库中直接存储、查询…

文心一言 VS 讯飞星火 VS chatgpt (259)-- 算法导论19.3 2题

二、使用聚合分析来证明 FIB-HEAP-DECREASE-KEY 的 O(1) 摊还时间是每一个操作的平均代价。如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; FIB-HEAP-DECREASE-KEY 是斐波那契堆&#xff08;Fibonacci Heap&#xff09;操作中的一个&#xff0c;它允许我们减少堆…

Excel如何设置密码保护【图文详情】

文章目录 前言一、Excel如何设置密码保护&#xff1f;二、Excel如何取消密码保护&#xff1f;总结 前言 在软件项目开发过程中&#xff0c;会输出很多技术文档&#xff0c;其中也包括保密级别很高的服务器账号Excel文档。为了确保服务器账号相关的Excel文档的安全性&#xff0…

1-1ARM开发环境搭建(GD32)

1:安装MDK最好是5.27以及以上版本&#xff0c;避免后续学习中出现相关错误 2&#xff1a;安装芯片支持包 双击安装即可&#xff0c;也可以是默认路径&#xff0c;也可以自己更改路径 3&#xff1a;安装jlink下载器驱动&#xff08;下载调试器&#xff09; 具体安装步骤如下所示…

汇昌联信科技:拼多多电商的运营流程有哪些?

在当今互联网高速发展的时代&#xff0c;电商平台层出不穷&#xff0c;其中拼多多以其独特的团购模式和低价策略迅速崛起&#xff0c;成为众多消费者和商家的新宠。那么&#xff0c;拼多多电商的运营流程究竟包含哪些环节呢?接下来&#xff0c;我们将从商品上架、营销推广、订…

ctfshow web入门 php反序列化 web267--web270

web267 查看源代码发现这三个页面 然后发现登录页面直接admin/admin登录成功 然后看到了 ///backdoor/shell unserialize(base64_decode($_GET[code]))EXP <?php namespace yii\rest{class IndexAction{public $checkAccess;public $id;public function __construct(){…

一篇详解Git版本控制工具

华子目录 版本控制集中化版本控制分布式版本控制 Git简史Git工作机制Git和代码托管中心局域网互联网 Git安装基础配置git的--local&#xff0c;--global&#xff0c;--system的区别 创建仓库方式1git init方式2git clone git网址 工作区&#xff0c;暂存区&#xff0c;本地仓库…

k8s v1.20二进制部署 部署 CNI 网络组件 部署 Calico

一、部署 flannel 1.1.K8S 中 Pod 网络通信 ●Pod 内容器与容器之间的通信 在同一个 Pod 内的容器&#xff08;Pod 内的容器是不会跨宿主机的&#xff09;共享同一个网络命名空间&#xff0c;相当于它们在同一台机器上一样&#xff0c;可以用 localhost 地址访问彼此的端口。…

ROS2 conda 环境下报 No module named ‘catkin_pkg‘

这里使用的环境是&#xff1a;Ubuntu 22.04, ROS2, Conda python 虚拟环境是3.10 ~/.bashrc 里注释掉 conda&#xff0c;并添加 export PYTHONPATH$PYTHONPATH:/usr/lib/python3/dist-packages sudo gedit ~/.bashrc source ~/.bashrc

springboot3 集成spring-authorization-server (一 基础篇)

官方文档 Spring Authorization Server 环境介绍 java&#xff1a;17 SpringBoot&#xff1a;3.2.0 SpringCloud&#xff1a;2023.0.0 引入maven配置 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter…