1. 二级指针做输出模型
#define  _CRT_SECURE_NO_WARNINGS 
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
//指针做输出:被调用函数分配内存  -----OK
//指针做输入:主调用函数 分配内存
//求文件中的两段话的长度
int getMem(char **myp1, int *mylen1, char **myp2, int *mylen2)
{
    char *tmp1 = NULL;//打造两个字符串,一级指针临时变量char *tmp1,在堆上分配100空间并赋值,最后用二级指针间接赋值,赋值给形参*myp1=temp1,间接修改实参p1的值
   //p1为一级指针char *p1
   //直接在被调函数完成,再次强调了在被调函数里使用n级指针(形参)可以修改主调函数里n-1级指针(实参)的值
    char *tmp2 = NULL;
    tmp1 = (char *)malloc(100);
    if (tmp1 == NULL)
    {
        return -1;
    }
    strcpy(tmp1, "abcdefg");
    *mylen1 = strlen(tmp1);

//用一级指针做形参修改普通变量(长度len)

    *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 ;
    }
    free(*myp1);  //释放完指针变量 所致的内存空间
    *myp1 = NULL;  //把实参修改成nULL
    */
    //第二种
    //用临时变量一级指针 char *tmp接住二级指针形参char **myp1
    //tmp = *myp1;
    //修改路线:临时变量(一级指针)->形参(二级指针)->实参(一级指针)

    char *tmp = NULL;
    if (myp1 == NULL)
    {
        return -1;
    }
    tmp = *myp1;
    free(tmp);  //释放完指针变量 所致的内存空间
    *myp1 = NULL;  //把实参修改成nULL
    return 0;
}


void main11()
{
    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);  

    system("pause");
    return ;
}

int getMem_Free0(char *myp1)
{
    if (myp1 == NULL)
    {
        return -1;
    }
    free(myp1);  //释放完指针变量 所致的内存空间

    myp1 = NULL;

    return 0;
}

void main14()
{
    char  *p1 = NULL;
    int len1 = 0;

    char *p2 = NULL;
    int len2 = 0;

    int ret = 0;

    ret  = getMem(&p1, &len1, &p2, &len2 );
//传递地址!!!在被调函数里使用n级指针可以修改主调函数里n-1级指针的值
    printf("p1: %s \n", p1);
    printf("p2: %s \n", p2);

    if (p1 != NULL)
    {
        free(p1);
        p1 = NULL;
    }
    if (p2 != NULL)
    {
        free(p2);
        p2 = NULL;
    }

    getMem_Free0(p1);  //在被调用函数中  把p1所指向的内存给释放掉 ,但是 实参p1不能被修改成NULLL 有野指针现象
    getMem_Free0(p2);  

    system("pause");
    return ;
}
  1. 二级指针的三种输入内存模型
    第一种模型是指针数组,数组元素完全在栈空间分配内存,但字符串常量是在全局数据区分配空间
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void printfArray1(char **array,int num)
{

    int i = 0;

    for(i=0;i< num;i++){
        printf("%s\n",*(array+i));
    }
}

int sortArray1(char ** array,int num)
{
    int i = 0,j=0;

    char *tmp = NULL;
    for(i=0;i<num;i++){
        for(j=i+1;j<num;j++){
            if(strcmp(array[i],array[j]) > 0)
            {//交换的是数组元素的值,也就是交换指针的指向
                tmp = array[i];
                array[i] = array[j];
                array[j] = tmp;
            }
        }
    }

    return 0;

}

int main(void)
{
    //数组 数组中的每一个元素是指针 指针数组
    char *strArray[] = {"aaaa","cccc","bbbb","hjkl"};
    int num = sizeof(strArray)/sizeof(strArray[0]);

    printf("排序之前\n");
    printfArray1(strArray,num);

    sortArray1(strArray,num);

    printf("排序之后\n");
    printfArray1(strArray,num);

    printf("Hello World!\n");
    return 0;
}

第二种内存模型

第二种是二维数组,先是全局数据区有字符串常量,在拷贝到栈空间的数组元素,注意结尾有’\0’,以及数组是线性存储。

#define  _CRT_SECURE_NO_WARNINGS 
#include <stdlib.h>
#include <string.h>
#include <stdio.h>


//打印  排序 
//封装成函数
void main31()
{
    int i = 0, j = 0;
    int num = 4;
    char myBuf[30];
    char tmpBuf[30];
    char myArray[10][30] = {"aaaaaa", "ccccc", "bbbbbbb", "1111111111111"};

    //打印 
    printf("排序之前\n");
    for (i=0; i<num; i++)
    {
        printf("%s\n", myArray[i]);
    }

    for (i=0; i<num; i++)
    {
        for (j=i+1; j<num; j++)
        {
            if (strcmp (myArray[i],  myArray[j]) > 0)
            {
                strcpy(tmpBuf, myArray[i]);  //交换的是内存块
                strcpy(myArray[i], myArray[j]);
                strcpy(myArray[j], tmpBuf);
            }
        }
    }

    //打印 
    printf("排序之后\n");
    for (i=0; i<num; i++)
    {
        printf("%s\n", myArray[i]);
    }


    printf("hello...\n");
    system("pause");
    return ;
}

