11.C语言提高(一)

作者: 任振铭 | 来源:发表于2018-06-18 17:29 被阅读12次
    1.排序

    形参中的数组,编译器会把他当作指针处理
    形参写在函数上,和写在函数内是一样的,只不过是
    写在函数上具有对外的属性

    2.关于数组地址

    看下面函数的运行结果,你会发现,b+1和&b+1的区别,b表示的是数组元素首地址,所以b+1就是将从首元素向后移动一个元素后得到的第二个元素的地址,但是&b+1的结果优点出乎意料,它相对于首元素地址向后移动了40个位置,而40刚好是数组b的长度,怎么解释呢,其实就是&b表示的是整个数组的地址,把整个数组当作一个整体,那么加一就是移动这样一个整体后的地址了

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    void main() {
        int b[10];
        printf("b:%d,b+1:%d,&b+1:%d", b, b + 1, &b + 1);
        system("pause");
    }
    打印结果:
    b:19922588,b+1:19922592,&b+1:19922628请按任意键继续. . .
    
    

    1)数组首元素的地址和数组地址是两个不同的概念
    2)数组名代表数组首元素地址,他是个常量
    解释如下:变量本质是内存空间的别名,而数组在定义时就会分配内存,内存就固定了,所以数组名起名以后就不能更改了
    3)数组首元素的地址和数组值相等
    C语言规定:
    int a[10]
    &a表示整个数组的地址,a表示数组首元素的地址,区别体现在地址+1的结果

    如何定义一个数组数据类型
    int a;//定义了一个int类型a
    typedef int (MyArrayType)[5];//定义一个数组数据类型MyArrayType,
    MyArrayType myArray;    //相当于int myArray[5]
    myArray[0] = 0;
    ...
    myArray[4]=4;
    
    定义数组指针的第一种方法
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    void main() {
        char *MyArray[] = {"1111","2222","3333"};
        //数组指针,一个指针,指向了数组
    
        //定义了一个数组数据类型
        typedef int(MyArrayType)[5];
    
        int i = 0;
    
        //用类型定义变量,相当于int myArray[5]
        MyArrayType myArray;
    
        //定义一个指针变量,这个变量指向数组
        MyArrayType *pArray;
    
        //定义一个数组,相当于一级指针
        int myArray2[5];
    
        //数组指针指向这个数组,相当于二级指针
        pArray = &myArray2;
    
        for (i = 0; i < 5; i++) {
            (*pArray)[i] = i + 1;
        }
    
        for (i = 0; i < 5; i++) {
            printf("%d", (*pArray)[i]);
        }
        system("pause");
    }
    
    定义数组指针的第二种方法
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    void main() {
        //定义声明一个数组指针类型
        typedef int(*PArrayType)[5];
        //告诉编译器给我分配一个指针变量
        PArrayType pArray;
    
        //定义一个数组,相当于一级指针
        int myArray2[5];
    
        //数组指针指向这个数组,相当于二级指针
        pArray = &myArray2;
        
    
        int i = 0;
    
        for (i = 0; i < 5; i++) {
            (*pArray)[i] = i + 1;
        }
    
        for (i = 0; i < 5; i++) {
            printf("%d", (*pArray)[i]);
        }
        system("pause");
    }
    
    定义数组指针的第三种方法
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    void main() {
        //直接定义一个指向数组的数组指针变量
        int(*PArrayType)[5];
    
        //定义一个数组,相当于一级指针
        int myArray2[5];
    
        //数组指针指向这个数组,相当于二级指针
        PArrayType = &myArray2;
        
    
        int i = 0;
    
        for (i = 0; i < 5; i++) {
            (*PArrayType)[i] = i + 1;
        }
    
        for (i = 0; i < 5; i++) {
            printf("%d", (*PArrayType)[i]);
        }
        system("pause");
    }
    
    3.内存四区的建立流程
    内存四驱的建立流程.png
    4.写一段程序证明栈的生长方向(开口向上还是开口向下)

    在栈内存中定义两个变量ab一个数组buf,比较他们的地址。如图所示,如果栈的开口向上,那么先入栈的变量地址会小于后入栈的变量地址,如果开口向下则反之,所以可以根据这个特性来判断,我们打印了ab的地址,结果是a地址大于b,那么可以确定当前环境下,栈开口是向下的,可以默认栈的开口是向下的,这种情况比较多。
    同时可以看到我们定义了一个数组,可以看到,虽然栈开口向下,但是数组中元素的存储方式并没有像栈中元素ab一样,你在栈开口向上的情况下试一下会发现,栈的开口不影响数组元素的存储形式,数组存储永远是图中所示的样子


    栈属性.png
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    void main() {
        int a;
        int b;
        printf("&a:%d,&b:%d\n", &a, &b);
        char buf[2] = {'a','b'};
        printf("&buf[0]:%d,&buf[1]:%d\n", &buf[0], &buf[1]);
        system("pause");
    }
    打印结果
    &a:8387696,&b:8387684
    &buf[0]:8387672,&buf[1]:8387673
    请按任意键继续. . .
    
    5.strcpy函数的5中推演方式
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    /*
    移动from和to指针,当from指针到达末尾的时候跳出
    */
    void copy1(char *from, char *to) {
        for (; *from != '\0'; from++, to++) {
            *to = *from;
        }
        //在字符串的末尾加上\0,因为拷贝的时候跳过了\0
        *to = '\0';
        return;
    }
    
    /*
        把自增运算放在了循环中,++优先级大于*
        但是因为++是放在后边的,所以整体执行顺序还是先
        将*from赋值给*to,然后from和to做自增运算
    */
    void copy2(char *from, char *to) {
        for (; *from != '\0';) {
            *to++ = *from++;
        }
        *to = '\0';
        return;
    }
    
    /*
        这种写法可以减少手动在末尾添加0的操作,
        (*to = *from) != '\0'这行代码做了两部操作
        第一先赋值,第二判断是否是0,所以当到达末尾
        得的时候,0也被拷贝了
    */
    void copy3(char *from, char *to) {
        while ((*to = *from) != '\0') {
            from++;
            to++;
        }
    }
    
    /*
       这个操作更加简化,所以操作写在了判断条件中
    */
    void copy4(char *from, char *to) {
        while ((*to++ = *from++) != '\0') {
            
        }
    }
    
    /*
    最简版
    */
    void copy5(char *from, char *to) {
        while (*to++ = *from++) {
    
        }
    }
    void main() {
        char *from = "hello world";
        char to[100];
    
        copy5(from, to);
    
        //C语言打印数据会将数组元素打印出来
        printf("to:%s\n", to);
        system("pause");
    }
    
    6.字符串反转的两种方式
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    //两头堵模型实现字符串反转
    void inverse1(char *str) {
        int length;
        char *start;
        char *end;
        length = strlen(str);
        start = str;
        end = str + length - 1;
    
        while (start < end) {
            char c = *start;
            *start = *end;
            *end = c;
            ++start;
            --end;
         }
    }
    
    /*
    递归调用实现字符串反转,这一点利用了栈的结构特点,先入后出
    不断的将字符串中每一个字符入栈,最后打印每一个字符,由后往前
    */
    void inverse2(char *str) {
        if (str == NULL) return;
    
        //到达字符串末尾,结束递归
        if (*str == '\0') return;
    
        inverse2(str + 1);
        printf("%c", *str);
    
    }
    /*
    将反转后的字符串保存到全局变量中
    */
    
    //全局变量被修改,多线程操作中会涉及到线程安全的问题
    char buf[100];
    void inverse3(char *str) {
        if (str == NULL) return;
    
        //到达字符串末尾,结束递归
        if (*str == '\0') return;
    
        inverse3(str + 1);
        strncat(buf, str,1);
    }
    void main() {
        char a[] = "abcdefg";
        //inverse1(a);
        //printf("%s\n", a);
        //inverse2(a);
    
        memset(buf, 0, sizeof(buf));
        inverse3(a);
        printf("buf = %s\n", buf);
        system("pause");
    }
    
    7.const深入理解
    int main(){
      const int a;
      int const b;
      const char *c;
      char * const d;
      const char * const e;
    }
    
    

    1.const修饰常量,前两种情况是相同的,表示常量ab都不能被修改
    2.const修饰指针,第三种是一个指向常整型的指针,他所指向的内存数据不能被修改,但是他本身可以被修改,也就是所,可以修改他指向的地址
    3.const修饰指针,第四种是一个常量指针,指针变量不能被修改,但是他指向的内存空间可以被修改,不能改变指向,但是值可变
    4.const修饰指针,第五种是一个指向常整型的常量指针,指针和他指向的内存空间都不能被修改

    8.二级指针做函数参数的作用

    通过二级指针做参数的好处是,可以用于修改原一级指针所指向的内存,同时也可以修改原一级指针的指向。那么直接用一级指针做参数进行传递就做不到这两点了吗,答案是,可以做到其中以点,就是操作原一级指针指向的内存,但是无法对原一级指针进行操作

    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    /*
        二级指针做函数参数,用于修改传入一级指针的指向
    */
    int getMem(char **a1, int *myLen1, char ** a2, int *myLen2) {
        char *temp1 = NULL;
        char *temp2 = NULL;
        temp1 = (char *)malloc(100);
        if (temp1 == NULL) {
            return -1;
        }
        strcpy(temp1, "hello");
    
        //让传入的二级指针指向的指针指向temp指向的内存空间
        //这行代码的意思:*a1表示p1,让p1指向了temp1指向的空间
        *a1 = temp1;
    
        temp2 = (char *)malloc(100);
        if (temp2 == NULL) {
            return -1;
        }
        strcpy(temp2, " world");
        *a2 = temp2;
    
    }
    /*
     通过二级指针释放一级指针指向的内存
    */
    void freeMem(char** temp1, char **temp2) {
        if (temp1 == NULL || temp2 == NULL)
        {
            return;
        }
        free(*temp1);
        *temp1 = NULL;
        free(*temp2);
        *temp2 = NULL;
    }
    
    /*
        如果传入一级指针释放内存会怎样?
    */
    void freeMem(char* temp1, char *temp2) {
        if (temp1 == NULL || temp2 == NULL)
        {
            return;
        }
        free(temp1);
    
        //这一步只是将局部变量temp1设置为null,所以并不影响main函数
        //中p1这个变量,所以传递一级指针,无法操作原一级指针变量
        temp1 = NULL;
        free(temp2);
    
        //这一步只是将局部变量temp2设置为null,所以并不影响main函数
        //中p2这个变量,所以传递一级指针,无法操作原一级指针变量
        temp2 = NULL;
    }
    void main() {
        char *p1 = NULL;
        int len1 = 0;
        char *p2 = NULL;
        int len2 = 0;
        int ret;
        ret = getMem(&p1, &len1, &p2, &len2);
        printf("%s\n", p1);
        printf("%s\n", p2);
        freeMem(&p1, &p2);
        system("pause");
    }
    
    9.二级指针做输入,指向指针数组
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    void printArr(char** temp, int num) {
        int i = 0;
        for (; i < num; i++) {
            //注意,这里*(temp) = *(temp+0) ,二级指针指向
            //指针数组,*(temp+i)表示的是原数组arr中的第i
            //个元素的地址,而我们知道,C语言直接打印数组
            //元素的地址,就会将数组元素打印出来
            printf(" *(temp + %d)  = %s\n", i, *(temp + i));
    
        }
    }
    
    void bubbleSort(char **temp, int num) {
    
        int i = 0, j = 0;
        for (int i = 0; i<num - 1; i++) {//外层循环控制排序趟数
            for (int j = 0; j<num - 1 - i; j++) {//内层循环控制每一趟排序多少次
                
                if (strcmp(*(temp + j),*(temp + j + 1))>0)
                {
                    char *smallest = *(temp + j);
                    *(temp + j) = *(temp + j + 1);
                    *(temp + j + 1) = smallest;
                }
            }
        }
    
    }
    
    void main() {
        //这是一个指针数组,存放的是是每个元素的地址
        char *arr[] = {"aaaaa","ccccc","ddddd","bbbbb"};
        int num = sizeof(arr) / sizeof(arr[0]);
    
        printf("数组的长度是:%d\n", num);
    
        printf("排序之前\n");
    
    
        printArr(arr, num);
    
        bubbleSort(arr, num);
        printf("排序之后\n");
        printArr(arr, num);
        system("pause");
    }
    
    二级指针做输入第一种形式.png

    顺便复习一下冒泡排序

    原理:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。重复第一趟步骤,直至全部排序完成。

    举例说明:要排序数组:int[] arr={6,3,8,2,9,1};

    第一趟排序:
        第一次排序:6和3比较,6大于3,交换位置: 3 6 8 2 9 1
        第二次排序:6和8比较,6小于8,不交换位置:3 6 8 2 9 1
        第三次排序:8和2比较,8大于2,交换位置: 3 6 2 8 9 1
        第四次排序:8和9比较,8小于9,不交换位置:3 6 2 8 9 1
        第五次排序:9和1比较:9大于1,交换位置: 3 6 2 8 1 9
        第一趟总共进行了5次比较, 排序结果: 3 6 2 8 1 9


    第二趟排序:
        第一次排序:3和6比较,3小于6,不交换位置:3 6 2 8 1 9
        第二次排序:6和2比较,6大于2,交换位置: 3 2 6 8 1 9
        第三次排序:6和8比较,6大于8,不交换位置:3 2 6 8 1 9
        第四次排序:8和1比较,8大于1,交换位置: 3 2 6 1 8 9
        第二趟总共进行了4次比较, 排序结果: 3 2 6 1 8 9


    第三趟排序:
        第一次排序:3和2比较,3大于2,交换位置: 2 3 6 1 8 9
        第二次排序:3和6比较,3小于6,不交换位置:2 3 6 1 8 9
        第三次排序:6和1比较,6大于1,交换位置: 2 3 1 6 8 9
        第二趟总共进行了3次比较, 排序结果: 2 3 1 6 8 9


    第四趟排序:
        第一次排序:2和3比较,2小于3,不交换位置:2 3 1 6 8 9
        第二次排序:3和1比较,3大于1,交换位置: 2 1 3 6 8 9
        第二趟总共进行了2次比较, 排序结果: 2 1 3 6 8 9


    第五趟排序:
        第一次排序:2和1比较,2大于1,交换位置: 1 2 3 6 8 9
        第二趟总共进行了1次比较, 排序结果: 1 2 3 6 8 9


    最终结果:1 2 3 6 8 9

    由此可见:N个数字要排序完成,总共进行N-1趟排序,每i趟的排序次数为(N-i)次,用双重循环语句,外层控制循环多少趟,内层控制每一趟的循环次数

    10.二级指针做输入,第二种形式
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    void main() {
        int i = 0;
        char **p2 = NULL;
        int num = 5;
    
        //在堆空间中开辟内存空间,大小是sizeof(char*)*num,
        //用于存储5个char*类型的指针变量
        p2 = (char **)malloc(sizeof(char*)*num);
        //开辟空间之后给每个元素赋值
        for (i = 0; i < num; i++) {
            //给每个char*类型的指针变量开辟指向的内存空间,这个空间
            //用于存放char类型的数据,最大可存100个char
            *(p2+i) = (char*)malloc(sizeof(char) * 100);
    
            //赋值
            sprintf(*(p2 + i), "存入我的数据%d,", i + 1);
        }
        for (i = 0; i < num; i++) {
            printf("%s\n", *(p2 + i));
        }
    
    
        //释放
        for (i = 0; i < num; i++) {
            if (*(p2 + i) != NULL) {
                free(*(p2 + i));
                *(p2 + i) = NULL;
            }
        }
        if (p2 != NULL)
        {
            free(p2);
            p2 = NULL;
        }
        system("pause");
    }
    void main2() {
        int i = 0;
        char **p2 = NULL;
        int num = 5;
    
        //在堆空间中开辟内存空间,大小是sizeof(char*)*num,
        //用于存储5个char*类型的指针变量
        p2 = (char **)malloc(sizeof(char*)*num);
        //开辟空间之后给每个元素赋值
        for (i = 0; i < num; i++) {
            //给每个char*类型的指针变量开辟指向的内存空间,这个空间
            //用于存放char类型的数据,最大可存100个char
            p2[i] = (char*)malloc(sizeof(char) * 100);
    
            //赋值
            sprintf(p2[i], "存入我的数据%d,", i + 1);
        }
        for (i = 0; i < num; i++) {
            printf("%s\n", p2[i]);
        }
    
    
        //释放
        for (i = 0; i < num; i++) {
            if(p2[i] != NULL) {
                free(p2[i]);
                p2[i] = NULL;
            }
        }
        if (p2 != NULL)
        {
            free(p2);
            p2 = NULL;
        }
        system("pause");
    }
    
    二级指针第二种形式.png
    11.二级指针操作字符串练习

    切割字符串并存储在堆内存中,然后打印切割结果
    char *p1 = "abcdef,acccd,eeee,aaaa,e3eeeee,sssss,ssccccccc,";

    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    int splitString(const char *buf1, char c, char** myp, int *count) {
        char *p = NULL, *ptmp = NULL;
        int tmpcount = 0;
    
        //两个指针都指向了字符串指针
        p = buf1;
        ptmp = buf1;
    
        do {
            //从p中找char c
            p = strchr(p, c);
            if (p != NULL) {
                //大于0说明指针已经有移动位置,新的字符串和旧的不是同一个,
                //如果字符串第一个字符就找到了符合条件的,那么p-pmt = 0
                if (p - ptmp > 0) {
                    //找到一个逗号后,将逗号之前的字符串拷贝到二级指针指向的空间中
                    strncpy(*(myp + tmpcount), ptmp, p - ptmp);
                    //在截出来的字符串后边添加字符串结束符\0
                    *(*(myp + tmpcount) + (p - ptmp)) = '\0';
                    tmpcount++;
                    ptmp = p = p + 1;
                    /*strncpy(myp[tmpcount], ptmp, p - ptmp);
                    myp[tmpcount][p - ptmp] = '\0';
                    tmpcount++;
                    ptmp = p = p + 1;*/
                }
            }
            else {
                break;
            }
        } while (*p != '\0');
        *count = tmpcount;
        return *count;
    }
    void main() {
        int ret = 0, i = 0;
        char *p1 = "abcdef,acccd,eeee,aaaa,e3eeeee,sssss,ssccccccc,";
        char cTemp = ',';
        int count;
    
        //开辟空间
        char **p = NULL;
        p = (char **)malloc(10 * sizeof(char *));
        if (p == NULL) {
            return;
        }
        for (i = 0; i < 10; i++) {
            *(p + i) = (char *)malloc(sizeof(char)*30);
            //p[i] = (char *)malloc(sizeof(char) * 30);
        }
    
        ret = splitString(p1, cTemp, p, &count);
    
        printf("得到了%d个元素\n", count);
        if (ret == 0)
        {
            printf("error\n");
        }
    
        for (i = 0; i < count; i++) {
            printf("%s\n", *(p + i));
        }
    
    
        //回收内存
        for (i = 0; i < 10; i++) {
            if (*(p + i) != NULL) {
                free(*(p + i));
                *(p + i) = NULL;
            }
        }
        if (p != NULL)
        {
            free(p);
            p = NULL;
        }
    
        //释放
        /*for (i = 0; i < num; i++) {
            if (p2[i] != NULL) {
                free(p2[i]);
                p2[i] = NULL;
            }
        }
        if (p2 != NULL)
        {
            free(p2);
            p2 = NULL;
        }*/
        system("pause");
    }
    
    12.定义数组指针的三种方式

    1)通过数组类型定义数组指针
    typedef int(ArrayType)[5];
    ArrarType *pointer;

    1. 声明一个数组指针类型
      typedef int (*MyPointer)[5];
      MyPointer myPoint;
    2. 直接定义
      int(*pointer)[n];
      ponter为数组指针变量名
      type (int)为指向的数组的类型
      n 为指向的数组的大小
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    void main() {
        //直接定义一个指向数组的数组指针变量
        int(*PArrayType)[5];
    
        //定义一个数组,相当于一级指针
        int myArray2[5];
    
        //数组指针指向这个数组,相当于二级指针
        PArrayType = &myArray2;
        
    
        int i = 0;
    
        for (i = 0; i < 5; i++) {
            (*PArrayType)[i] = i + 1;
        }
    
        for (i = 0; i < 5; i++) {
            printf("%d", (*PArrayType)[i]);
        }
        system("pause");
    }
    void main2() {
        char *MyArray[] = {"1111","2222","3333"};
        //数组指针,一个指针,指向了数组
    
        //定义了一个数组数据类型
        typedef int(MyArrayType)[5];
    
        int i = 0;
    
        //用类型定义变量,相当于int myArray[5]
        MyArrayType myArray;
    
        //定义一个指针变量,这个变量指向数组
        MyArrayType *pArray;
    
        //定义一个数组,相当于一级指针
        int myArray2[5];
    
        //数组指针指向这个数组,相当于二级指针
        pArray = &myArray2;
    
        for (i = 0; i < 5; i++) {
            (*pArray)[i] = i + 1;
        }
    
        for (i = 0; i < 5; i++) {
            printf("%d", (*pArray)[i]);
        }
        system("pause");
    }
    
    

    相关文章

      网友评论

        本文标题:11.C语言提高(一)

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