前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >指针(3)---不同指针变量

指针(3)---不同指针变量

作者头像
Skrrapper
发布2024-06-18 15:24:23
260
发布2024-06-18 15:24:23
举报
文章被收录于专栏:技术分享技术分享

1.字符指针变量及其他简单类型指针变量

字符的类型是char;

字符指针的类型是char*

表达方式如下:

代码语言:javascript
复制
int main()
{
 char ch = 'w';
 char *pc = &ch;
 *pc = 'w';
 return 0;
}

这是最简单也是最普遍的表达方式。

但是还有另外一种:

代码语言:javascript
复制
char* pstr = "hello bit.";

在这行代码中,貌似将hello bit.这个字符串放进了字符指针变量pstr中,但事实真的如此吗?

实际上,它的本质是把这个字符串的首字符h的地址放进了pstr中 

我们会发现这个概念貌似似曾相识,没错,它跟数组指针的存放方式相差无几。

可以把字符串想象成一个字符数组,因为数组就是地址,取字符串的地址也就可以相当于取数组

当常量字符串出现在表达式中时候,它的值也就是第一个字符的地址。

但是需要注意的是:内容相同的常量字符串只会保存一份地址:

代码语言:javascript
复制
const char *str3 = "hello bit.";
const char *str4 = "hello bit.";

这两个不同的指针变量实际上指向的是同一个常量字符串hello bit.

所以这里的str3==str4。

事实上,

各种简单类型例如整型int,字符型char等的指针变量事实上意义和用法都大同小异:

整型指针变量:用来存放整型变量的地址,

代码语言:javascript
复制
int a = 100;
int *p_a = &a;

字符指针变量:用来存放字符型变量的地址, 

代码语言:javascript
复制
char b="hello world.";
cahr *p_b = &b;

浮点指针变量:用来存放浮点型变量的地址,

代码语言:javascript
复制
float c = 3,14;
float *p_c = &c;

2.数组指针变量

指针数组是数组元素是指针的数组;

那么数组指针是什么呢?

代码语言:javascript
复制
int *p1[10];
int (*p2)[10];

上方的p1和p2分别代表什么呢?

对它们进行分析:

我们注意到p1的类型实际上是int*,而后方的[10]告诉我们数组的本质,所以可以得出p1是指针数组;

我们注意到p2的类型实际上是int,而括号将*p2同时扩了起来,这说明p2实际上是一个指针变量,接着后面的[10]告诉我们它指向的是一个大小为10个整数的数组,所以p2是一个指针,指向一个数组,这个指针也就称为数组指针。

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

那么,

数组指针变量也就应该是:存放的应该是数组的地址,能够指向数组的指针变量。

数组指针变量的初始化

数值指针变量用来存放地址,如果我们需要获取数组的地址也就需要用到:

&数组名

代码语言:javascript
复制
int arr[10] = {0};
&arr;//得到的就是数组的地址

我们存放数组的地址也就存放在指针数组变量中:

代码语言:javascript
复制
int(*p)[10] = &arr;

二维数组的传参本质

在未学习数组指针变量之前,二维数组传参我们使用的是数组;

但是在学习了数组指针变量之后,我们应该了解其实写成指针也是可以的。

我们知道:二维数组的元素都是一维数组,那么它的首元素就是第一行数组的地址 所以二维数组的数组名的表示就是第一行一维数组的地址。

那么第一行地址的类型也就是数组指针类型。

也就意味着二维数组传参本质上也就是传递了地址,传递也就是这个一维数组的地址。

代码语言:javascript
复制
//void Print(int (*arr)[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;
//}

所以,二维数组传参,形参部分既可以理解为数组也可以理解为是指针。

指针数组与数组指针

在此再次强调这二者的区别,

指针数组是元素是指针的一类数组,它的本质是数组。

数组指针是指向某个数组的指针,它的本质是指针。

3.函数指针变量

数组指针变量是用来存放数组的地址;

函数指针变量是用来存放函数的地址。

那么函数的地址是什么?

可以看到add与&add的地址是同一个,这也就说明函数的地址就是函数名的地址。

所以如果要存放函数的地址,也就可以创建函数指针变量。

函数指针变量表达式

代码语言:javascript
复制
int (*p)(int x, int  y);//(*p)的括号不能省略,如果省略了那么类型就会由int变为int*

需要注意的是,这里的两个参数名通常是可以去掉的,因为它们实际上作用不大,我们使用函数指针变量的主要目的是将这个函数存放起来。

代码语言:javascript
复制
int (*p)(int,int);

函数指针类型

而对应的函数指针类型:

代码语言:javascript
复制
int (*) (int a, int b); 

函数指针变量各个部分的意义

我们看到,int是pf3指向函数的返回类型,也就是说这里是指原函数的类型;

代码语言:javascript
复制
char* test(int a, char c)
{
	//...
	return NULL;
}