//问题的本质是:dm03_二级指针做输入_第2种内存模型 的 myArray + 1
                // dm03_二级指针做输入_第1种内存模型   myArray + 1 不一样 ;
//指针的步长不一样  指针所指向的内存空间的数据类不一样 。。。。
void printMyArray02_err(char **myArray, int num)
{
    int i = 0;
    for (i=0; i<num; i++)
    {
        //printf("%s \n", myArray[i]);
        printf("%s \n", *(myArray+i) );  //
    }
}

void printMyArray02(char myArray[10][30], int num)
{
    int i = 0;
    for (i=0; i<num; i++)
    {
        //printf("%s \n", myArray[i]);
        printf("%s \n", *(myArray+i) );  //
    }
}



//交换的是内存块。。。。。。。。
void sortMyArray02(char myArray[10][30], int num)
{
    int i, j = 0;
    char tmpBuf[30];

    for (i=0; i<num; i++)
    {
        for (j=i+1; j<num; j++)
        {
            if (strcmp (myArray[i],  myArray[j]) > 0)
            {
                strcpy(tmpBuf, myArray[i]);  //交换的是内存块
                strcpy(myArray[i], myArray[j]);
                strcpy(myArray[j], tmpBuf);
            }
        }
    }

}

//打印  排序 
//封装成函数
void main333()
{
    int i = 0, j = 0;
    int num = 4;
    char myBuf[30];
    char tmpBuf[30];
    char myArray[10][30] = {"aaaaaa", "ccccc", "bbbbbbb", "1111111111111"};

    //myArray: 编译器只会关心:有10行 ,每行30列。。。。。干什么?myArray+1  多维数组名的本质,

    {
        int len1 = sizeof(myArray);
        int len2 = sizeof(myArray[0]);
        int size = len1/len2;
        printf("len1:%d , len2:%d  size:%d \n", len1, len2, size);

    }


    //打印 
    printf("排序之前\n");

    printMyArray02(myArray, num);


    sortMyArray02(myArray, num);

    //打印 
    printf("排序之后\n");
    printMyArray02(myArray, num);

    printf("hello...\n");
    system("pause");
    return ;
}

第三种内存模型
第三种是手工打造二维内存,总指针在栈空间,各数组元素指针在堆空间,堆空间指向的内存也在堆空间

char **getMem(int num)
{
    char ** p2 = NULL;
    int i = 0;
    p2 = (char**)malloc(sizeof(char*)*num);//malloc的结果作为一个数组名赋值给其他变量,类型转换时转换为数组名对应的类型
                                            //如果把malloc的内存空间看做一个数组,里面的元素类型就是sizeof的参数类型,其结果就是比数组元素多一级的指针。
    if(p2 == NULL)
        return NULL;

    for(i= 0;i<num;i++){
        p2[i] = (char*)malloc(100);
        if(NULL == p2[i])
            return NULL;
        sprintf(p2[i],"%d%d%d",i+1,i+1,i+1);
    }

    return p2;

}


/*要避免二级指针成为野指针,在被调函数里释放内存的时候就需要使用三级指针*/
int freeMem(char *** p,int num)
{
    int  i = 0;

    if(p == NULL)
        return -3;
    for(i = 0;i < num;i++){
        printf("%d:%d\n",i+1,(int)(*p)[i]);
        if((*p)[i] != NULL){//先根据指针变量自身的地址使用间接访问操作得到指针的实际指向,再使用下标操作得到具体元素
            free((*p)[i]);
            (*p)[i] = NULL;
             printf("%d:%d\n",i+1,(int)(*p)[i]);
        }
        else
            return -1;
    }

    if((*p)!= NULL){//先分配的内存后释放
        printf("P:%d\n",(int)(*p));
        free((*p));
        (*p) = NULL;
        printf("P:%d\n",(int)(*p));
    }
    else
        return -2;

    return 0;
}

int sortArray3(char ** array,int num)
{
    int i = 0,j=0;

    char *tmp = NULL;

    if(array == NULL)
           return -1;

    for(i=0;i<num;i++){
        for(j=i+1;j<num;j++){
            if(strcmp(array[i],array[j]) < 0){//交换的是数组元素的值,也就是交换指针的指向
                tmp = array[i];
                array[i] = array[j];
                array[j] = tmp;
            }
        }
    }

    return 0;

}
void printfArray3(char **array,int num)
{

    int i = 0;

    if(array == NULL)
           return;

    for(i=0;i< num;i++){
        printf("%s\n",*(array+i));//可见自己在堆空间申请一个指针数组,
                                    //再给指针数组的每个元素单独赋值的模型和第一种模型的指针步长一样
    }
}
int main(void)
{
    //自己在堆空间分配内存
    int num = 5;
    char ** strArray = getMem(num);


    printf("排序之前\n");
    printfArray3(strArray,num);

    sortArray3(strArray,num);

    printf("排序之后\n");
    printfArray3(strArray,num);

    printf("str:%d\n",(int)strArray);
    freeMem(&strArray,num);
    printf("str:%d\n",(int)strArray);

    printf("Hello World!\n");
    return 0;
}

三种内存模型比较

第一种模型是指针数组,数组元素完全在栈空间分配内存,但字符串常量是在全局数据区分配空间

