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.hqwc.cn/news/700253.html

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

相关文章

Oracle 临时表空间的管理

Oracle 临时表空间的管理 临时表空间的处理 1.创建一个新的temporary tablespace; create temporary tablespace tp tempfile ...... size 10m autoextend on; 2.改变数据库的默认临时表空间 alter database default temporary tablespace tp; 3。drop tablespace temp; …

Python数据分析常用模块的介绍与使用

Python数据分析模块 前言一、Numpy模块Numpy介绍Numpy的使用Numpy生成数组ndarrayarray生成数组arange生成数组random生成数组其他示例 关于randint示例1示例2 关于rand Numpy数组统计方法示例 二、Pandas模块pandas介绍Series示例 DataFrame示例 三、其他模块Matplotlib/Seabo…

【JAVA】数组的定义与使用

前一篇我们讲述了方法的使用和递归&#xff0c;这一讲 我们来叙述一下数组相关知识点。最近更新较快&#xff0c;大家紧跟步伐哦~~ 1. 数组的基本概念 1.1 为什么要使用数组 假设现在要存5个学生的javaSE考试成绩&#xff0c;并对其进行输出&#xff0c;按照之前掌握的知识点&…

uniapp的底部弹出层实现保姆式教程

实现照片&#xff1a; 此过程先进入uniapp官网&#xff0c;找到扩展组件 打开找到里面的uni-popup和uni-icons 点击进入&#xff0c;下载&安装 点击下载并导入HBuilderX 导入到你使用的目录&#xff0c;如test目录 同样将uni-icons点击下载并导入HBuilderX 点击合并 此时te…

运输层(计算机网络谢希仁第八版)——学习笔记五

课件&#xff1a;课程包列表 (51zhy.cn) 目录 运输层协议概述 用户报协议UDP 传输控制协议TCP概述 可靠传输的工作原理 TCP可靠传输的实现 TCP的流量控制 TCP的拥塞控制 TCP的运输连接管理 运输层协议概述 进程之间的通信 运输层的位置——只有位于网络边缘部分的主机的协议栈才…

Jmeter+Grafana+Prometheus搭建压测监控平台

本文不介绍压测的规范与技术指标&#xff0c;本文是演示针对Jmeter如何将压测过程中的数据指标&#xff0c;通过Prometheus采集存储&#xff0c;并在Granfan平台进行仪表盘展示; 介绍 系统压测属于日常项目开发中的一个测试环节&#xff0c;使用测试工具模拟真实用户行为&…

Day 46 139.单词拆分

单词拆分 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict&#xff0c;判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。 说明&#xff1a; 拆分时可以重复使用字典中的单词。 你可以假设字典中没有重复的单词。 示例 1&#xff1a; 输入: s “leet…

小红书自动私信获客,打造个人品牌

在当今这个内容为王、社交至上的时代&#xff0c;小红书作为新兴的社交电商平台&#xff0c;凭借其独特的社区氛围和强大的种草能力&#xff0c;成为了众多KOL、商家以及个人品牌打造的首选平台。想要在小红书上脱颖而出&#xff0c;精准引流获客&#xff0c;利用自动私信功能不…

国外新闻媒体推广:多元化媒体分发投放-大舍传媒

前言 &#xff1a;随着全球化的进程&#xff0c;国外新闻市场呈现出快速发展的趋势。在这个趋势下&#xff0c;国外新闻媒体推广成为了各行业企业宣传业务的重要一环。本文将重点介绍大舍传媒的多元化媒体分发投放服务&#xff0c;以及对国外新闻媒体推广的意义。 1. 多元化媒…

linux内核:持续更新

内核源码树 COPYING文件是内核许可证&#xff0c;CREDITS是开发了很多内核代码的开发者列表&#xff0c;MAINTAINERS是维护者列表&#xff0c;它们负责维护内核子系统和驱动程序&#xff0c;makefile是基本内核的makefile 向内核插入驱动模块 命令&#xff1a;insmod xxx.ko …

网络库-POCO介绍

1.简介 POCO C Libraries 提供一套 C 的类库用以开发基于网络的可移植的应用程序&#xff0c;它提供了许多模块&#xff0c;包括网络编程、文件系统访问、线程和并发、数据库访问、XML处理、配置管理、日志记录等功能。Poco库的设计目标是易于使用、高度可定制和可扩展。 包含…

LeetCode2390从字符串中移除星号

题目描述 给你一个包含若干星号 * 的字符串 s 。在一步操作中&#xff0c;你可以&#xff1a;选中 s 中的一个星号。移除星号 左侧 最近的那个 非星号 字符&#xff0c;并移除该星号自身。返回移除 所有 星号之后的字符串。注意&#xff1a;生成的输入保证总是可以执行题面中描…