二级指针算是一种相对高级的用法,很多人会因为其复杂,难以理解而尽可能的规避它。但在大型项目开发中,通常无法会避免使用二级指针,而且学习二级指针,能够帮助我们巩固指针存在的意义,学会了二级指针,你才真正意义上的掌握了c语言。指针存在的意义就是用来作函数参数的,二级指针也不例外。作为函数参数,我们把主函数比作功能齐全的系统,把子函数比作拥有特定功能的模块,那么对于大部分任务都会有如下两种需求:
1.子函数为了执行特定的功能,需要从主函数中获取信息,这就需要主函数向子函数输入相应的信息。
2.主函数为了更好的执行系统功能,需要从子函数中获取信息,这就需要子函数向主函数输出相应的信息。
上述的输入和输出功能,就是指针这家伙用作函数参数时最大的意义。因此,下文就会详细的展开讲,二级指针作函数参数时,分别作输出和输入时的使用特性。
1.指针作为输入
指针作为输入时,主函数会通过指针向子函数传递数据,子函数对主函数的数据进行操作,因此需要在主函数中分配内存空间。而指针作输入时的内存模型有如下三种。
void main()
{
int i = 0;
//第一种:指针数组
char * p1[] = {"123", "456", "789"};
//第二种:二维数组
char p2[3][4] = {"123", "456", "789"};
//第三种:malloc二维内存
char **p3 = (char **)malloc(3 * sizeof(char *)); //int array[3];
for (i=0; i<3; i++)
{
p3[i] = (char *)malloc(10*sizeof(char)); //char buf[10]
sprintf(p3[i], "%d%d%d", i, i, i);
}
}
如下为上述三种内存模型的内存四区图,通过内存四区图我们应该就能很直观的感受到三种二级指针内存模型的异同点了。
2.指针作为输出:
子函数要向主函数输出信息,因此信息主体在子函数中产生,所以我们要在子函数中分配内存,而为了使主函数能够使用子函数中分配的内存,需要将内存分配在堆空间上,这样当子函数退出时,该内存空间不会被自动释放。当然不要忘了在主函数里free堆空间的内存。
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
//指针做输出:被调用函数分配内存
//求文件中的两段话的长度
int getMem(char **myp1, int *mylen1, char **myp2, int *mylen2)
{
char *tmp1 = NULL;
char *tmp2 = NULL;
tmp1 = (char *)malloc(100);
if (tmp1 == NULL)
{
return -1;
}
strcpy(tmp1, "abcdefg");
*mylen1 = strlen(tmp1);
*myp1 = tmp1; //间接修改实参p1的值
tmp2 = (char *)malloc(100);
if (tmp2 == NULL)
{
return -2;
}
strcpy(tmp2, "11122233333");
*mylen2 = strlen(tmp2);
*myp2 = tmp2; //间接修改实参p1的值
return 0;
}
int getMem_Free(char **myp1)
{
if (myp1 == NULL)
{
return -1;
}
tmp = *myp1;
free(tmp); //释放完指针变量 所致的内存空间
*myp1 = NULL; //把实参修改成nULL
return 0;
}
void main()
{
char *p1 = NULL;
int len1 = 0;
char *p2 = NULL;
int len2 = 0;
int ret = 0;
ret = getMem(&p1, &len1, &p2, &len2 );
printf("p1: %s \n", p1);
printf("p2: %s \n", p2);
getMem_Free(&p1);
getMem_Free(&p2);
return ;
}
Note:上述代码就不作过多剖析了,个人浅见,把内存四区模型刻进你的大脑皮层,你终将玩弄指针于鼓掌。emmm ,我来玩弄下。根据下图的代码段和其内存四区模型我们发现,代码free的只是tmp,最终的myp1这个指针将会是野指针。
那么从下面的代码段和其内存四区模型我们发现,利用更高一级的指针作形参就可以防止出现野指针。