第二种是二维数组,先是全局数据区有字符串常量,在拷贝到栈空间的数组元素,注意结尾有’\0’,以及数组是线性存储。

但是需要注意:显示使用二维数组作为函数参数,可以指定指针步长,与直接使用二级指针不一样

第三种是手工打造二维内存,总指针在栈空间,各数组元素指针在堆空间,堆空间指向的内存也在堆空间

二级指针内存模型_函数

#define  _CRT_SECURE_NO_WARNINGS 
#include <stdlib.h>
#include <string.h>
#include <stdio.h>


void main2()
{
    int i = 0;

    //指针数组
    char *   p1[] = {"123", "456", "789"};


    //二维数组
    char p2[3][4]  = {"123", "456", "789"};

    //手工二维内存
    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);
    }
}


void main555()
{
    printf("hello...\n");
    system("pause");
    return ;
}

两个辅助指针变量挖取单词

使用主调函数在栈空间分配内存,用二维数组做函数参数,返回值是int表示各种结果
虽然数组做参数会退化为指针,但当显示的使用某一类型某一维数的数组做参数以后便隐含的指定了指针的步长,尤其是多维数组

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int spitString(const char * src_str,char cTemp,char dst_strArray[][30],int *nCount)
{

    int ret = 0;//函数返回值,如果字符串首字母不是分隔符,结尾是分隔符,返回零

    char *pTmp = NULL;//辅助指针变量1,用来表示分隔符的地址
    char *p = NULL;//辅助指针变量2,用来表示单词开头的地址

    int len = 0;//每个单词的长度
    int countTemp = 0;//所有单词的个数,会传递给主调函数

    /*1. 将两个指针变量同时指向待处理字符串的起始位置*/
    p = src_str;
    pTmp = src_str;

    /*2. 按照分隔符计算单词个数并挖去单个单词到指定二维数组*/
    do{
        pTmp = strchr(pTmp, cTemp);//搜索待处理字符串中分隔符的起始位置
        if (pTmp != NULL){
            if ((len = (pTmp - p)) > 0){//分隔符的地址减去辅助指针变量2的地址就是单词有效长度,大于零表示首字母不是分隔符
                strncpy(dst_strArray[countTemp],p,len);//len就是单个单词的长度
                dst_strArray[countTemp][len] = '\0';//写成C风格的字符串形式

                /*3. 重新调整辅助指针变量的值*/
                countTemp++;//单词个数增加
                p = pTmp = pTmp + 1;//将寻找位置调整为分隔符后面一个位置
            }
            else{//隔符的地址减去辅助指针变量2的地址就是单词有效长度,等于零表示首字母是分隔符
                p = pTmp = pTmp + 1;//将寻找位置调整为分隔符后面一个位置
                ret = -2;//首字母是分隔符,结尾也是分隔符返回-2
            }
        }
        else{//剩余字符串不含有分隔符
            pTmp = p;//将辅助指针变量1重新指向剩余字符串的开头,因为之前变成了NULL
            while ((*pTmp++ ) != '\0');//计算出结束符的位置,pTmp最后的结果是结束符后面一个字节的位置(字符串越界)

            if ((len = (pTmp - p)) > 0){//此时的len就是包含了结束符'\0'的长度
                strncpy(dst_strArray[countTemp], p, len);//已经复制了'\0'成为有效C风格的字符串形式
                countTemp++;//加上最后一个不含分隔符的单词
            }
            if (ret == -2)
                ret = -1;//首字母是分隔符,结尾不是分隔符返回-1
            else
                ret = -3;//首字母和结尾都不是分隔符返回-3
            break;
        }
    } while ((*pTmp) != '\0');

    *nCount = countTemp;//更新单词个数

    return ret;

}

void printArray(char array[][30],int count,int spit_ret)
{
    int i = 0;
    switch (spit_ret)
    {
    case 0:
        printf("开头非分隔符,结尾是分隔符\n");
        break;
    case -1:
        printf("开头是分隔符,结尾非分隔符\n");
        break;
    case -2:
        printf("开头是分隔符,结尾是分隔符\n");
        break;
    case -3:
        printf("开头非分隔符,结尾非分隔符\n");
        break;
    default:
        break;
    }
    for (i = 0; i < count; i++){//循环打印所有挖取出的单词
        printf("%s\n", array[i]);
    }
}
int main(void)
{
    char *strArray1 = "aaaa,bbbb,cccc,,hghgh,";//待处理字符串
    char *strArray2 = ",aaaa,bbbb,cccc,dddd,hghgh,";
    char *strArray3 = "aaaa,bbbb,cccc,dddd,hghgh";
    char *strArray4 = ",aaaa,bbbb,cccc,dddd,hghgh";
    char dst[10][30] = {0};//存储目标单词用的二维数组,主调函数分配内存

    int count = 0;//单词个数

    int ret = 0;//返回结果

    ret = spitString(strArray1, ',', dst, &count);//调用接口函数
    printArray(dst,count,ret);

    ret = spitString(strArray2, ',', dst, &count);//调用接口函数
    printArray(dst, count, ret);

    ret = spitString(strArray3, ',', dst, &count);//调用接口函数
    printArray(dst, count, ret);

    ret = spitString(strArray4, ',', dst, &count);//调用接口函数
    printArray(dst, count, ret);



    system("PAUSE");
    return ret;
}

