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

指针(3)

1.字符指针变量

有一种指针类型是字符指针:char *

一般的字符指针书写:

//字符指针一般的写法:
int main()
{char ch = 'q';char * c=&ch;*c = 'e';printf("%c\n", ch);return 0;
}

还有一种写法:

int main()
{char* ch = "q";char *pc=&ch;//pc 就是字符指针const char* p = "abcdef";  //例如在X86的环境下,指针变量大小是4字节,是不可能存放的下//"abcdef\0"7个字符的,所以不是将字符串存入p中的,而是将第一个字符的地址存放在p中//顺藤摸瓜就可以找到整个字符串了,其实在上面的描述中,指针变量里面存放的是字符串中首//元素的地址,在这一点上就像是数组printf("%c\n", *p);//输出的结果为 a//1.可以将这个字符串想象成一个字符串数组,但是这个数组是不能修改的,因为是常量字符串//2.当常量字符串出现在表达式中的时候,他的值是第一个字符的地址printf("abcdef[3]=%c\n", "abcdef"[3]);printf("p[3]     =%c\n", p[3]);p[3] = 'w'; //出现错误 因为是常量字符串是不可以修改的return 0;
}

上面的代码中的 const char* p = "abcdef";  是将字符串的首字符放在了指针变量 p 中,并不是将整个字符串放在 变量 p 中。

下面还有一个有趣的代码:

int main()
{char ch1[] = "hello world";char ch2[] = "hello world";char * ch3 = "hello world";char * ch4 = "hello world";if (ch1 == ch2){printf("ch1 and ch2 are same\n");}elseprintf("ch1 and ch2 are not same\n");if (ch3  == ch4){printf("ch3 and ch4 are same\n");}elseprintf("ch3 and ch4 are not same\n");return 0;
}

为什么是这样的输出的结果?

ch1 和 ch2 是两个不同的数组,向内存中申请了两个不同的空间,数组名表示的是首元素的地址,所以 ch1 与 ch2 不相等

在内存中内容相同的常量字符串只会保存一份,ch3 和 ch4 是两个指针变量,但是里面的内容是一样的,所以指向的是同一块地址,所以指向的都是 a 的地址,所以 ch3 和 ch4 是相等的

2.数组指针变量

2.1 数组指针变量是什么?

前面提到的指针数组是一种数组,数组中存放的是指针(地址)。

数组指针变量类比于其他的类型的指针变量:

整型指针变量:int * pa; 存放的是整型变量的地址,进行解引用操作能够指向整型的数据。

浮点型指针变量:char * pc; 存放的是浮点型的变量的地址,进行解引用操作能够指向浮点型的数据。

那么数组指针变量:存放的就是数组的地址,进行解引用操作能够指向数组的数据。

思考一下,下面的两个代码中 p1 和 p2 分别代表什么?

int *p1[10]; // * p1 不加括号,p1 优先和 [] 先结合 int (*p2)[10];

p1 [10] 优先结合,代表一个数组,所以 p1 就是数组,里面有 10 个元素,每个元素是 int * ,p1是 指针数组

 p2 是指针,指针指向的是数组,数组里面有10个元素,每个元素的类型是 int,所以 p2 是指向数组的指针,即数组指针 

 数组指针变量:

int (*pa)[10];

注意:

[ ] 的优先级要高于 * 号的优先级,所以必须加上 () 来保证 p 和 * 先结合。

2.2 数组指针变量怎样初始化?

数组指针变量是存放数组的地址,取出数组的地址:&数组名。

int main()
{int arr[5]={0};&arr;//得到的就是数组的地址reurn 0;
}

将取出的数组的地址存放在数组指针变量中:

int (*parr)[5]=&arr;

对数组指针类型进一步剖析:

int ( *p )[5] 的剖析:((*p))p是数组指针的变量名,代表是一个指针变量,指向的是一个数组,( [ 5 ] )里面有5个元素,( int )每个元素类型是整型

3.二维数组的传参本质

基于数组指针的理解,进一步的来了解一下二维数组传参的本质:

过去我们对二维数组传参时,写法如下:

//二维数组传参,形参写的是二维数组
//写法1:
void Print(int arr[3][5], int r, int c)
{int i = 0;for (i = 0; i < r; i++){int j = 0;for (j = 0; j < c; j++){printf("%d ", arr[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} };Print(arr, 3, 5);   //打印arr数组的内容return 0;
}

另外一种二维数组传参的写法:

//写法2:
void Print(int (*p)[5], int r, int c)//传过来的是首元素的地址,而二维数组的首元素的地址是一行一维数组//所以要用数组指针变量来接收传过来的一行一维数组地址
{int i = 0;for (i = 0; i < r; i++){int j = 0;for (j = 0; j < c; j++){printf("%d ",*(*(p+i)+j));//*(p+i)=arr[i]//*(*(p+i)+j)=arr[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} };Print(arr, 3, 5);   //打印arr数组的内容return 0;
}

结论:

二维数组传参,形参的部分可以写成数组(便于理解,数组本质上是指针),也可以写成指针的形式,因为传参传的是地址,需要用指针来接收。 

4.函数指针变量

4.1 函数指针变量的创建

什么是函数指针,类比于数组指针,我们就可以很快的理解到,就是存放函数地址的指针变量,之后通过地址也可以来调用函数的。

函数有地址吗?

测试代码如下:

void test()
{printf("hello!\n");
}int main()
{printf("test: %p\n",  test);printf("&test: %p\n",&test);return 0;
}

上述的两个输出结果是有一样的,
函数名:函数的地址
&函数名:函数的地址 

4.2 函数指针类型的书写和函数指针变量的使用

int Add(int x, int y)
{return x + y;
}int main()
{int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };int (*pa)[10] = &arr;//数组指针//可以类比于数组指针,写出函数指针//printf("&Add=%p\n", &Add);//printf("Add =%p\n", Add);上述的两个输出结果是有一样的,函数名:函数的地址&函数名:函数的地址//想把函数的地址存放起来:int (*pc)(int, int) = &Add;//pc 就是函数指针变量,存放的是函数的地址,* pc 后面的(int,int)代表的是两个参数各自的类型int ret1 = Add(3, 5);//之前调用函数的方法printf("%d\n", ret1);int ret2 = (*pc)(8, 9); //(*pc)--对pc变量解引用找到那个函数,(8, 9)进行传参printf("%d\n", ret2);因为上述的两个输出结果是有一样的,函数名:函数的地址&函数名:函数的地址//所以:int(* pc2)(int, int) = Add;int ret3 = (*pc2)(1, 6);printf("%d\n", ret3);//函数调用时,Add( )前面是函数名,函数名存放的是地址,当我们不解引用,直接用地址去调用也是可以的int ret4 = pc2(1, 6);printf("%d\n", ret4);return 0;
}

函数指针类型的解析:

int (*pf) ( int x , int y) 

int 是 pf指向函数的返回类型,pf 函数指针变量名,( int x , int y) 是 pf 指向函数的参数类型和个数,其中的 x 和 y 可以省略掉

4.3 两个有趣的代码

代码1:

(*(void (*)())0)();
void (*)() 是函数指针类型,参数为空,返回类型是 void(void (*)())0 <===> (类型)0 --->强制类型转换,转换为存放地址的指针变量*(void (*)())0 进行解引用操作,结果应该是一个函数(*(void (*)())0)();--->函数的调用
void (* signal(int, void(*)(int) ) )(int);
signal是一个函数
signal函数的参数有2个,第一个是int类型
第二个是函数指针类型,该指针指向的函数参数是int,返回类型是void
signal函数的返回类型是这种类型的void(*)(int)函数指针
该指针指向的函数参数是int,返回类型是void

4.3 typedef 关键字

typedef 是用来类型的重命名的,将复杂的类型简单化

typedef unsigned int uint; //将 unsigned int 重命名为 unit//数组指针类型的重定义
//typedef int (*)[10] pArr_t; //error
typedef int (*pArr_t)[10] ;//函数指针类型的重定义
//typedef int (*)(int,int) pf_t; //error
typedef int (*pf_t)(int,int);int main()
{unsigned int n1;uint n2;pArr_t pa;int (*pb)[10];pf_t pf;int (*pl)(int, int);return 0;
}

5.函数指针数组

6.转移表

相关文章:

  • 【上位机——MFC】MFC入门
  • day 22 作业
  • 实战指南:封装Faster-Whisper为FastAPI接口并实现高并发处理-附整合包
  • SAP PO开发-端到端配置
  • 2.1 基于委托的异步编程方法
  • SSRF学习
  • Spring 01
  • 9、Hooks:现代魔法咒语集——React 19 核心Hooks
  • 数字系统与编码
  • 计算机组成原理笔记(十六)——4.1基本算术运算的实现
  • Java Streams 使用教程
  • 即梦AI与可灵AI视频生成功能对比分分析
  • AI与思维模型【70】——遗忘曲线
  • 从外网访问局域网服务器的方法+Linux文件和命令
  • App-Controller - 通过自然语言操控应用程序的智能框架
  • tigase源码学习杂记-组件化设计
  • 人工智能之矢量搜索报告
  • 如何轻松实现用户充值系统的API自动化测试
  • 【实战中提升自己】内网安全部署之端口隔离与MAC地址认证
  • Dify部署内网时遇到的代理问题及解决办法
  • 同济研究生开发AI二维码拿下大奖,新一代00开发者掀起AI创业潮
  • 讲座|在数字化时代,“记住”到底意味着什么
  • 人民日报头版:各地扎实开展学习教育,一体推进学查改
  • 李家超:香港特区政府积极推进十五运会各项筹办工作
  • 42岁北京大学科学技术与医学史系副教授陈昊逝世
  • 跨境电商敦煌网在美下载量飙升,如何接住美国用户的“流量”?