二级指针是一个指向指针的指针。
用形象的话说就是:
一个变量的内存就是一个饭馆,指针就是一张纸,上面写了这个饭馆的地址(红魔馆以南200米路西),你照着地址走就能找到。而二级指针也是一张纸,你翻开一看写了一个纸条的位置(门卫的桌子上),你要先在门卫的桌子上找到纸条,然后在根据纸条上的信息找到饭馆。纸条是一级指针,叙述纸条位置的纸是二级指针,三级四级以此类推。
那为啥要用二级指针呢?
二级指针可以做参数输出
这是很容易理解的,给一个函数传入一个参数,希望函数把这个函数处理一下。
说的更具体一点就是,我要经常使用一类字符串,但是它们有细微的差别,每次都自己定义又太麻烦,于是想把这个任务交给一个函数。
下面的程序中,通过输入函数的指针 p 来获取字符串"hello world",但是第一各函数失败了,第二个函数成功了,就是因为第二个函数用到了二级指针。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int getMem1(char *p)
{
//从堆中申请空间交给p
p = (char *)malloc(sizeof(char) * 100);
//存入字符串
strcpy(p, "hello world");
printf("getMem1 p = %s\n",p);
return 0;
}
int getMem2(char **p)
{
if (p == NULL)
{
return -1;
}
//从堆中申请空间
char *temp = (char *)malloc(sizeof(char) * 100);
if (temp == NULL)//判断空间是否被申请,可以忽略
{
return -2;
}
//存入字符串
strcpy(temp, "hello world");
//把字符串交给*p
*p = temp;
printf("getMem2 p = %s\n", temp);
return 0;
}
int main(void)
{
char *p = NULL;
//一级指针做函数的参数输出
int ret = getMem1(p);
//判断函数是否正常结束,可忽略
if (ret != 0)
{
printf("err :%d\n",ret);
return ret;
}
printf("p = %s\n",p);
if (p != NULL)
{
free(p);
p = NULL;
}
//二级指针做函数的参数输出
ret = getMem2(&p);
//判断函数是否正常结束,可忽略
if (ret != 0)
{
printf("err :%d\n", ret);
return ret;
}
printf("p = %s\n", p);
if (p != NULL)
{
free(p);
p = NULL;
}
system("pause");
return 0;
}
可以看出 getMem1() 在函数体内确实给了 p 一个 “hello world” ,不过出了函数就没了,因为它把形参指向了字符串,而函数一结束形参就没了。
getMem2() 在函数体内部和外部都能保证 p 拥有 “hello world” ,因为它是把 p 指向了字符串。
读懂了上面的例子就能介绍二级指针更花样的玩法了
二级指针做输入的三种模型
指针数组
第一个模型就是一个指针数组,很好理解的一种模型:
数组本身就是一个指针,而如果数组中的每个元素都是指针的话,数组名自然就是一个二级指针了(指向指针的指针)。
下面的一个程序就是对指针数组的排序操作
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//打印数组中的元素
void print_array(char **p, int n)
{
int i = 0;
for (i = 0; i < n; i++)
{
printf("p[%d]:%s\n", i, p[i]);
}
}
void sort_array(char **p, int n)
{
int i, j;
char *temp;
//选择法排序
for (i = 0; i < n; i++)
{
for (j = i + 1; j < n; j++)
{
if (strcmp(p[i], p[j]) > 0)
{
temp = p[i];
p[i] = p[j];
p[j] = temp;
}
}
}
}
int main(void)
{
int i = 0, j = 0;
char *temp = NULL;
//指针数组,指针的数组,是一个数组,每个元素都是指针
char *p[] = { "11111","00000","33333","22222" };
//获取数组中元素的个数
int n = sizeof(p) / sizeof(p[0]);
printf("排序前:\n");
print_array(p,n);
//选择法排序
sort_array(p,n);
printf("排序后:\n");
print_array(p,n);
system("pause");
return 0;
}
二维数组
其实二维数组也是个二级指针,它的首先指向一堆一维数组(行),然后在根据一维数组指向各个元素的内存。
如 a[2] [3]
a是一个二级指针,a[0],a[1],a[2]都是些一级指针,a[0][1]之类的才是真正的内存
下面的程序就是展示a[0]到底有多长的
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//a[][30]是为了告诉函数 a + 1要移动的步长
void print_array(char a[][30], int n)
{
int i = 0;
printf("a:%d,a+1:%d\n", a, a + 1);//a+1成为了&a+4
for (i = 0; i < n; i++)
{
printf("%s ",a[i]);
//printf("%s ", *(a + i));//首行地址和首元素的地址相同
}
printf("\n");
}
void sort_array(char a[][30],int n)
{
int i = 0, j = 0;
char tmp[30];
for (i = 0; i < n; i++)
{
for (j = i + 1; j < n; j++)
{
if (strcmp(a[i], a[j]) > 0)
{
strcpy(tmp,a[i]);
strcpy(a[i],a[j]);
strcpy(a[j],tmp);
}
}
}
}
int main(void)
{
//a[]等价于*a
int i = 0;
//4个a[30]的一维数组,二维数组
//a代表首行地址,首行地址和首行首元素的地址有区别,但值是一样的,区别:步长不一样
char a[4][30] = { "aaaaaaa" ,"bbbbbbb" ,"vvvvvvv" ,"1111111" };
//获取行数
int n = sizeof(a) / (sizeof(a[1]));
printf("排序前\n");
print_array(a,n);
sort_array(a, n);
printf("排序后\n");
print_array(a, n);
system("pause");
return 0;
}
手动创建的二维数组
这种结构画出来是这样的,跟二维数组有极大的相似性,都是一个二级指针指向了一堆一级指针,然后这些一级指针再指向一些变量,不过这并不算二维数组,因为每一行(为了方便叙述)没有必要长度一样,比如p[0]长度8,p[1]完全可以长度是9,p[2]长256,没一点问题
下面的程序是对上面的结构的一个创建方式
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//分配空间
char ** getMem(int n)
{
int i = 0;
//声明一个二级指针,给它分配n个一级指针的空间
char **buf = (char **)malloc(n * sizeof(char *));
if (buf == NULL)
{
printf("buf err\n");
return NULL;
}
for (i = 0; i < n; i++)
{
char str[30];
//给buf的每个元素再分配30个char的空间
buf[i] = (char *)malloc(30 * sizeof(char));
//产生数据
sprintf(str, "test%d%d", i, i);
//装填数据
strcpy(buf[i], str);
}
return buf;
}
//只是为了展示buf中的内容而写的函数
void print_buf(char **buf,int n)
{
int i = 0;
for (i = 0; i < n; i++)
{
printf("%s ", buf[i]);
}
printf("\n");
}
//为了完整释放掉buf的空间而写的函数
void free_buf(char **buf,int n)
{
int i;
//先释放buf中的元素
for (i = 0; i < n; i++)
{
free(buf[i]);
buf[i] = NULL;
}
//再释放buf
if (buf != NULL)
{
free(buf);
buf = NULL;
}
}
int main(void)
{
//10个char *,每一个的值都是空
int n = 3;
char **buf = NULL;
buf = getMem(n);
if (buf == NULL)
{
printf("getMem err\n");
return -1;
}
print_buf(buf,n);
free_buf(buf,n);
buf = NULL;
system("pause");
return 0;
}