使用主调函数在堆空间分配内存,用二级指针做函数参数,返回值是int表示各种结果

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int spitString(const char * src_str,char cTemp,char **dst_strArray,int *nCount)
{

    int ret = 0;//函数返回值,如果字符串首字母不是分隔符,结尾是分隔符,返回零

    char *pTmp = NULL;//辅助指针变量1,用来表示分隔符的地址
    char *p = NULL;//辅助指针变量2,用来表示单词开头的地址

    int len = 0;//每个单词的长度
    int countTemp = 0;//所有单词的个数,会传递给主调函数

    if (src_str == NULL){
        ret = -4;
        return ret;
    }

    if (dst_strArray == NULL){
        ret = -5;
        return ret;
    }

    /*1. 将两个指针变量同时指向待处理字符串的起始位置*/
    p = src_str;
    pTmp = src_str;

    /*2. 按照分隔符计算单词个数并挖去单个单词到指定二维数组*/
    do{
        pTmp = strchr(pTmp, cTemp);//搜索待处理字符串中分隔符的起始位置
        if (pTmp != NULL){
            if ((len = (pTmp - p)) > 0){//分隔符的地址减去辅助指针变量2的地址就是单词有效长度,大于零表示首字母不是分隔符
                strncpy(dst_strArray[countTemp],p,len);//len就是单个单词的长度
                dst_strArray[countTemp][len] = '\0';//写成C风格的字符串形式

                /*3. 重新调整辅助指针变量的值*/
                countTemp++;//单词个数增加
                p = pTmp = pTmp + 1;//将寻找位置调整为分隔符后面一个位置
            }
            else{//隔符的地址减去辅助指针变量2的地址就是单词有效长度,等于零表示首字母是分隔符
                p = pTmp = pTmp + 1;//将寻找位置调整为分隔符后面一个位置
                ret = -2;//首字母是分隔符,结尾也是分隔符返回-2
            }
        }
        else{//剩余字符串不含有分隔符
            pTmp = p;//将辅助指针变量1重新指向剩余字符串的开头,因为之前变成了NULL
            while ((*pTmp++ ) != '\0');//计算出结束符的位置,pTmp最后的结果是结束符后面一个字节的位置(字符串越界)

            if ((len = (pTmp - p)) > 0){//此时的len就是包含了结束符'\0'的长度
                strncpy(dst_strArray[countTemp], p, len);//已经复制了'\0'成为有效C风格的字符串形式
                countTemp++;//加上最后一个不含分隔符的单词
            }
            if (ret == -2)
                ret = -1;//首字母是分隔符,结尾不是分隔符返回-1
            else
                ret = -3;//首字母和结尾都不是分隔符返回-3
            break;
        }
    } while ((*pTmp) != '\0');

    *nCount = countTemp;//更新单词个数

    return ret;

}

void printArray(char **array,int count,int spit_ret)//再次注意,二级指针作为参数传递给函数的时候不要和二维数组混合,
                                                    //二维数组决定了指针步长以及下标引用时候的步长
{
    int i = 0;
    switch (spit_ret)
    {
    case 0:
        printf("开头非分隔符,结尾是分隔符\n");
        break;
    case -1:
        printf("开头是分隔符,结尾非分隔符\n");
        break;
    case -2:
        printf("开头是分隔符,结尾是分隔符\n");
        break;
    case -3:
        printf("开头非分隔符,结尾非分隔符\n");
        break;
    default:
        break;
    }
    for (i = 0; i < count; i++){//循环打印所有挖取出的单词
        printf("%s\n", array[i]);
    }
}
#define COUNT 10
#define COL_COUNT 30
int main(void)
{
    char *strArray1 = "aaaa,bbbb,cccc,,hghgh,";//待处理字符串
    char *strArray2 = ",aaaa,bbbb,cccc,dddd,hghgh,";
    char *strArray3 = "aaaa,bbbb,cccc,dddd,hghgh";
    char *strArray4 = ",aaaa,bbbb,cccc,dddd,hghgh";
    char ** dst = NULL;
    int  i = 0;
    int count = 0;//单词个数

    int ret = 0;//返回结果


    dst = (char **)malloc(COUNT * sizeof(char *));
    if (dst == NULL)
    {
        return -1;
    }
    for (i = 0; i < COUNT; i++){
        dst[i] = (char*)malloc(COL_COUNT * sizeof(char));
        if (dst[i] == NULL)
        {
            return -1;
        }
    }


    ret = spitString(strArray1, ',', dst, &count);//调用接口函数
    printArray(dst,count,ret);

    ret = spitString(strArray2, ',', dst, &count);//调用接口函数
    printArray(dst, count, ret);

    ret = spitString(strArray3, ',', dst, &count);//调用接口函数
    printArray(dst, count, ret);

    ret = spitString(strArray4, ',', dst, &count);//调用接口函数
    printArray(dst, count, ret);

    /*释放内存*/
    for (i = 0; i < COUNT; i++){

        if (dst[i] != NULL)
        {
            free(dst[i]);
            dst[i] = NULL;
            printf("dst[%d]:%d\n", i,(int)dst[i]);
        }
    }

    if (dst != NULL)
    {
        free(dst);
        dst = NULL;
    }
    printf("dst:%d\n",(int)dst);
    system("PAUSE");
    return ret;
}

