12.C语言提高(二)

作者: 任振铭 | 来源:发表于2018-07-13 07:59 被阅读16次
    1.二维数组的本质

    二维数组的本质是一个数组指针,放宽来说多维数组的本质也是一个数组指针

    int arr[i][j]

    (arr+i)代表第i行的地址 二级指针
    *(arr+i)代表第i行首元素的地址 一级指针 ,第i行地址和第i行首元素地址虽然是相同的
    但是指针的表示形式不同
    *(arr+i)+j == arr[i][j] 代表第i行第j列元素的地址
    ((arr+i)+j) 代表第i行第j列元素的值

    二维数组与指针.png
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    void main() {
        int arr[3][5];
        int i = 0;
        int j = 0;
        int temp = 0;
        for (i = 0; i < 3; i++) {
            for (j = 0; j < 5; j++) {
                arr[i][j] = temp++;
            }
        }
        for (i = 0; i < 3; i++) {
            for (j = 0; j < 5; j++) {
                printf("%d ", arr[i][j]);
            } 
        }
        printf("\n");
        //观察这两行打印,看地址有什么特点,arr+1相对于arr移动了
        //20,这刚好等于二维数组一行的字节数(5*4(一个int4个字节))
        printf("arr %d,arr+1 %d\n",arr,arr+1);
        //&arr+1相对于&arr移动了60个字节,刚好等于一个二维数组
        //的总字节数(5*4*3)
        printf("&arr %d,&arr+1 %d\n",&arr,&arr+1);
    
        //定义一个指向数组的指针变量,指向以int[5]为元素的二维数组
        int(*p)[5];
    
        //指向arr
        p = arr;
    
        //打印,你会发现p[i][j]和arr[i][j]打印结果是一样的,其实二维数组的本质
        //就是一个数组指针,arr和p是等价的
        for (i = 0; i < 3; i++) {
            for (j = 0; j < 5; j++) {
                printf("%d ", p[i][j]);
            }
        }
    
        //(arr+i)代表第i行的地址   二级指针
        //*(arr+i)代表第i行首元素的地址  一级指针  ,第i行地址和第i行首元素地址虽然是相同的
        //但是指针的表示形式不同
        //*(arr+i)+j   == arr[i][j]  代表第i行第j列元素的地址
        //*(*(arr+i)+j) 代表第i行第j列元素的值
        system("pause");
    }
    
    2.数组做函数参数的退化问题

    1.C语言中只会以机械式的值拷贝方式传递参数(实参把值传递给形参)。

    下边两个方法参数不同,但是打印结果是相同的,sizeof(数组)得到的值都是4,为什么?一个char 一个int,这是因为一维数组做函数参数会退化为一个一维指针,所以结果是一样的,解释一下C语言中只会以机械式的值拷贝方式传递参数这句话,当一个数组作为地址传递到一个方法中,实参向形参传递值的时候,并没有在内存中重新创建一个数组,将数组元素一个一个拷贝进去,而是将实参数组的地址传递给了形参,形参也指向了那个数组,这就是值传递,地址值的传递

    原因:c语言的高效性体现在这里,这样做更高效

    int fun(char a[20],size_t b){
        printf("%d  %d",b,sizeof(a));
    }
    
    int fun(int a[20],size_t b){
        printf("%d  %d",b,sizeof(a));
    }
    

    2.二维数组做参数

    //一维数组做参数退化过程
    void fun(int a[5])   ---> void fun(inta[]) ---> void fun(int *a)
    //二维数组做参数退化过程
    void fun(int a[3][5]) ---> void fun(int a[][5]) --->void fun(int (*a)[5])
    
    数组做函数参数的等效性

    一维数组 char a[30] 等价的指针参数 char*a
    指针数组 char *a[30] 等价的指针参数 char *a
    二维数组 char a[20][30] 等价的指针参数 char(
    a)[30]

    4.三种判断字符串到达结尾的方式

    '\0' 数字0 NULL都可以作为判断字符串结尾的方式,原因在于stdlib.h中有这样的定义


    NULL定义.png
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    
    void main() {
        char *a[5] = {
            "helloe",
            "world",
            "\0"
        };
    
        char *b[5] = {
            "today",
            "yesterday",
            0
        };
    
        char *c[5] = {
            "good",
            "bad",
            NULL
        };
    
        for (int i = 0; a[i] != NULL;i++) {
            printf("%s", a[i]);
        }
    
        printf("\n");
        for (int i = 0; b[i] != NULL; i++) {
            printf("%s", b[i]);
        }
        printf("\n");
        for (int i = 0; c[i] != NULL; i++) {
            printf("%s", c[i]);
        }
        system("pause");
    }
    
    
    5.结构体做形参和结构体指针做形参

    看下边的函数,为什么通过结构体赋值失败,而通过结构体指针就成功了,我来画一下内存结构示意图,就明白了

    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    typedef struct Worker {
        char *name;
        int age;
        int id;
    } Worker;
    
    
    void copy(Worker from,Worker to){
        to = from;
    }
    
    void copy2(Worker *from, Worker *to) {
        *to = *from;
    }
    void main() {
        Worker w1 = {"zhangsan",12,1};
    
        Worker w2;
        Worker w3;
        //通过等号,会将结构体中的值拷贝到w2中
        w2 = w1;
        printf("w2.name=%s", w2.name);
    
        //通过函数进行赋值
        //copy(w1, w3);
        //运行直接报错。使用了未初始化的局部变量w3,但是在copy函数中
        //我们已经赋值了
        //printf("w3.name=%s", w3.name);
    
        //换一种方式,通过传递结构体指针,打印成功
        copy2(&w1,&w3);
        system("pause");
    }
    
    

    针对第一个copy函数
    结构体通过等号赋值,做的是内存中值的拷贝,不是地址,这一点要和数组做形参区别开。因为是值拷贝,所以w2赋值给to之后,二者在除了值相同,是没有联系的,to的改变不会影响到w2,所以打印w2失败


    结构体变量做形参.png

    针对第二个copy2函数


    结构体指针做形参.png
    6.结构体做参数
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    typedef struct Worker {
        char *name;
        int age;
        int id;
    } Worker;
    
    void printWorker(Worker *worker, int num) {
        for (int i = 0; i < num; i++) {
            //printf("age:%d\n", (*(worker+i)).age);
            //printf("age:%d\n", (worker + i)->age);
            printf("age:%d\n", worker[i].age);
        }
    }
    
    void sortWorker(Worker *worker, int num) {
        int i, j;
        Worker tmp;
        for (i = 0; i < num; i++) {
            for (j = i + 1; j < num; j++) {
                /*if ((worker + i)->age > (worker + j)->age) {
                    tmp = *(worker + i);
                    *(worker + i) = *(worker + j);
                    *(worker + j) = tmp;
                }*/
                if (worker[i].age >worker[j].age) {
                    tmp = worker[i];
                    worker[i] = worker[j];
                    worker[j] = tmp;
                }
            }
        }
    }
    
    void main() {
        int i = 0, num = 3;
        Worker Array[3];
        for (i = 0; i < num; i++) {
            printf("请输入age:");
            scanf("%d", &(Array[i].age));
        }
        printf("\n排序前\n:");
        printWorker(Array, num);
        sortWorker(Array, num);
        printf("\n排序后\n:");
        printWorker(Array, num);
        system("pause");
    }
    
    
    
    7.结构体二级指针做参数

    注意三个createWorker方法

    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    typedef struct Worker {
        char *name;
        int age;
        int id;
    } Worker;
    
    Worker *createWorker1(int num) {
        Worker *tmp = (Worker*)malloc(sizeof(Worker)*num);
        if (tmp == NULL) {
            return NULL;
        }
        return tmp;
    }
    
    int createWorker2(Worker **worker, int num) {
        Worker *tmp = NULL;
        tmp = (Worker*)malloc(sizeof(Worker)*num);
        if (tmp == NULL) {
            return -1;
        }
        //这样做的效果是让二级指针Worker **worker指向了tmp,并没有改变
        //Worker *pWorker这个指针的指向,可以分析一下,但函数执行时,定义了一个
        //二级指针worker指向了Worker *pWorker,worker = &tmp这样操作改变的只是
        //临时变量worker,对pWorker没有影响,所以后边操作pWorker导致崩溃
        worker = &tmp;
        return 0;
    }
    
    int createWorker3(Worker **worker, int num) {
        Worker *tmp = NULL;
        tmp = (Worker*)malloc(sizeof(Worker)*num);
        if (tmp == NULL) {
            return -1;
        }
        *worker = tmp;
        return 0;
    }
    
    void printWorker(Worker *worker, int num) {
        for (int i = 0; i < num; i++) {
            //printf("age:%d\n", (*(worker+i)).age);
            //printf("age:%d\n", (worker + i)->age);
            printf("age:%d\n", worker[i].age);
        }
    }
    
    void sortWorker(Worker *worker, int num) {
        int i, j;
        Worker tmp;
        for (i = 0; i < num; i++) {
            for (j = i + 1; j < num; j++) {
                /*if ((worker + i)->age > (worker + j)->age) {
                tmp = *(worker + i);
                *(worker + i) = *(worker + j);
                *(worker + j) = tmp;
                }*/
                if (worker[i].age >worker[j].age) {
                    tmp = worker[i];
                    worker[i] = worker[j];
                    worker[j] = tmp;
                }
            }
        }
    }
    void main() {
        int i = 0, num = 3;
        Worker *pWorker = NULL;
        //在堆内存中申请空间返回内存地址
        //pWorker = createWorker1(num);
        //createWorker2(&pWorker, num);
        createWorker3(&pWorker, num);
        for (i = 0; i < num; i++) {
            printf("请输入age:");
            scanf("%d", &(pWorker[i].age));
        }
        printf("\n排序前\n:");
        printWorker(pWorker, num);
        sortWorker(pWorker, num);
        printf("\n排序后\n:");
        printWorker(pWorker, num);
        system("pause");
    }
    
    
    
    8.练习,将前两个内存中的元素拷贝到一个新的二级指针指向的空间,并排序
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    /*
    把第一种内存模型第二种内存模型结果copy到第三种内存模型中,并排序打印
    */
    int sort(char **myp1, int num1, char(*myp2)[30], int num2, char ***myp3, int *num3) {
        char **p3 = NULL;
        int i = 0, j = 0, k = 0;
        int tmplen = 0;
        char *tmpP = NULL;
        //p3指向的内存空间用于接收指向两个数组的值,所以他的大小应该是前两个空间的和
        p3 = (char **)malloc((num1 + num2) * sizeof(char*));
        if (p3 == NULL) {
            return -1;
        }
    
        //将第一个数组的值拷贝到申请的空间中
        for (i = 0; i < num1; i++) {
            //得到myp1指向的第i个元素的长度(+1是结束符空间)
            tmplen = strlen(myp1[i]) + 1;
            //开辟等长的空间
            p3[i] = (char *)malloc(tmplen * sizeof(char));
            //拷贝到开辟的空间中
            strcpy(p3[i], myp1[i]);
        }
        printf("i = %d\n", i);
        //同样道理拷贝myp2
        for (j = 0; j < num2; j++,i++){
            tmplen = strlen(myp2[j]) + 1;
            p3[i] = (char *)malloc(tmplen * sizeof(char));
            if (p3[i] == NULL) {
                return -3;
            }
            strcpy(p3[i], myp2[j]);
        }
    
        //排序
        tmplen = num1 + num2;
        //*num3 = num1+num2;
        for (i = 0; i < tmplen; i++) {
            for (j = i + 1; j < tmplen; j++) {
                if (strcmp(p3[i], p3[j]) > 0) {
                    tmpP = p3[i];
                    p3[i] = p3[j];
                    p3[j] = tmpP;
                }
            }
        }
    
        //间接赋值
        *num3 = tmplen;
        *myp3 = p3;
        return 0;
    }
    
    void sortFree1(char **p, int len) {
        int i = 0;
        if (p == NULL) {
            return;
        }
        for (i = 0; i < len; i++) {
            free(p[i]);
        }
        free(p);
    }
    
    /*
        把二级指针指向的二维内存释放掉,同时间接修改了实参的值
    */
    void sortFree2(char ***myp,int len) {
        int i = 0;
        char **p = NULL;
        if (myp == NULL) {
            return;
        }
        //还原成二级指针
        p = *myp;
        if (p == NULL) {
            return;
        }
        for (i = 0; i < len; i++) {
            free(p[i]);
        }
        //间接赋值是指针存在的最大意义
        *myp = NULL;
    }
    void main() {
        int ret = 0;
        char *p1[] = {"aaaaaaa","cccccc","bbbbbb"};
        char buf2[10][30] = {"11111","333333","222222"};
        char **p3 = NULL;
    
        int len1, len2, len3=0, i = 0;
        len2 = 3;
        len1 = sizeof(p1) / sizeof(*p1);
        printf("len1=%d", len1);
        ret = sort(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]);
        }
    
        system("pause");
    }
    
    
    

    相关文章

      网友评论

        本文标题:12.C语言提高(二)

        本文链接:https://www.haomeiwen.com/subject/zdgdpftx.html