1.字符指针变量字符指针是char*类型下边是基本用法#define _CRT_SECURE_NO_WARNINGS 1 #include stdio.h int main() { char ch d; char* pc ch; return 0; }这就是最基础的用法一个字符类型存一个字符取地址后地址保存在pc字符指针变量中还有其他用法。#define _CRT_SECURE_NO_WARNINGS 1 #include stdio.h int main() { char* pstr hello world; return 0; }这里是把字符串的地址放在pstr中了吗其实并不是他只是把“hello world”的首字符地址存在pstr中了可以发现上面结论是正确的因为首字符地址和整个字符串的地址是一样的。#define _CRT_SECURE_NO_WARNINGS 1 #include stdio.h int main() { char pstr1[20] { hello world }; char pstr2[20] { hello world }; char* pstr3 hello world; char* pstr4 hello world; if (pstr1 pstr2) { printf(pstr1pstr2\n); } else { printf(pstr1!pstr2\n); } if (pstr3 pstr4) { printf(pstr3pstr4\n); } else { printf(pstr3!pstr4\n); } return 0; }看一下这个代码他的输出结果是什么为什么有这个结果我一步步解释首先我们创建了两个字符数组用来存储字符串在内存中创建了两块空间分别存放这两个数组即使他们的内容是一样的其实这个也很好理解吧主要是第二个之所以说pstr3pstr4是因为用字符指针指定的字符串并非数组他是不能改变内容的既然我们不能对它做修改那内存为了省空间自然也没必要专门开辟两块空间存储两个完全一样的内容。2.数组指针数组指针是指针本质和整形指针、字符指针是一样的都是存储对应类型的指针顾名思义数组指针存储的整个数组的地址下边是一个基本的数组指针变量#define _CRT_SECURE_NO_WARNINGS 1 #include stdio.h int main() { int arr[10] { 0 }; int (*parr)[10] arr; return 0; }如代码所见创建一个数组将数组的地址传给数组指针变量parrparr的类型是int(*)[10]从上边的例子也可以看到数组指针变量初始化的时候需要用对应的数组地址赋值所谓对应用上述例子来说明其实前边的int很好理解但需要注意的是后边的10这个大小是一定要相等的以往我们初始化数组的时候为了避免越界可以给大空间不完全初始化但这里是需要严格对应的。那接下来我就要讲一下二维数组了请大家回忆一下我们说过二维数组也是数组数组名也是首元素地址二维数组的首元素是一个一维数组所以二维数组数组名的类型其实是一个数组指针。那么整个数组的地址和数组首元素地址区别大吗首先需要明确他们都是从数组的首元素开始的只不过在1或者时数组首元素指针跳过数组类型大小的空间而数组地址直接跳过整个数组指向数组后边的那一块空间加其他数同理。3.函数指针变量内存可以存储我们常见的变量比如说指针变量、整型变量等等但万物都并非凭空出现函数既然能运行那他必有一个实体存在换句话说同样有一个类型的指针存储函数的地址以便调用对应函数而存储这个函数地址的变量就叫做函数指针变量。#define _CRT_SECURE_NO_WARNINGS 1 #include stdio.h void test() { printf(hello world\n); } int main() { void (*pf1)() test; void (*pf2)() test; printf(pf1%p\n, pf1); printf(pf2%p\n, pf2); return 0; }下边这段代码是为了演示两种函数指针类型的初始化方式也就是说函数名和函数名其实是等价的。这里的函数指针类型就是void (*)()同理假如一个返回值是int参数是两个int的函数他对应的函数指针类型是int (*)(int,int)或者int (*)(int x,int y),前提是函数定义里两个形参名字是x和y。那么问题来了函数指针类型有啥用#define _CRT_SECURE_NO_WARNINGS 1 #include stdio.h int add(int a, int b) { return a b; } int main() { int (*pf)(int, int) add; int a 3; int b 5; printf(ab%d\n, pf(a, b)); printf(ab%d\n, (*pf)(a, b)); return 0; }如你所见可以用来调用函数那可能你就要接着问了为什么不直接函数名传参调用呢这不是多此一举吗那么我给大家看一段代码#define _CRT_SECURE_NO_WARNINGS 1 #include stdio.h int add(int a, int b) { return a b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } int div(int a, int b) { return a / b; } void menu() { printf(输入1加法\n); printf(输入2减法\n); printf(输入3乘法\n); printf(输入4除法\n); printf(输入0退出\n); } void comp(int(*pf)(int, int),int a,int b) { printf(%d\n, pf(a, b)); } int main() { int input 0; do { int a 0; int b 0; printf(输入操作的两个数); scanf(%d %d, a, b); menu(); scanf(%d, input); switch (input) { case 1: comp(add, a, b); break; case 2: comp(sub, a, b); break; case 3: comp(mul, a, b); break; case 4: comp(div, a, b); break; default: break; } } while (input); return 0; }你仔细看看我这个代码思考一下如果用传统方式传参调用函数那么代码会多么冗余这就是函数指针变量的意义所在。相信你也看到了一些细节比如我用了函数指针数组存储加减乘除四个函数的地址那么函数指针数组又是什么顾名思义函数指针数组就是存储函数指针的数组跟整型数组、字符数组等其实没有本质区别int(*parr[10])(int,int) parr就是一个函数指针数组。可能你会觉得有一些混淆因为确实数组指针也好函数指针也好写起来都有点麻烦那么我要介绍一种类型重定义技巧可以帮忙简化这些问题其实就是利用typedeftypedef unsigned int uint; typedef int(*pf)(int, int); typedef int(*parr)[10];上述是常见的typedef使用方式注意数组指针和函数指针都是把重定义的类型名放在中间了。
