C语言高级应用 -------- (4)指针的复杂应用 typedef,函数指针,

一、指针数组和数组指针

前面的是形容词,后面的才是主题。
1、字面意思来理解指针数组与数组指针
(1)指针数组的实质是一个数组,这个数组中存储的内容全部是指针变量。
(2)数组指针的实质是一个指针,这个指针指向的是一个数组。

2、分析指针数组与数组指针的表达式

int *p[5];
int (*p)[5];
 int *(p[5]);

优先级:()和 [ ] 的优先级别相同,然后按照从左到右的方式来结合。

p[5],c语言编译器,发现p 先和 [5] 结合,所以 p 就是一个数组(4*5=20字节的大小)。
int * (p[6]), 说明在这个数组当中,都是 int * 类型的指针。

int (*p) : 可以看出 p 先和 * 结合,所以它本质是一个指针(4字节大小)

  • 第一个:int *p[5]; 核心是p,p是一个数组,数组有5个元素大,数组中的元素都是指针,指针指向的元素类型是int类型的;整个符号是一个指针数组。

  • 第二个,int (*p)[5];核心是p,p是一个指针,指针指向一个数组,数组有5个元素,数组中存的元素是int类型; 总结一下整个符号的意义就是数组指针。

  • 第三个,int *(p[5]); 解析方法和结论和第一个相同,()在这里是可有可无的。

3、数组指针的用法:

补充: 对于 int a[5]; int *p;
p = a; 正确,a做右值的时候,只代表首元素的首地址。
p = &a; 错误,类型不匹配,&a,代表的是整个数组的首地址。

&a是int (*)[5],是数组的指针;

int *p;
int a[5];

p = a;		// 般配的,类型匹配的,所以编译器不会警告不会报错。
//p = &a;		// 类型不匹配,p是int *, &a是int (*)[5],是数组的指针;

int (*p1)[5] ;
p1 = &a;		// p1类型是int (*)[5],&a的类型也是int (*)[5],都是数组指针

二、函数指针与 typedef

为什么函数指针和 typedef 相结合呢?
因为函数指针太长了, 我们需要利用 typedef 来创造一个新的数据类型

理解函数为什么是一个指针?

void fun1(const char *p)
{

}

void main(void)
{
	printf("%p\n",fun1);     // 0x804841d 
	printf("%p\n","uboot");  // 0x80484f4
}

可以看出我们 函数(程序)的地址,和我们的 字符串常量,基本在一个区域里面。

1、函数指针的书写和分析方法

补充:类型:就是将我们的变量符号去掉。

  • 定义一个数组指针:
int (*p1)[5]   // p1是变量名, 他的类型是 int(*)[5]。 
  • 定义一个函数指针:
    函数指针的类型,和函数的类型一样。
void fun1(void)
void (*p)(void)  定义一个函数指针,变量名为 p , 类型为void(*)(void)
赋值,
p = fun1         
p = &fun1        fun1 和 &fun1 做右值的时候表示地址。
p = (void(*)(void)) 0x11111111	 我们将一个地址 强制类型转化为void(*)(void)类型
调用。
p(); 直接进行调用。


int fun1(int a,int b)
int (*p1)(int a,int b) 定义一个函数指针,变量名为 p1 , 类型为int(*)(int a, int b)

赋值和上个类似。

调用:
p(a,b);

函数名和数组名的区别:
fun 和 &fun 做右值都是这个函数的首地址。(对应于函数指针)
a :代表数组中首元素的地址。(对应于普通指针)
&a:代表整个数组的首地址。 (对应于数组指针)

复杂案例:返回值也为指针

char *strcpy(char *dest, const char *src);
char *(*pFunc)(char *dest, const char *src);

2、typedef 关键字的用法

作用:
(1)用来定义一个新的类型。
(2)将类型进行重命名。

我们利用teyedef 来进行定义新的 函数指针类型
将我们函数指针的变量换成了一个类型:

举例:

函数指针
char* (*p)(char *, const char *);
解析: p 为指针变量,  其类型为char* (*)(char *, const char *);

定义新类型
typedef char* (*pType)(char *, const char *);
解析:pType 为新的类型, 代替的类型为 char* (*)(char *, const char *);

分析一个复杂案例:

typedef unsigned int bool;
// 第二种方法:用函数指针方式调用
// 利用 typedef 来定义一个新的类型
typedef bool(*pCopySDMMC2Mem)(int, unsigned int, unsigned short, unsigned int*, bool);

新类型:pCopySDMMC2Mem
代替的类型:bool(*)(int, unsigned int, unsigned short, unsigned int*, bool)

// 实际使用时
pCopySDMMC2Mem p1 = (pCopySDMMC2Mem)(*(unsigned int *)0xD0037F98);
p1(x, x, x, x, x);		// 第一种调用方法