使用主调函数在堆空间分配内存,用二级指针做函数参数,返回值是int表示各种结果

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int spitString(const char * src_str,char cTemp,char **dst_strArray,int *nCount)
{

    int ret = 0;//函数返回值,如果字符串首字母不是分隔符,结尾是分隔符,返回零

    char *pTmp = NULL;//辅助指针变量1,用来表示分隔符的地址
    char *p = NULL;//辅助指针变量2,用来表示单词开头的地址

    int len = 0;//每个单词的长度
    int countTemp = 0;//所有单词的个数,会传递给主调函数

    if (src_str == NULL){
        ret = -4;
        return ret;
    }

    if (dst_strArray == NULL){
        ret = -5;
        return ret;
    }

    /*1. 将两个指针变量同时指向待处理字符串的起始位置*/
    p = src_str;
    pTmp = src_str;

    /*2. 按照分隔符计算单词个数并挖去单个单词到指定二维数组*/
    do{
        pTmp = strchr(pTmp, cTemp);//搜索待处理字符串中分隔符的起始位置
        if (pTmp != NULL){
            if ((len = (pTmp - p)) > 0){//分隔符的地址减去辅助指针变量2的地址就是单词有效长度,大于零表示首字母不是分隔符
                strncpy(dst_strArray[countTemp],p,len);//len就是单个单词的长度
                dst_strArray[countTemp][len] = '\0';//写成C风格的字符串形式

                /*3. 重新调整辅助指针变量的值*/
                countTemp++;//单词个数增加
                p = pTmp = pTmp + 1;//将寻找位置调整为分隔符后面一个位置
            }
            else{//隔符的地址减去辅助指针变量2的地址就是单词有效长度,等于零表示首字母是分隔符
                p = pTmp = pTmp + 1;//将寻找位置调整为分隔符后面一个位置
                ret = -2;//首字母是分隔符,结尾也是分隔符返回-2
            }
        }
        else{//剩余字符串不含有分隔符
            pTmp = p;//将辅助指针变量1重新指向剩余字符串的开头,因为之前变成了NULL
            while ((*pTmp++ ) != '\0');//计算出结束符的位置,pTmp最后的结果是结束符后面一个字节的位置(字符串越界)

            if ((len = (pTmp - p)) > 0){//此时的len就是包含了结束符'\0'的长度
                strncpy(dst_strArray[countTemp], p, len);//已经复制了'\0'成为有效C风格的字符串形式
                countTemp++;//加上最后一个不含分隔符的单词
            }
            if (ret == -2)
                ret = -1;//首字母是分隔符,结尾不是分隔符返回-1
            else
                ret = -3;//首字母和结尾都不是分隔符返回-3
            break;
        }
    } while ((*pTmp) != '\0');

    *nCount = countTemp;//更新单词个数

    return ret;

}

void printArray(char **array,int count,int spit_ret)//再次注意,二级指针作为参数传递给函数的时候不要和二维数组混合,
                                                    //二维数组决定了指针步长以及下标引用时候的步长
{
    int i = 0;
    switch (spit_ret)
    {
    case 0:
        printf("开头非分隔符,结尾是分隔符\n");
        break;
    case -1:
        printf("开头是分隔符,结尾非分隔符\n");
        break;
    case -2:
        printf("开头是分隔符,结尾是分隔符\n");
        break;
    case -3:
        printf("开头非分隔符,结尾非分隔符\n");
        break;
    default:
        break;
    }
    for (i = 0; i < count; i++){//循环打印所有挖取出的单词
        printf("%s\n", array[i]);
    }
}
#define COUNT 10
#define COL_COUNT 30
int main(void)
{
    char *strArray1 = "aaaa,bbbb,cccc,,hghgh,";//待处理字符串
    char *strArray2 = ",aaaa,bbbb,cccc,dddd,hghgh,";
    char *strArray3 = "aaaa,bbbb,cccc,dddd,hghgh";
    char *strArray4 = ",aaaa,bbbb,cccc,dddd,hghgh";
    char ** dst = NULL;
    int  i = 0;
    int count = 0;//单词个数

    int ret = 0;//返回结果


    dst = (char **)malloc(COUNT * sizeof(char *));
    if (dst == NULL)
    {
        return -1;
    }
    for (i = 0; i < COUNT; i++){
        dst[i] = (char*)malloc(COL_COUNT * sizeof(char));
        if (dst[i] == NULL)
        {
            return -1;
        }
    }


    ret = spitString(strArray1, ',', dst, &count);//调用接口函数
    printArray(dst,count,ret);

    ret = spitString(strArray2, ',', dst, &count);//调用接口函数
    printArray(dst, count, ret);

    ret = spitString(strArray3, ',', dst, &count);//调用接口函数
    printArray(dst, count, ret);

    ret = spitString(strArray4, ',', dst, &count);//调用接口函数
    printArray(dst, count, ret);

    /*释放内存*/
    for (i = 0; i < COUNT; i++){

        if (dst[i] != NULL)
        {
            free(dst[i]);
            dst[i] = NULL;
            printf("dst[%d]:%d\n", i,(int)dst[i]);
        }
    }

    if (dst != NULL)
    {
        free(dst);
        dst = NULL;
    }
    printf("dst:%d\n",(int)dst);
    system("PAUSE");
    return ret;
}

