04二级指针三种内存模型

二级指针

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 *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 ;
    }
    free(*myp1);  //释放完指针变量 所致的内存空间
    *myp1 = NULL;  //把实参修改成nULL
    */
    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 );

    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 ;
}

2. 二级指针的三种输入内存模型

2.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;
}

2.2 第二种内存模型

编译器只会关心多维数组的行数和列数,从而决定了数组名对应的指针变量的步长为列数及其后面维数参数的组合(如果不止二维的话)。

二维数组

#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 ;
}

任何数组名当做参数传递给被调 函数以后,不再具有数组名的属性,在被调函数里对其进行取地址和sizeof操作的结果只是一个普通的指针变量对应的结果。

2.3 第三种内存模型

自己打造内存空间

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;
}

2.4 三种内存模型比较

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

  • 第二种是二维数组,先是全局数据区有字符串常量,在拷贝到栈空间的数组元素,注意结尾有’\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 ;
}

几级指针其实不重要,总之都是内存首地址,只有当我们要使用该段内存空间的数据的时候,才关心他是多少级指针,对应的内存空间存放的是什么数据类型

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

  • 2.5.1使用主调函数在栈空间分配内存,用二维数组做函数参数,返回值是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;
}

  • 2.5.2使用主调函数在堆空间分配内存,用二级指针做函数参数,返回值是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;
}

再次注意,二级指针作为参数传递给函数的时候不要和二维数组混合,二维数组决定了指针步长以及下标引用时候的步长

  • 2.5.3使用被调函数在堆空间分配内存,使用二级指针做返回值抛出分配好的内存空间首地址给主调函数
#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;
}

注意:如果被调函数的形参和主调函数传递的实参是同级指针的话,在被调函数里面不能避免主调函数实参引起的野指针,毕竟是值传递,无法修改主调函数实参指针的值为NULL。

  • 2.5.4使用被调函数在堆空间分配内存,使用三级指针做函数参数,并在被调函数消灭野指针
#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)
            {
                len++;//字符串结束符需要占用一个字节
                myp[countTmp] = (char*)malloc(len*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){//单独加上最后一个不是用分隔符结尾的单词
                len++;
                myp[countTmp] = (char*)malloc(len*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;
}

主要是利用三级指针使得释放内存变得简便直接,直接在被调函数完成,再次强调了在被调函数里使用n级指针可以修改主调函数里n-1级指针的值


使用goto语句使得程序有一个入口,一个出口或者说尽可能少的出口


有指针作为函数参数的时候,记得检验指针的合法性

二级指针三种内存模型强化训练

将第一种内存模型(指针数组/二级指针)和第二种内存模型(二维数组/数组指针)的内容合并到第三种内存模型(自己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;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值