目录
一、指针数组和数组指针
前面的是形容词,后面的才是主题。
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 teacher
,teacher
第二个是结构体指针类型,有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 *p
和 int *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个字节。