使用被调函数在堆空间分配内存,使用二级指针做返回值抛出分配好的内存空间首地址给主调函数

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char **spitString(const char * src_str,char cTemp,int *nCount,int *ret)
{
    char *pTmp = NULL, *p = NULL;
    char **myp = NULL;

    int countTmp = 0;
    int flag = 0,len = 0;

    pTmp = p = src_str;

    /************************************************************************/
    /* 第一次循环,用于检测出有多少个单词                                                                     */
    /************************************************************************/
    do{
        pTmp = strchr(pTmp, cTemp);
        if (pTmp != NULL){
            if ((len = pTmp - p) > 0)
            {
                countTmp++;
                pTmp = p = pTmp + 1;
            }
            else{
                flag = -2;
                pTmp = p = pTmp + 1;//处理分隔符开头的字符串或者有两个分隔符连续的字符串
            }
        }
        else{//待处理字符串不是以分隔符结尾
            pTmp = p;
            while ((*pTmp) != '\0'){//将第二个辅助指针变量移动到字符串结束符'\0'的位置
                pTmp++;
            }
            if (pTmp - p > 0){//单独加上最后一个不是用分隔符结尾的单词
                countTmp++;
            }
            break;
        }
    } while ((*pTmp) != '\0');

    *nCount = countTmp;

    myp = (char **)malloc(countTmp*sizeof(char *));
    if (myp == NULL){
        flag = -4;//一维内存分配失败
        *ret = flag;
        return NULL;
    }

    /************************************************************************/
    /* 第二次循环,用于挖取单词                                                                    */
    /************************************************************************/
    pTmp = p = src_str;
    countTmp = 0;
    do{
        pTmp = strchr(pTmp, cTemp);
        if (pTmp != NULL){
            if ((len = pTmp - p) > 0)
            {
                len++;//字符串结束符需要占用一个字节
                myp[countTmp] = (char*)malloc(len*sizeof(char));
                if (myp[countTmp] == NULL){
                    flag = -5;
                    *ret = flag;
                    return NULL;
                }
                strncpy(myp[countTmp],p,--len);//先复制单词有效长度的字节
                myp[countTmp][len] = '\0';//最后一个字节存储结束符
                countTmp++;
                pTmp = p = pTmp + 1;
            }
            else{
                flag = -2;
                pTmp = p = pTmp + 1;//处理分隔符开头的字符串,假设不存在两个分隔符连续的情况
            }
        }
        else{//待处理字符串不是以分隔符结尾
            pTmp = p;
            while ((*pTmp) != '\0'){//将第二个辅助指针变量移动到字符串结束符'\0'的位置
                pTmp++;
            }
            if ((len = pTmp - p) > 0){//单独加上最后一个不是用分隔符结尾的单词
                len++;
                myp[countTmp] = (char*)malloc(len*sizeof(char));
                if (myp[countTmp] == NULL){
                    flag = -5;//二维内存分配失败
                    *ret = flag;
                    return NULL;
                }
                strncpy(myp[countTmp], p, --len);
                myp[countTmp][len] = '\0';
                countTmp++;
            }
            if (flag == -2)
                flag = -1;
            else
                flag = -3;
            break;
        }
    } while ((*pTmp) != '\0');

    *ret = flag;
    return myp;

}

void FreeMem(char **myp, int count)
{
    int i = 0;
    if (myp == NULL)
    {
        return;
    }
    for (i = 0; i < count; i++)
    {
        if (myp[i] != NULL)
        {
            free(myp[i]);
            myp[i] = NULL;
        }
    }
    if (myp != NULL)
    {
        free(myp);
        //myp=NULL;//注意:这里并不会影响到主调函数的二级指针的值,除非使用三级指针
    }
}

void printArray(char **array,int count,int spit_ret)//再次注意,二级指针作为参数传递给函数的时候不要和二维数组混合,
                                                    //二维数组决定了指针步长以及下标引用时候的步长
{
    int i = 0;
    switch (spit_ret)
    {
    case 0:
        printf("开头非分隔符,结尾是分隔符\n");
        break;
    case -1:
        printf("开头是分隔符,结尾非分隔符\n");
        break;
    case -2:
        printf("开头是分隔符,结尾是分隔符\n");
        break;
    case -3:
        printf("开头非分隔符,结尾非分隔符\n");
        break;
    case -4:
        printf("一维内存分配失败\n");
        break;
    case -5:
        printf("二维内存分配失败\n");
        break;
    default:
        break;
    }
    for (i = 0; i < count; i++){//循环打印所有挖取出的单词
        printf("%s\n", array[i]);
    }
}

