题目
//有一个字符串符合以下特征(”abcdef,acccd,eeee,aaaa,e3eeeee,sssss,";),用逗号分割
赋值给char **p
main函数可以这样输出:
p[0] : “abcdef”
p[1] : “acccd”
p[2] : “eeee”
p[3] : “aaaa”
p[4] : “e3eeeee”
p[5] : “sssss”
题外话:二级指针三种内存模型
tips : 看到这就动笔画一下三种模型的内存四区示意图
1、指针数组模型
char *p[] = {"123", "456", "789"};
2、二维数组模型
char p[3][4] = {"123", "456", "789"};
3、手工二维内存(堆区开辟)
char **p = NULL;
p = (char **)malloc(sizeof(char *) * 3); //相当于char arr[3];
if(int i = 0; i < 3; i++)
{
p[i] = (char *)malloc(sizeof(char) * 4); //相当于char buf[4];
}
p[0] = "123";
p[1] = "456";
p[2] = "789";
//用完还需要释放
if(int i = 0; i < 3; i++)
{
free(p[i]);
}
free(p);
p = NULL; //假如在函数中,这句话也可以不要,不会出现野指针的情况,函数结束系统自动出栈了
回到题目
0、函数调用模型的建立
int splitString(char *buf, char item, char ***arr, int rows)
依次为:输入字符串、输入分割参数、输入&输出二级指针模型的地址,二级指针的行
1、main函数调用
void main()
{
int ret = 0, i = 0;
char* p1 = "abcdef, acccd, eeee, aaaa, e3eeeee, sssss, ";
char cTem = ',';
int nCount;
char** p = NULL; //char buf[][] char *p[]
ret = splitString(p1, cTem, &p, &nCount);
if (ret != 0)
{
printf("fucn spitString() err: %d \n", ret);
return;
}
//输出测试
for (i = 0; i < nCount; i++)
{
printf("%s \n", p[i]);
}
system("pause");
return;
}
2、分割函数
int splitString(char* buf, char item, char*** arr, int* rows)
{
int ret = 0;
//判断输入是否为空
if (buf == NULL)
{
ret = -1;
printf("func splitString err:%d buf == NULL", ret);
return ret;
}
//二级指针,用于代替*arr操作,最后赋值*arr = tmpArr即可
char** tmpArr = NULL;
//两个指针变量
char* p = NULL;
char* pTmp = NULL;
//同时指向buf的首地址
p = buf;
pTmp = buf;
int tmpCount = 0; //逗号的个数
//找到有多少逗号
do
{
p = strchr(p, item); //找到item在字符串中第一次出现的索引,没有返回NULL
if (p != NULL)
{
if ((p - pTmp) > 0)
{
tmpCount++;
pTmp = p = p + 1;
}
}
else
{
break;
}
} while (*p != '\0');
tmpArr = (char**)malloc(sizeof(char*) * (tmpCount+1)); //多给一维,万一最后没有逗号,逗号就会比输出的字符串个数少一个
if (tmpArr == NULL)
{
ret = -2;
printf("fun splitString err: %d tmpArr == NULL", ret);
goto END;
}
/*************************************/
tmpCount = 0;
p = buf;
pTmp = buf;
//接下来分配堆区的内存给二维数组
do
{
p = strchr(p, item);
if (p != NULL)
{
if ((p - pTmp) > 0)
{
int len = p - pTmp + 1;
tmpArr[tmpCount] = (char*)malloc(sizeof(char) * len);
if (tmpArr[tmpCount] == NULL)
{
ret = -3;
printf("fun splitString err: %d tmpArr[tmpCount] == NULL", ret);
goto END;
}
strncpy(tmpArr[tmpCount], pTmp, p - pTmp);
tmpArr[tmpCount][p - pTmp] = '\0';
pTmp = p = p + 1;
tmpCount++;
}
}
else
{
break;
}
} while (*p != '\0');
END:
//释放内存
if (ret != 0)
{
FreeMem(&tmpArr, tmpCount);
}
else
{
*arr = tmpArr;
*rows = tmpCount; //输出二维数组的行数是逗号个数加一
}
return ret;
}
3、释放内存函数
void FreeMem(char*** p, int cnt)
{
char** tmp = NULL;
if (p == NULL)
{
return;
}
tmp = *p;
if (tmp == NULL)
{
return;
}
else
{
for (int i = 0; i < cnt; i++)
{
if (tmp[i] != NULL)
{
free(tmp[i]);
}
}
if (tmp != NULL)
{
free(tmp);
}
}
*p = NULL;
}
总结
二维指针模型比较复杂
- 首先了解C风格字符串其实就是字符数组末尾为
'\0'
- 接下来掌握三种内存模型的内存四区图画法
- 指针的精髓在于间接赋值,例如我想对
int p;
赋值,我用int *pp = &p;
然后*pp
做一系列balabala的操作,就是对p
所代表的空间在操作; - 堆区开辟内存要释放,不然占用内存,而且容易出现野指针的情况
- 野指针就是指,内存空间已经释放了,但是我的指针p的值还是指向的释放的内存空间,那里面已经没有值了,所以在
free(p)
操作后再加p = NULL;
的操作