1、指针数组与数组指针
int *p[5]; //p[5]数组,则*p[5]是指针数组
int (*p)[5];//*p是指针,则(*p)[5]是数组指针
int *(p[5]);//p[5]是数组,则*(p[5])是指针数组
2、函数指针
1、函数指针
有个函数是:void func(void); 其对应的函数指针:
void (*p)(void); 类型是:void (*)(void);
函数名和数组名最大的区别就是:函数名做右值时加不加&效果和意义都是一样的;但是数组名做右值时加不加&意义就不一样。
#include <stdio.h>
void func(void)
{
printf("this is func.\n");
}
int main(void)
{
void (*pFunc1)(void);
//pFunc1 = func; //左边是函数指针变量,右边函数名
pFunc1 = &func; //func和&func一样
pFunc1(); //用函数指针解引用该函数
return 0;
}
譬如函数是strcpy函数(char *strcpy(char *dest, const char *src);),
对应的函数指针是:char *(*pFunc)(char *dest, const char *src);
#include <string.h>
#include <stdio.h>
//char *strcpy(char *dest, const char *src);
int main(void)
{
char a[10] = {0};
char* (*pFunc1)(char*, const char *);
pFunc1 = strcpy;
pFunc1(a,"asdfgh");
printf("a = %s.\n", a);
return 0;
}
结果:a = asdfgh.
3、typedef
typedef是给类型重命名,也就是说typedef加工出来的都是类型,而不是变量。
(1)类型是一个数据模板,变量是一个实在的数据。类型是不占内存的,而变量是占内存的。
(2)面向对象的语言中:类型就是类class,变量就是对象。
typedef char* (*pType)(char*, const char *);//函数指针重命名
pType pFunc1; //定义函数指针
1.typedef与#define宏的区别
typedef char *pChar;
#define pChar char *
2.typedef与结构体
(1)结构体在使用时都是先定义结构体类型,再用结构体类型去定义变量。
(2)C语言语法规定,结构体类型使用时必须是struct 结构体类型名 结构体变量名;这样的方式来定义变量。
// 第一个类型名:struct student,第二个类型名是student
typedef struct student
{
char name[20];
int age;
}student;
(3)使用typedef一次定义2个类型,分别是结构体变量类型,和结构体变量指针类型。
// 第一个是结构体类型,有2个名字:struct teacher,teacher
// 第二个是结构体指针类型,有2个名字:struct teacher *, pTeacher
typedef struct teacher
{
char name[20];
int age;
int mager;
}teacher, *pTeacher;
3.typedef与const
(1)typedef int *PINT;
const PINT p2; //相当于是int *const p2;
(2)typedef int *PINT;
PINT const p2; //相当于是int *const p2;
(3)如果确实想得到const int *p;这种效果,只能
typedef const int *CPINT;
CPINT p1;
4.使用typedef的重要意义(2个:简化类型、创造平台无关类型)
(1)简化类型的描述。
char *(*)(char *, char *); typedef char *(*pFunc)(char *, char *);
(2)很多编程体系下,人们倾向于不使用int、double等C语言内建类型,因为这些类型本身和平台是相关的(譬如int在16位机器上是16位的,在32位机器上就是32位的)。为了解决这个问题,很多程序使用自定义的中间类型来做缓冲。譬如linux内核中大量使用了这种技术.
内核中先定义:typedef int size_t; 然后在特定的编码需要下用size_t来替代int(譬如可能还有typedef int len_t)
(3)STM32的库中全部使用了自定义类型,譬如typedef volatile unsigned int vu32;
4、二重指针
二重指针的用法
(1)二重指针指向一重指针的地址
(2)二重指针指向指针数组的
int *p1[5];
int **p2;
p2 = p1; //
(3)实践编程中二重指针用的比较少,大部分时候就是和指针数组纠结起来用的。
(4)实践编程中有时在函数传参时为了通过函数内部改变外部的一个指针变量,会传这个指针变量的地址(也就是二重指针)进去
void func(int **p)
{
*p = (int *)0x12345678;
}
int main(void)
{
int a = 4;
int *p = &a; // p指向a
printf("p = %p.\n", p); // p打印出来就是a的内存地址
func(&p); // 在func内部将p指向了别的地方
printf("p = %p.\n", p); // p已经不指向a了,所以打印出来不是a的地址
//*p = 23; // 因为此时p指向0x12345678,但是这个地址是不
// 允许访问的,因此会段错误。
}
二重指针与数组指针
(1)二重指针、数组指针、结构体指针、一重指针、普通变量的本质都是相同的,都是变量。
(2)所有的指针变量本质都是相同的,都是4个字节,都是用来指向别的东西的,不同类型的指针变量只是可以指向的(编译器允许你指向的)变量类型不同。
(3)二重指针就是:指针数组指针
5、二重数组
二维数组的下标式访问和指针式访问
(1)回顾:一维数组的两种访问方式。以int b[10]为例, int *p = b;。
b[0] 等同于 *(p+0); b[9] 等同于 *(p+9); b[i] 等同于 *(p+i)
(2)二维数组的两种访问方式:以int a[2][5]为例,(合适类型的)p = a;
a[0][0]等同于*(*(p+0)+0); a[i][j]等同于 *(*(p+i)+j)
指针指向二维数组的数组名
(1)二维数组的数组名表示二维数组的第一维数组中首元素(也就是第二维的数组)的首地址
(2)二维数组的数组名a等同于&a[0],这个和一维数组的符号含义是相符的。
(3)用数组指针来指向二维数组的数组名是类型匹配的。
int a[3][5] = {{1,2,3,4,5}, {6,7,8,9,10} ,{11,12,13,14,15}};
int (*p)[5];
p = a;
printf("a[2][2] = %d.\n", *(*(a+2)+2));//13
printf("a[2][2] = %d.\n", *(*(p+2)+2));//13
指针指向二维数组的第一维
(1)用int *p来指向二维数组的第一维a[i]
int *p1;
p1 = a[1];
//p1 = &a[0][0];
printf("a[1][4] = %d.\n",*(p1+4));//10