在ROM 里面的一个固定地址,里面存放着 整个函数的首地址


pCopySDMMC2Mem p1 = (pCopySDMMC2Mem)(*(unsigned int *)0xD0037F98);
1、先将 0xD0037F98 进行解引用。----- 提取出它里面存放的对应函数的 首地址。
(*(unsigned int *)0xD0037F98)
2、在对这个地址进行强制转换。

三、函数指针的实战

1、通过函数指针来调用执行函数

通过一个函数指针来调用执行多个函数。 通过指向不同的函数。

	switch (c)
	{
		case '+':
			p1 = add;  break; // 不加break,则会一直执行下去
		case '-':
			p1 = sub;
		case '*':
			p1 = multiply;
		case '/':
			p1 = division;
		default :             // 即以上情况都没有的时候
			p1 = NULL; break;
	}

2、结构体内嵌函数指针实现分层(回头再来理解,linux驱动架构)

上层为下层提供服务。

四、再次讨论 typedef (结尾有 ;)

1、如何使用typedef

命名的记法:将定义变量的变量名,变为新类型的名字。

函数指针:
int (*p1)(void);             定义了一个变量,变量名为 p1。

typedef int (*ptype)(void);  定义了一个新的类型,类型名为 ptype。
ptype  p1;                   定义了一个变量,变量名为 p1。

普通变量:
int a;                       定义一个变量,变量名为 a。

typedef int size;           定义了一个新的类型, 新的类型名为 a。
size a;                     定义一个变量,变量名为 a。

数组变量:
int*p1)[5];              定义了一个数组指针变量, 变量名为 p1。

typedef  int*ptype)[5]; 定义了一个新的类型, 新的类型名为 ptype。
ptype p1;   				定义了一个变量,变量名为 p1。

typedef 和 define 的区别

typedef char*  tpChar;
#define dpChar  char *	
	
tpChar	P1,P2;         //p1和p2的类型都是char*
dpChar	P3,P4;          //char*p3,p4

2、typedef 与 struct

  • 没加 typedef 的情况:这个结构体类型为 :struct student
struct student
{
	char name[20];
	int age;
};
  • 这个类型有2个名字:第一个名字是 struct student ,第二个类型名叫 student_t
typedef struct student
{
	char name[20];
	int age;
}student_t;
  • 第一个类型名:struct student ,第二个类型名是 student
typedef struct student
{
	char name[20];
	int age;
}student;

我们一次定义了2个类型:
第一个是结构体类型,有2个名字:struct teacherteacher
第二个是结构体指针类型,有2个名字:struct teacher *pTeacher

typedef struct teacher
{
	char name[20];
	int age;
	int mager;
}teacher, *pTeacher;  逗号的意思就是我们一次定义了两个类型
 
struct teacher t1
teacher t1

struct teacher * p1
pTeacher p1 ;

3、typedef 和 const

回顾const 对指针的用法
const int *pint *const p 是不同的。前者是p指向的变量是const,后者是p本身const。

(1) typedef int *PINT; const PINT p2; 相当于是int *const p2;

typedef int *PINT;

int a = 23;
int b = 11;
 
	const PINT p2 = &a;		
	*p2 = 33;
	printf("*p2 = %d.\n", *p2);  //*p2 = 33,说明P2指向的变量可以被改变
	p2 = &b;				// 报错error: assignment of read-only variable ‘p2’
	                         //说明P2本身是const的

(2) typedef int *PINT; PINT const p2; 相当于是int *const p2;

typedef int *PINT;

int a = 23;
int b = 11;
 
	PINT const p2 = &a;		
	*p2 = 33;
	printf("*p2 = %d.\n", *p2);  //*p2 = 33,说明P2指向的变量可以被改变
	p2 = &b;				// 报错error: assignment of read-only variable ‘p2’
	                         //说明P2本身是const的

(3)如果确实想得到const int *p;这种效果,只能typedef const int *CPINT; CPINT p1;

typedef const int *CPINT;

	int a = 23;
	int b = 11;

	CPINT p = &a;
	*p = 33;				//报错 error: assignment of read-only location ‘*p’,P所指的变量的const的,不能被修改
	p = &b;

4、typedef的好处

1、简化类型的描述。(函数指针的类型很长)

2、解决另外一个问题。
比如在 32 位的机器上面,我们的 int 是4个字节。如果我们每个地方都用 int 的话,移植到8位机器上面, 这个 int 就变小了, int成了 2个字节,就会引发错误。
但是我们如果使用 typedef的话

typedef int size_t ;
typedef double size_t; 这样的话,我们的 size_t 就还是 4个字节。

五、二重指针

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想文艺一点的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值