int main()
{
	char* (*pt)(int, char) = test;

	return 0;
}

在上述代码中,通常在主函数中调用这个函数时我们会忽略这个函数的类型其实是char*,所以我们在书写函数指针变量的时候前面也应该是char*而不是char。

注意:我们在书写函数指针变量的时候,其实是无需用*来解引用的。

因为函数的地址就是函数名的地址,无需再使用解引用操作来调出函数的地址,它本身就已经得知了。

代码语言:javascript
复制
int (*pf)(int,char)=&Add;//这样可行
int (pf)(int,char)=&Add;//这样也可行

函数指针的应用

在了解函数指针变量的基本知识之后,我们就可以试着使用它。

我们知道,指针的重要作用之一就是通过&与*来访问该指针的源头从而返回值。

那么我们也可以使用函数指针来实现函数的调用。

代码语言:javascript
复制
int Add(int x, int y)
{
 return x+y;
}
int main()
{
 int(*pf3)(int, int) = Add;
 
 printf("%d\n", (*pf3)(2, 3));
 printf("%d\n", pf3(3, 5));
 return 0;
}//结果也就是5和8(2+3;3+5)

函数指针的隐藏

函数指针的表达式很冗长,有时候它会隐藏于某行代码之中很难辨识。反过来看如果我们不认识函数指针,可能碰到一行复杂难懂的代码我们更会无从下手。

举两个例子:

A ep1
代码语言:javascript
复制
(*(void (*)())0)();

这这行代码的括号刨析一下:

我们知道,像(int)3.14这种形式的书写代表着:强制类型转换。也就是说把原本属于浮点型的3.14强制转换成整型。

而上述代码中的(void(*)()),这是一个函数指针类型的书写格式。

所以也就是说上述类型将原本是整型的0强制转换成函数指针类型。也就是转换成了一个函数的地址。这个函数没有参数,返回类型是void。

而在整个括号的初始还有一个*号,它代表着对函数指针的解引用。

所以,这实际上是一次函数调用。

调用0地址处的函数。

B ep 1
代码语言:javascript
复制
void (*signal(int , void(*)(int)))(int);

同样对每个括号包括的内容进行刨析我们可以得知:

上述代码是函数声明;

signal是一个函数;

函数的参数类型一个是int整型,另一个是函数指针类型,返回类型是void;

而在signa之外又是一个函数指针变量的格式,所以也就是最外边是一个函数指针变量,这个函数的返回类型是void,函数参数类型是int。

总结:这些看似很复杂的代码,如果我们不了解函数指针的知识的话,是很难理解的。

那么既然函数指针变量这一类的代码如此冗杂,我们该如何化繁为简呢?

函数指针数组

将函数的地址存放在一个数组中,那么这个元素是函数指针的数组也就被称为函数指针数组

函数指针数组的定义

代码语言:javascript
复制
int (*parr1[10])();

parr1与[]在一起说明它是个数组;

在外围我们看出数组的元素是int (*)() 类型的函数指针。

应用

代码语言:javascript
复制
int A(int x, int y)
{
	return x + y;
}
 
int B(int x, int y)
{
	return x - y;
}
 
int C(int x, int y)
{
	return x * y;
}
 
int D(int x, int y)
{
	return x/y;
}
 
int main()
{
	int(*p[4])(int, int) = {A,B,C,D};
	int i = 0;
	for (i = 0; i < 4; i++)
	{
	printf("%d\n",p[i](8, 4));
	}
    return 0;
}

4.typedf关键字在指针中的应用

有一种关键字可以重命名类型,可以将复杂的类型简单化。

它就是typedf关键字

简单类型重命名

代码语言:javascript
复制
typedef unsigned int uint;
//将unsigned int 重命名为uint

一级指针类型重命名 

代码语言:javascript
复制
typedef int* ptr_t;//指针类型重命名

数组和函数指针类型重命名

代码语言:javascript
复制
typedef int(*parr_t)[5]; //新的类型名必须在*的右边
typedef void(*pfun_t)(int);//新的类型名必须在*的右边

在使用的过程中,我们就可以使用重命名后的名字来代替原本冗长的名字,同时需要注意在不同的类型中使用时用法会有所不同。

代码语言:javascript
复制
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-04-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.字符指针变量及其他简单类型指针变量
  • 2.数组指针变量
    • 数组指针变量的初始化
      • 二维数组的传参本质
        • 指针数组与数组指针
        • 3.函数指针变量
          • 函数指针变量表达式
            • 函数指针类型
              • 函数指针变量各个部分的意义
                • 函数指针的应用
                  • 函数指针的隐藏
                    • A ep1
                    • B ep 1
                  • 函数指针数组
                  • 4.typedf关键字在指针中的应用
                    • 简单类型重命名
                      • 一级指针类型重命名 
                        • 数组和函数指针类型重命名
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档