读书笔记 《C Primer Plus》(2-指针数组-数组指针-指针函数-函数指针)
指针是C语言的灵魂,成也指针,败也指针。
在指针的世界里,就有了一些比较拗口的概念:指针数组/数组指针/指针函数/函数指针,本文旨在说清楚这些概念。
指针数组
场景:
在某些情况,有一系列的指针是相关联的,比如有很多常量字符串“AAA”,“BBB”"CCC"等,通过数组保存他们,可以写成:
const char pStr[] = {"AAA", "BBB", "CCC"};
我们在代码逻辑中,可以动态地选择不同的数组元素。
比如遍历这三个元素:
for(int i = 0 ; i < sizeof(pStr)/sizeof(char *); i++)
{
printf("pStr[%d]=%s\n", i, pStr[i]);
}
指针数组,是数组,只是这个数组中每个元素为指针。
数组指针
场景:
指针非常的灵活,如果使用指针指向一个数组,那么对指针进行运算,则可以很方便地读写数据。
比如简单的指针指向一维数组:
char arr[10] = {0};
char *pArr = arr; //此后可以通过pArr读写arr数组
如果是一个二维数组:
char arr[5][10] = {0};
char (*pArr)[10] = arr; //此后可以通过pArr,指向一个[10]的数组
传递参数的时候,可以使用数组指针,指明参数的类型:
void testFunc(char (*pArr)[10])
{
int i;
for(i = 0; i < 5; i ++)
printf("[%s]\n", pArr[i]);
}
int main(void)
{
char arr[5][10] = {"12345", "22345", "32345", "42345", "52345", };
char (*pArr)[10] = arr; //此后可以通过pArr,指向一个[10]的数组
testFunc(pArr);
return 0;
}
类似,我们可以使一个指针指向一个三维或者更高维度的数组。
因此,数组指针,是一个指针,指向一个数组,通过该指针读写数组。
指针函数
指针函数,表明这是一个函数,不过这个函数返回的在一个指针类型的数据。
场景:
对于一些资源分配的函数,比如malloc的逻辑封装:
char * mallocFactory(int len)
{
char *pMalloc = (char *)malloc(len);
return pMalloc;
}
这样做封装的好处是资源管理与代码逻辑相对独立,便于平台移植或者资源的统一管理。
函数指针
函数指针,表明这是一个指针,不过这个指针指向的是一个函数。
函数指针需要先定义一种函数的类型,确定该函数的参数与返回值类型。
然后将该指针指向一个具体的函数,便于具体的函数被执行。
场景:
经典的场景包括回调函数,注册函数等。
比如某个排序算法逻辑已经完成,需要用户传入特定逻辑的函数,完成大小比较。
c语言的qsort函数就是这样,该函数原型如下:
void qsort(
void *base,
size_t nmemb,
size_t size,
int (*compar)(const void *, const void *)
);
可以看到第四个参数需要传入函数指针,而且已经规定好了函数类型。
函数可以有不同的签名(出入参,返回值类型不同),用typedef可方便定义类型,比如:
int MyAdd(int a, int b)
{
cout<<"MyAdd: "<<a<<"+"<<b<<endl;
return a+b;
}
typedef int (FuncType)(int, int); //声明函数类型
typedef int (*pFunc)(int, int); //声明函数指针类型
int (*pFuncAdd)(int, int); //定义变量,是函数指针类型的变量
int TestFunc() {
FuncType *addFunc1 = MyAdd;
pFunc addFunc2 = MyAdd;
pFuncAdd = MyAdd;
addFunc1(1, 2);
addFunc2(3, 4);
pFuncAdd(5, 6);
return 0;
}
语言只是一个工具,工具中有不一样的小工具适应不同的需求。
拗口的概念只是在一些比较相近或者相似的场景下的应用。