• C语言学习(9)


    一、二级指针(多级指针)

    1. 定义: 类型名 **指针的名字

      比如: int **p; //定义了int类型的二级指针

      注意:你定义的二级指针,它的类型必须跟一级指针类型一致

      作用:用来指向另外一个一级指针在内存当中的地址(指向指针的指针)

      总结:二级指针在使用的时候无非就两种情况

      **q

      *q 二级指针解引用一次和两次分别代表什么??这个搞清楚代码就不会写错

    #include <stdio.h>
    
    int main()
    {
        int a = 888;
        int *p = &a;  //定义一级指针p
        int **q = &p; //定义二级指针q,指向一级指针p的地址
    
        //通过二级指针去访问a
        //把二级指针解引用两次 **q--》**&p --》*p --》*&a--》a
        //把二级指针解引用一次 *q--》*&p--》p
        printf("通过二级指针q访问a: %d
    ", **q); //q解引用两次即可
        printf("通过二级指针q访问a: %d
    ", **&p);
        printf("二级指针q存放的是指针p的首地址:%p
    ", &p);
        printf("二级指针q存放的是指针p的首地址:%p
    ", q);
    }
    #include <stdio.h>
    #include <string.h>
    int main()
    {
        int i;
        char buf[10] = "hello";
        char *p = buf;
        char **q = &p; // 定义二级指针q
    
        //通过二级指针访问修改buf中的内容
        //**q --》**&p--》*p --》*&buf[0] --》buf[0]
        //*q --》p --》buf--》&buf[0]
        printf("通过二级指针q访问buf完整的字符串 %s
    ", *q);
        for (i = 0; i < strlen(buf); i++)
            printf("通过二级指针q访问buf中的单个字符 %c
    ", *(*q + i));
    }

    练习:

      1. char buf[3][10]={"hello","hehe","haha"};

      char *p=buf[0];

      char **q=&p;

      //通过q访问二维数组中的三个字符串 %s

      //通过q访问二维数组中的单个字符%c

    #include <stdio.h>
    #include <string.h>
    /*
        二级指针使用的套路:
            通过等价替换--》把二级指针解引用替换成你熟悉的一级指针
    */
    int main()
    {
        int i, j;
        char buf[3][10] = {"hello", "hehe", "haha"};
        /*    char *p=buf[0];
        char **q=&p;
         //访问二维数组中的字符
        for(i=0; i<30; i++)
            printf("二级指针q访问数组:%c
    ",*(*q+i)); 
        
        //访问二维数组中的字符串--》找到字符串的起始地址即可
        for(i=0; i<30; i+=10)
            printf("三个字符串分别是:%s
    ",*q+i); //等价于p+i */
    
        //扩展思考:
        char(*p)[10] = &buf[0]; //数组指针
        //二级指针的类型要跟一级指针类型一致
        char(**q)[10] = &p; //p的类型是数组指针
        //通过二级指针q去遍历访问二维数组的字符
        // *q相当于p
        for (i = 0; i < 3; i++)
        {
            for (j = 0; j < 10; j++)
                printf("(*q)[%d][%d] is:%c
    ",i,j, (*q)[i][j]);
        }
    
        //通过二级指针q去遍历字符串
        for (i = 0; i < 3; i++)
            printf("%s
    ", *(*q + i));
    }

    二、C语言中的函数

    1. 函数的概念和作用:

      数学当中:函数就是别人提供给你公式方法 sin() cos()

      C语言中:为了提高代码的复用性,把一些功能封装成函数供开发人员使用

      printf() --》函数 strcmp() strcpy()

    2. 函数的定义

      函数的定义都是写在main函数的外面

      返回值类型 函数名(形参列表) //函数头

      {

        函数体;

      }

      返回值: 告诉函数的调用者,我这个函数执行完毕之后的结果

      函数名: 变量名规则一样,通俗易懂,不能跟C语言的关键字以及C语言的函数名字冲突

      形参列表:告诉程序员,在使用这个函数的使用需要传递多少个实参,每个实参分别是什么类型

      实参(实际参数):函数调用的使用,传递的参数就是实参

      形参(形式参数):函数定义的时候出现在圆括号里面的都是形参

      函数的调用

      语法格式: 函数名(实参列表) //实参的个数,类型必须跟形参保存一致

    #include <stdio.h>
    //把比较两个整数大小的代码--》封装成函数
    //函数如何封装--》程序员说了算,封装的函数用起来很方便--》成功
    //目的:为了提高代码的重复使用效率,任何其他代码将来都能使用
    
    int numcmp(int n1, int n2) //形式参数
    {
        if (n1 > n2)
            return 1;
        else if (n1 < n2)
            return -1;
        else
            return 0; //通过返回值判断大小
    }
    
    void othernumcmp(int n1, int n2) //没有返回值
    {
        if (n1 > n2)
            printf("n1>n2");
        else if (n1 < n2)
            printf("n1<n2");
        else
            printf("n1==n2");
    }
    int main()
    {
        int a;
        int b;
        printf("请输入两个整数!
    ");
        scanf("%d%d", &a, &b);
    
        //调用函数
        //实参赋值给形参
        int ret = numcmp(a, b); //编译器翻译这句话 n1=a  n2=b
        if (ret > 0)
            printf("a>b
    ");
        else if (ret < 0)
            printf("a<b
    ");
        else
            printf("a==b
    ");
    }

    3. 函数定义需要注意的问题

      第一个: 函数的定义要么写在main函数的前面,要么写在main函数的后面(注意提前声明)

      第二个:实参和形参的关系一定要搞清楚

    #include <stdio.h>
    //numcmp定义在main的后面
    int numcmp(int a, int b); //声明了函数头,形式参数
    
    //fun定义在main的前面
    int fun(int c, int d) //故意迷惑你,把形参的名字取得跟实参一模一样
    {
        c += 18;
        d += 10;
        return 0;
    }
    int main() //C语言代码都是从main函数开始往下执行
    {
        int a;
        int b;
        int c = 6;
        int d = 12;
        printf("请输入两个整数!
    ");
        scanf("%d%d", &a, &b);
    
        //调用函数fun
        fun(c, d);
        printf("c is:%d
    ", c);
        printf("d is:%d
    ", d);
    
        //调用函数numcmp
        //实参赋值给形参
        int ret = numcmp(a, b); //编译器翻译这句话 n1=a  n2=b
        if (ret > 0)
            printf("a>b
    ");
        else if (ret < 0)
            printf("a<b
    ");
        else
            printf("a==b
    ");
    }
    
    int numcmp(int a, int b)
    {
        if (a > b)
            return 1;
        else if (a < b)
            return -1;
        else
            return 0; //通过返回值判断大小
    }

      C语言中函数调用传递参数只有两种写法: 传值和传地址

      区分传值和传地址: 函数的形参是普通变量 --》传值

      函数的形参是指针--》传地址

      传值:实参把自己的值拷贝一份给形参

      传地址:实参把自己的地址赋值给形参指针

    #include <stdio.h>
    //经典例子--》定义一个函数交换两个整数的值
    void swapnum(int n1, int n2) //实参传值给形参
    //实参a,b和形参n1,n2是四个独立的变量,a,b只是把自己的值拷贝它们
    {
        printf("形参n1 n2地址是:%p
    ", &n1);
        printf("形参n1 n2地址是:%p
    ", &n2);
        int temp;
        temp = n1;
        n1 = n2;
        n2 = temp; //跟实参a,b没有任何关系
        printf("函数中n1  n2: %d  %d
    ", n1, n2);
    }
    
    void otherswap(int *p1, int *p2) //实参传地址给形参
    {
        printf("形参p1存放的地址:%p
    ", p1);
        printf("形参p2存放的地址:%p
    ", p2);
        int temp;
        temp = *p1;
        *p1 = *p2;
        *p2 = temp; //把p1和p2两个指针中的内容交换
    }
    
    int main()
    {
        int a = 89;
        int b = 63;
        printf("实参a的地址:%p
    ", &a);
        printf("实参b的地址:%p
    ", &b);
        printf("交换之前: %d   %d 
    ", a, b);
        //调用函数swapnum
        swapnum(a, b); //编译器翻译:  n1=a  n2=b
    
        //调用otherswap
        // otherswap(&a,&b); //编译器翻译:p1=&a  p2=&b
        printf("交换之后: %d   %d 
    ", a, b);
    }

      第三个:数组作为实参和形参

      数组作为实参: 传递的地址(指针)

      数组作为形参: 要么写成指针的形式,或者写成 数组+n(n表示元素个数)

      第四个:局部变量和全局变量--》作用域

      全局变量: 定义在main函数外面以及其他函数的外面

      全局变量在定义的.c文件中任何函数都能使用,并且其他.c文件也能使用

      全局变量是所有函数共享的,作用域属于整个文件

      局部变量: 定义在某个函数的花括号里面的,作用域仅限于函数内部

      作用域(作用区域):指的是这个变量的使用范围

      需要注意的问题:

      第一个: 全局变量跟局部变量同名,优先使用自己定义的局部变量(全局

      变量不起作用了)

    #include <stdio.h>
    int num = 100; //全局变量
    
    int fun(int a) //形参a也是局部变量
    {
        //由于fun函数中没有单独定义num,使用的是全局变量num
        num += 5; //使用的全局变量
        printf("num在fun函数中被修改了%d
    ", num);
        printf("a is:%d
    ", a);
    }
    
    int main()
    {
        printf("main函数中使用num %d
    ", num); //全局变量num
        int num = 888;                         //局部变量如果跟全局变量同名,会覆盖掉同名的全局变量
        fun(num);                              //num用的是哪个num
        printf("调用fun之后,main函数中使用num %d
    ", num);
    }

    练习:

      封装一个函数,把用户从键盘输入的任意一个字符串中的非英文字符去除

    #include <stdio.h>
    #include <string.h>
    
    /*
        函数名的不同风格:
            getnum()   
            get_num()  //ubuntu系统中采用这种风格
            getNum() //C++  JAVA  驼峰命名
    */
    //封装函数实现去除字符串中的非英文字符
    void rmchar(char *buf) //char *buf=buf实参传递地址给形参
    {
        int i, j;
        for (i = 0; i < strlen(buf); i++)
        {
            if (buf[i] < 'a' || buf[i] > 'z')
            {
                for (j = i; j < strlen(buf); j++)
                    buf[j] = buf[j + 1];
                i--;
            }
        }
    }
    //这种封装通用性比较差
    //void otherchar(char otherbuf[20])  //要求你传递char [20]类型的数组
    //通用性好一些,n表示数组元素个数
    void otherchar(char otherbuf[], int n)
    {
        int i, j;
        for (i = 0; i < strlen(otherbuf); i++)
        {
            if (otherbuf[i] < 'a' || otherbuf[i] > 'z')
            {
                for (j = i; j < strlen(otherbuf); j++)
                    otherbuf[j] = otherbuf[j + 1];
                i--;
            }
        }
    }
    
    int main()
    {
        char buf[16];
        scanf("%s", buf);
        //rmchar(buf); //编译器翻译:char *buf=buf
        otherchar(buf, 16);
        printf("去除非英文字符之后:%s
    ", buf);
    }

    作业:

      第一题: 明确指出每个num是全局还是局部,值是多少,不要编译运行

    #include <stdio.h>
    
    int num = 100; //全局变量
    
    int fun(int num) //任何函数的形参都是局部变量,跟全局变量同名
    
    {
    
        num += 5; //fun自己的形参num 87
    
        printf("num在fun函数中被修改了%d
    ", num); //fun自己的形参num 87
    
        return num; //fun自己的形参num 87
    }
    
    int main()
    
    {
    
        //到目前为止main没有定义自己的num
    
        num -= 18; //全局变量num==82
    
        fun(num); //函数调用
    
        printf("main函数中使用num %d
    ", num); // 全局变量num==82
    
        int num = 888; //main自己局部变量
    
            fun(num); //main自己的num
    
        printf("调用fun之后,main函数中使用num %d
    ", num); //main自己的num
    }

      第二题:请问程序执行结果是多少??

    void foo(int b[][3]);
    
    void main()
    {
        int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};
        foo(a);
        printf("%d",a[2][1]);
    }
    void foo(int b[][3])
    {
        ++b;
        b[1][1]=9;
    }

      难点:知识点 数组作为形参--》理解为指针,传地址

      指针的运算,写法 ++b很好理解 b[1][1]的理解

    结果为:9
    因为foo(a)//a为数组名,int(*)[3]的指针;
    将首地址赋值给b,进入函数后,立马执行++;
    此时b的指引的地址+1了,指向了a的第2位数组的首地址
    即{4,5,6}这个数组
    然后b[1][1]=9;则表示数组{7,8,9}中的8替换成了9;
    回到主函数,打印的是a[2][1]即是被替换后的那个值。
    最后结果为:9
    View Code

      第三题: 封装一个函数 int fun(int n) 该函数可以把1到n之间(1<= 数字 <=n)所有能被6整除的数,或者数字中包含6的全部找到并打印出来

    #include <stdio.h>
    /*
    *封装一个函数  int  fun(int  n)   
    *该函数可以把1到n之间(1<= 数字 <=n)所有能被6整除的数,
    *或者数字中包含6的全部找到并打印出来  
    */
    int fun(int n) //能被6整除,或数字包含6的数打印出来;
    {
        int i;
        for (i = 1; i <= n; i++)
        {
            if (i % 6 == 0 || i % 10 == 6 || i / 10 == 6 || i / 100 == 6 || i / 1000 == 6)
            {
                printf("%d  ", i);
            }
        }
    }
    
    int main()
    {
        int input = 0;
        printf("Please enter a number
    ");
        scanf("%d", &input);
        fun(input);
        return 0;
    }
    View Code
  • 相关阅读:
    tree
    gridview XML
    http://jingyan.baidu.com/article/22a299b513f3db9e18376a5e.html
    tree btn
    sss rar jar
    ComponentOne Studio
    http://wpf_sl.cnblogs.com/ WPF/Silverlight深蓝团队
    ddddd
    [牛客每日一题] (前缀和+线性 DP) NC15553 数学考试
    [停更一周,我干了什么] [C++/QT] 一个基于avl树,trie树,哈希散列表的英汉词典
  • 原文地址:https://www.cnblogs.com/xiaojian98/p/12470872.html
Copyright © 2020-2023  润新知