int main(void)
{
    char *strArray1 = "aaaa,bbbb,cccc,hghgh,";//待处理字符串
    char *strArray2 = ",aaaa,bbbb,cccc,dddd,hghgh,";
    char *strArray3 = "aaaa,bbbb,cccc,dddd,hghgh";
    char *strArray4 = ",aaaa,bbbb,cccc,dddd,hghgh";
    char cTmp = ',';
    char ** dst = NULL;
    int  i = 0;
    int count = 0;//单词个数

    int ret = 0;//返回结果

    dst = spitString(strArray1, cTmp, &count, &ret);
    if (dst == NULL){
        return -1;
    }
    printArray(dst, count, ret);
    FreeMem(dst, count);
    dst = NULL;//在被调函数里无法消除主调函数里dst的野指针,除非使用三级指针

    dst = spitString(strArray2, cTmp, &count, &ret);
    if (dst == NULL){
        return -1;
    }
    printArray(dst, count, ret);
    FreeMem(dst, count);
    dst = NULL;

    dst = spitString(strArray3, cTmp, &count, &ret);
    if (dst == NULL){
        return -1;
    }
    printArray(dst, count, ret);
    FreeMem(dst, count);
    dst = NULL;

    dst = spitString(strArray4, cTmp, &count, &ret);
    if (dst == NULL){
        return -1;
    }
    printArray(dst, count, ret);
    FreeMem(dst, count);
    dst = NULL;


    system("PAUSE");
    return ret;
}

使用被调函数在堆空间分配内存,使用三级指针做函数参数,并在被调函数消灭野指针

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


void FreeMem(char ***myp, int count)
{
    int i = 0;
    char ** p_tmp = NULL;

    if (myp == NULL)
    {
        return;
    }

    p_tmp = *myp;
    if (p_tmp == NULL)
    {
        return;
    }


    for (i = 0; i < count; i++)
    {
        if (p_tmp[i] != NULL)
        {
            free(p_tmp[i]);
            p_tmp[i] = NULL;
        }
    }
    if (p_tmp != NULL)
    {
        free(p_tmp);
        p_tmp = NULL;
    }
    *myp = NULL;//注意:这里由于使用三级指针,所以可以根据二级指针的地址修改二级指针的值
}

int spitString(const char * src_str,char cTemp,char *** dst,int *nCount)
{
    char *pTmp = NULL, *p = NULL;
    char **myp = NULL;

    int countTmp = 0;
    int flag = 0,len = 0;

    pTmp = p = src_str;

    /************************************************************************
                        第一次循环,用于检测出有多少个单词                                                                     
    ************************************************************************/
    do{
        pTmp = strchr(pTmp, cTemp);
        if (pTmp != NULL){
            if ((len = pTmp - p) > 0)
            {
                countTmp++;
                pTmp = p = pTmp + 1;
            }
            else{
                flag = -2;
                pTmp = p = pTmp + 1;//处理分隔符开头的字符串或者有两个分隔符连续的字符串
            }
        }
        else{//待处理字符串不是以分隔符结尾
            pTmp = p;
            while ((*pTmp) != '\0'){//将第二个辅助指针变量移动到字符串结束符'\0'的位置
                pTmp++;
            }
            if (pTmp - p > 0){//单独加上最后一个不是用分隔符结尾的单词
                countTmp++;
            }
            break;
        }
    } while ((*pTmp) != '\0');

    *nCount = countTmp;

    myp = (char **)malloc(countTmp*sizeof(char *));
    if (myp == NULL){
        flag = -4;//一维内存分配失败
        goto END;
    }
    memset(myp, 0, countTmp*sizeof(char *));//分配好内存以后,用memset初始化


    /************************************************************************/
    /* 第二次循环,用于挖取单词                                                                    */
    /************************************************************************/
    pTmp = p = src_str;
    countTmp = 0;
    do{
        pTmp = strchr(pTmp, cTemp);
        if (pTmp != NULL){
            if ((len = pTmp - p) > 0)
            {
                myp[countTmp] = (char*)malloc((len+1)*sizeof(char));//字符串结束符需要占用一个字节
                if (myp[countTmp] == NULL){
                    flag = -5;
                    goto END;
                }
                strncpy(myp[countTmp],p,len);//先复制单词有效长度的字节
                myp[countTmp][len] = '\0';//最后一个字节存储结束符
                countTmp++;
                pTmp = p = pTmp + 1;
            }
            else{
                flag = -2;
                pTmp = p = pTmp + 1;//处理分隔符开头的字符串,假设不存在两个分隔符连续的情况
            }
        }
        else{//待处理字符串不是以分隔符结尾
            pTmp = p;
            while ((*pTmp) != '\0'){//将第二个辅助指针变量移动到字符串结束符'\0'的位置
                pTmp++;
            }
            if ((len = pTmp - p) > 0){//单独加上最后一个不是用分隔符结尾的单词
                myp[countTmp] = (char*)malloc((len+1)*sizeof(char));
                if (myp[countTmp] == NULL){
                    flag = -5;//二维内存分配失败
                    goto END;
                }
                strncpy(myp[countTmp], p, len);
                myp[countTmp][len] = '\0';
                countTmp++;
            }
            if (flag == -2)
                flag = -1;
            else
                flag = -3;
            break;
        }
    } while ((*pTmp) != '\0');

    *dst = myp;
    return flag;//正常分配内存、挖取字符串并不会释放内存

END://使用goto语句使得程序有一个入口,一个出口或者说尽可能少的出口
    FreeMem(&myp, countTmp);
    return flag;

}


