当前位置: 首页 > news >正文

C语言 之 理解指针(4)

文章目录

  • 1. 字符指针变量
  • 2. 数组指针变量
    • 2.1 对数组指针变量的理解
    • 2.2 数组指针变量的初始化
  • 3. 二维数组传参的本质
  • 4. 函数指针变量
    • 4.1 函数指针变量的创建
    • 4.2 函数指针变量的使用
  • 5. 函数指针数组

1. 字符指针变量

我们在前面使用的主要是整形指针变量,现在要学习的是字符指针变量。
一般使用:

int main()
{char ch = 'a';  //字符变量char *pc = &ch;  //字符指针变量*pc = 'b';return 0;
}

除了上面这种用法外,还有用于字符串。
如:

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

输出结果为:
在这里插入图片描述
代码 const char* pstr = "hello world"; 要注意:并不是把字符串hello world放入了字符指针变量pstr中,其本质是把字符串hello world的首个字符的地址放到了pstr中。
在这里插入图片描述
上面的代码如图所示,即把常量字符串的首字符h的地址存到了pstr

相关例题:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{char str1[] = "hello world";char str2[] = "hello world";const char* str3 = "hello world";const char* str4 = "hello world";if (str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

输出结果为:
在这里插入图片描述
首先就像之前所说的,数组名是首元素的地址,str1和str2两个数组的内容虽然相同,但是使用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块,所以他们的地址是不同的。而对于str3和str4来说,它们是字符指针,它们指向的是同⼀个常量字符串。C语言中会把常量字符串存储到单独的⼀个内存区域,当⼏个指针指向同⼀个字符串的时候,实际上指向的是同⼀块内存。

2. 数组指针变量

2.1 对数组指针变量的理解

之前我们学习了指针数组,所以我们知道指针数组是用来存放指针的数组
那么数组指针变量是指针变量?还是数组?
答:指针变量

结合我们之前所学:
整形指针变量:int * p; 存放的是整形变量的地址,能够指向整形数据的指针。
字符指针变量:char * p; 存放的是字符变量的地址,能够指向字符数据的指针。
由此可得:数组指针变量应该是存放数组的地址,能够指向数组的指针变量。

数组指针变量:

int (*p)[10];

解释: p和*结合,说明了p是一个指针类型的变量,然后指针指向的是一个大小为10个整形的数组。所以
p是⼀个指针,指向⼀个数组,叫作数组指针

要注意的是:[]的优先级要⾼于*号,所以必须加上()来保证p先和 * 结合。
不能写成int *p[10];

2.2 数组指针变量的初始化

首先我们需要知道,数组指针变量是用来存放数组的地址的。
要想获得数组的地址,就得使用我们之前所学的操作符&
我们也知道,除了两个特殊情况,数组名就是首元素的地址,而这两个特殊情况中,&arr就是取整个数组的地址。

这里的&arr就是取了整个数组的地址,我们之前所学的指针存的都是一个地址,所以现在要想存一整个数组的地址,我们就得用到数组指针了。

int arr[10] = {0};
&arr;//得到的就是数组的地址

使用数组指针变量存放整个数组的地址:

 int(*p)[10] = &arr;

在这里插入图片描述

3. 二维数组传参的本质

在之前我们有⼀个⼆维数组的需要传参给⼀个函数的时候,我们可能是这样写的:

#include <stdio.h>
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");}
}
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;
}

这里的实参是二维数组,形参部分也写成了二维数组的形式,结合刚才所学,是否还有其它写法?

首先让我们再来回忆一下我们对数组的了解,假如有一个整形数组,那么它的每一个元素就是一个整形变量,那么二维数组呢?其实二维数组就可以看作是每个元素都是一维数组的数组,即二维数组的每个元素都是一维数组。那么一维数组的首元素就是第一个变量,二维数组的首元素就是二维数组的第一行,即第一个一维数组。
在这里插入图片描述

所以,根据数组名是数组首元素的地址这个规则,⼆维数组的数组名表示的就是第⼀行的地址,是⼀维数组的地址。根据上面的例子,第⼀行的⼀维数组的类型就是 int [5] ,所以第⼀行的地址的类
型就是数组指针类型 int(*)[5](注:对于数组,去掉数组名就是类型) 我们也知道,数组传参的本质是传首元素的地址,那就意味着⼆维数组传参本质上也是传递地址,传递的是第⼀行这个⼀维数组的地址,由于这个二维数组的首元素地址是一整个数组,那么形参也是可以写成指针形式的,即数组指针变量的形式。代码如下:

#include <stdio.h>
void test(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("\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;
}

总结:二维数组传参,形参的部分可以写成数组,也可以写成指针形式。

4. 函数指针变量

4.1 函数指针变量的创建

经过我们前面的学习,通过类比
整形指针变量:int * p; 存放的是整形变量的地址,能够指向整形数据的指针。
字符指针变量:char * p; 存放的是字符变量的地址,能够指向字符数据的指针。
以及刚才的数组指针变量,所以我们就可以知道
函数指针变量是能够存放函数的地址,能够指向函数数据的指针啦。

所以,函数也是有地址的喔,我们可以来看看:

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

输出结果为:
在这里插入图片描述
上面的例子中打印出来了地址,所以函数是有地址的,并且函数名就是函数的地址,当然也可以通过 &函数名的方式获得函数的地址

如果我们要将函数的地址存放起来,就得创建函数指针变量
函数指针变量的写法和数组指针变量的写法类似
如下:
例1:无参型

void test()
{printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)() = test;

例2:带参型

int Add(int x, int y)
{return x + y;
}
int (*pf3)(int, int) = Add;
int (*pf3)(int x, int y) = &Add;  //x和y写上或者省略都是可以的

解析:
在这里插入图片描述

4.2 函数指针变量的使用

通过函数指针调⽤指针指向的函数

#include <stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{int(*pf3)(int, int) = Add;printf("%d\n", (*pf3)(1, 2));printf("%d\n", pf3(10, 20));return 0;
}

输出结果:
在这里插入图片描述

5. 函数指针数组

我们前面已经学习了指针数组了,即所存放的每个元素是指针的数组。
如:

int *arr[10];
//数组的每个元素是int*

那么如果我们想要把每个函数的地址存放到数组中,这个数组就叫作函数指针数组
函数指针数组的定义:

int (*parr[3])();

parr 先和 [ ] 结合,说明 parr是数组,数组的内容是什么呢?是 int (*)() 类型的函数指针。

函数指针数组的使用 可以点击此处查看

http://www.lryc.cn/news/410486.html

相关文章:

  • Java设计模式—单例模式(Singleton Pattern)
  • AV1帧间预测(二):运动补偿
  • 数学建模(5)——逻辑回归
  • 【C++高阶】:深入探索C++11
  • 6. 自定义Docker镜像
  • 「12月·长沙」人工智能与网络安全国际学术会议(ISAICS 2024)
  • 【技术支持案例】使用S32K144+NSD8381驱动电子膨胀阀
  • 第二期:集成电路(IC)——智能世界的微观建筑大师
  • 基于物联网的区块链算力网络,IGP/BGP协议
  • 每日一题~960 div2 A+B+C(简单奇偶博弈,构造,观察性质算贡献)
  • 音视频入门基础:H.264专题(17)——FFmpeg源码获取H.264裸流文件信息(视频压缩编码格式、色彩格式、视频分辨率、帧率)的总流程
  • Aboboo一些操作
  • 获取行号LineNumberReader
  • python数据结构与算法
  • 大数据学习之Flink基础(补充)
  • C++基础语法:友元
  • 【大模型系列】Video-LaVIT(2024.06)
  • 【总结】nacos作为注册中心-应用启动失败:NacosDiscoveryProperties{serverAddr=‘127.0.0.1:8848‘……
  • C语言——数组和排序
  • QEMU 新增QMPHMP指令【原文阅读】
  • 【Linux】全志Tina配置屏幕时钟的方法
  • 探索WebKit的CSS表格布局:打造灵活的网页数据展示
  • 信号的运算
  • Vue3知识点汇总
  • C++设计模式--单例模式
  • 数据驱动未来:构建下一代湖仓一体电商数据分析平台,引领实时商业智能革命
  • 学习JavaScript第五天
  • pythonGame-实现简单的坦克大战
  • 不太常见的asmnet诊断
  • 双指针-【3,4,5,6,7,8】