void printArray(char **array,int count,int spit_ret)
//再次注意,二级指针作为参数传递给函数的时候不要和二维数组混合,
 //二维数组决定了指针步长以及下标引用时候的步长
{
    int i = 0;
    switch (spit_ret)
    {
    case 0:
        printf("开头非分隔符,结尾是分隔符\n");
        break;
    case -1:
        printf("开头是分隔符,结尾非分隔符\n");
        break;
    case -2:
        printf("开头是分隔符,结尾是分隔符\n");
        break;
    case -3:
        printf("开头非分隔符,结尾非分隔符\n");
        break;
    case -4:
        printf("一维内存分配失败\n");
        break;
    case -5:
        printf("二维内存分配失败\n");
        break;
    default:
        break;
    }
    for (i = 0; i < count; i++){//循环打印所有挖取出的单词
        printf("%s\n", array[i]);
    }
}

int main(void)
{
    char *strArray1 = "aaaa,bbbb,cccc,hghgh,";//待处理字符串
    char *strArray2 = ",aaaa,bbbb,cccc,dddd,hghgh,";
    char *strArray3 = "aaaa,bbbb,cccc,dddd,hghgh";
    char *strArray4 = ",aaaa,bbbb,cccc,dddd,hghgh";
    char cTmp = ',';
    char ** dst = NULL;
    int  i = 0;
    int count = 0;//单词个数

    int ret = 0;//返回结果

    ret = spitString(strArray1, cTmp, &dst, &count);
    printArray(dst, count, ret);
    FreeMem(&dst, count);

    ret = spitString(strArray2, cTmp, &dst, &count);
    printArray(dst, count, ret);
    FreeMem(&dst, count);

    ret = spitString(strArray3, cTmp, &dst, &count);
    printArray(dst, count, ret);
    FreeMem(&dst, count);

    ret = spitString(strArray4, cTmp, &dst, &count);
    printArray(dst, count, ret);
    FreeMem(&dst, count);

    system("PAUSE");
    return ret;
}

三种内存模型训练

将第一种内存模型(指针数组/二级指针)和第二种内存模型(二维数组/数组指针)的内容合并到第三种内存模型(自己malloc)并排序.

#define _CRT_SECURE_NO_WARNINGS


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int createNewLife(char **myp1,int num1,char (*myp2)[30],int num2,char ***myp3,int *num3)
{
    int ret = 0;
    char **dst_ptr = NULL;
    char *tmp_ptr = NULL;
    int tmp_len = 0;
    int i = 0, k = 0;

    if (myp1 == NULL || myp2 == NULL || myp3 == NULL){
        return -1;
    }

    dst_ptr = (char **)malloc(sizeof(char*)*(num1 + num2));
    if (dst_ptr == NULL)
    {
        return -2;
    }
    k = 0;
    for (i = 0; i < num1; i++, k++)
    {
        tmp_len = strlen(myp1[i]) + 1;
        dst_ptr[k] = (char*)malloc(sizeof(char)*tmp_len);
        if (dst_ptr[k] == NULL)
        {
            return -3;
        }
        strcpy(dst_ptr[k],myp1[i]);
    }

    for (i = 0; i < num2; i++, k++)
    {
        tmp_len = strlen(myp2[i]) + 1;
        dst_ptr[k] = (char*)malloc(sizeof(char)*tmp_len);
        if (dst_ptr[k] == NULL)
        {
            return -3;
        }
        strcpy(dst_ptr[k], myp2[i]);
    }

    tmp_len = num1 + num2;

    for (i = 0; i < tmp_len; i++)
    {
        for (k = i + 1; k < tmp_len; k++)
        {
            if (strcmp(dst_ptr[i], dst_ptr[k])>0)
            {
                tmp_ptr = dst_ptr[i];
                dst_ptr[i] = dst_ptr[k];
                dst_ptr[k] = tmp_ptr;
            }
        }
    }


    *num3 = tmp_len;
    *myp3 = dst_ptr;
    return ret;
}


void freeNewLife(char ***ptr,int num)
{
    char ** tmp_ptr = NULL;
    int i = 0;

    if (ptr == NULL)
    {
        return -1;
    }

    tmp_ptr = *ptr;

    if (tmp_ptr == NULL)
    {
        return -2;
    }

    for (i = 0; i < num; i++)
    {
        if (tmp_ptr[i] != NULL)
        {
            free(tmp_ptr[i]);
            tmp_ptr[i] = NULL;
        }
    }
    free(tmp_ptr);

    *ptr = NULL;
}
int main(void)
{
    int ret = 0;
    char *p1[] = { "aa", "ccccccc", "bbbbbb" };
    char buf2[10][30] = { "111111", "3333333", "222222" };
    char **p3 = NULL;
    int len1, len2, len3, i = 0;

    len1 = sizeof(p1) / sizeof(*p1);
    len2 = 3;

    ret = createNewLife(p1, len1, buf2, len2, &p3, &len3);
    if (ret != 0)
    {
        printf("func sort() err:%d \n", ret);
        return ret;
    }

    for (i = 0; i < len3; i++)
    {
        printf("%s\n", p3[i]);
    }

    freeNewLife(&p3, len3);
    system("PAUSE");
    return 1;
}