zoukankan      html  css  js  c++  java
  • C Primer Plus学习笔记【9-10章节】

    9.1 复习函数

    函数(function)是完成特定任务的独立程序代码单元,函数具备两个功能,执行某些动作,返回一个值供程序是使用。

    9.11创建并使用简单函数

    #include <stdio.h>
    #define NAME "GICATHINK, INC."
    #define ADDRESS "101 Megabuck Plaza"
    #define PLACE "Megapolis, CA 94904"
    #define WIDTH 40
    
    void statbar(void);   /* 函数原型 */
    
    int main(void)
    {
        statbar();
        printf("%s
    ", NAME);
        printf("%s
    ", ADDRESS);
        printf("%s
    ", PLACE);
        statbar();
        
        return 0;
    }
    
    void statbar(void)      /* 定义函数 */
    {
        int count;
        
        for (count = 1; count <= WIDTH; count++) {
            putchar('*');
        }
        putchar('
    ');
    }

    输出

    ****************************************

    GICATHINK, INC.

    101 Megabuck Plaza

    Megapolis, CA 94904

    ****************************************

    Program ended with exit code: 0

    9.1.2分析程序

    9.1.3函数参数

    使用带有参数的函数

    #include <stdio.h>
    #include <string.h>      /* 为strlen()提供原型 */
    #define NAME "GICATHINK, INC."
    #define ADDRESS "101 Megabuck Plaza"
    #define PLACE "Megapolis, CA 94904"
    #define WIDTH 40
    #define SPACE ' '
    
    void show_n_char(char ch, int num);
    
    int main(void){
        int spaces;
        
        show_n_char('*', WIDTH);
        putchar('
    ');
        show_n_char(SPACE, 12);
        printf("%s
    ", NAME);
        spaces = (WIDTH - strlen(ADDRESS)) / 2;  /* 计算需要跳过的空格 */
        
        show_n_char(SPACE, spaces);
        printf("%s
    ", ADDRESS);
        show_n_char(SPACE, (WIDTH - strlen(PLACE)) / 2);
        
        printf("%s
    ", PLACE);
        show_n_char('*', WIDTH);
        putchar('
    ');
        
        return 0;
    
    }
    
    void show_n_char(char ch, int num){
        int count;
        for (count = 1; count <= num; count++) {
            putchar(ch);
        }
    }

    输出

    ****************************************

                GICATHINK, INC.

               101 Megabuck Plaza

              Megapolis, CA 94904

    ****************************************

     

    9.1.4 定义带形式参数的函数

    9.1.5 声明带形式参数函数的原型

    void show_n_char(char, int);

    可以在里面不写变量名,在原型中使用变量名并没有实际创建变量。

    9.1.6 调用带实际参数的函数

    9.1.7 黑盒视角

    函数的形参,内部定义变量都属于局部变量,在主函数中定义相同变量,不会影响局部变量,局部变量也不会影响主函数的变量。

    9.1.8 使用return从函数中返回值

     示例代码

    #include <stdio.h>
    
    int imin(int, int);
    
    int main(void)
    {
        int evil1, evil2;
        
        printf("Enter a pair of integers (q to quit):
    ");
        
        while (scanf("%d %d", &evil1, &evil2) == 2) {
            printf("The lesser of %d and %d is %d.
    ",
                   evil1,evil2, imin(evil1, evil2));
            printf("Enter a pair of integers (q to quit):
    ");
        }
        printf("Bye.
    ");
        return 0;
    }
    
    int imin(int n, int m){
        int min;
        if (n < m) {
            min = n;
        }
        else
            min = m;
        
        return min;
    }

    输出

    Enter a pair of integers (q to quit):

    50 100

    The lesser of 50 and 100 is 50.

    Enter a pair of integers (q to quit):

    20 10

    The lesser of 20 and 10 is 10.

    Enter a pair of integers (q to quit):

    qBye.

    改进用条件表达式就只用一条语句

    #include <stdio.h>
    
    int imin(int, int);
    
    int main(void)
    {
        int evil1, evil2;
        
        printf("Enter a pair of integers (q to quit):
    ");
        
        while (scanf("%d %d", &evil1, &evil2) == 2) {
            printf("The lesser of %d and %d is %d.
    ",
                   evil1,evil2, imin(evil1, evil2));
            printf("Enter a pair of integers (q to quit):
    ");
        }
        printf("Bye.
    ");
        return 0;
    }
    
    int imin(int n, int m){
        return (n < m) ? n : m;
    }

    还可以改进用两个return,代码略

    return可以后面不加东西,会终止函数。

    9.1.9 函数类型

    要正确的使用函数,程序在第1次使用函数之前就必须知道函数的类型。方法之一是,把完整的函数定义放在第1次调用函数的前面。第二种就是提前声明函数,把函数信息告知编译器。

    9.2 ANSI C函数原型

    在ANSI C标准之前,申明函数只需要声明函数的类型,不用声明烦人喝参数。

    比如 int imin();

    9.2.1问题所在

    错误示例代码

    #include <stdio.h>
    
    int imax();
    
    int main(void)
    {
        printf("The maxinum of %d and %d is %d.
    ", 3, 5, imax(3));
        printf("The maxinum of %d and %d is %d.
    ", 3, 5, imax(3.0 , 5.0));
        
        return 0;
    }
    
    int imax(n, m)
    int n, m;
    {
        return (n >m ? n : m);
    }

    错误输出

    The maxinum of 3 and 5 is 3.

    The maxinum of 3 and 5 is 0.

    Program ended with exit code: 0

    这个是因为主函数把它的参数存储在被称为栈(stack)的临时存储区,被调函数从栈中读取这些参数。对于该例子,这两个过程并未相互协调。主调函数根据函数调用中的实际参数决定传递的类型,而被调函数根据它的形参读取值。这就出现问题了,int是32为的,double是128位的,float作为参数传参时会升级为double

    9.2.2ANSI的解决方案。

    就是在函数原型(function prototype)来声明函数的返回类型,参数的数量和每个参数的类型。

    #include <stdio.h>
    
    int imax(int, int);
    
    int main(void)
    {
        printf("The maxinum of %d and %d is %d.
    ", 3, 5, imax(3, 5));
        printf("The maxinum of %d and %d is %d.
    ", 3, 5, imax(3.0 , 5.0));
        
        return 0;
    }
    
    int imax(n, m)
    int n, m;
    {
        return (n >m ? n : m);
    }

    这样在函数在调用的时候,会把实际参数的类型转换承形式参数的类型。

    9.2.3 无参数和未指定参数

    用void print_name(void);

    后面将介绍通过*接受不定长参数,如printf,scanf

    9.2.4 函数原型的有点

    函数原型好处很多,要用起来,如果为较小的函数,可以写在被调用之前,此时函数的定义也相当于函数原型

    int imax(int a, int b) { return a > b ? a: b}

    int main(void)

    {

        ...

        z = imax......

        return 0;

    }

    9.3 递归

    本人对递归看到就头痛,递归有时候更简洁,但效率没有循环高。

    9.3.1演示递归

    #include <stdio.h>
    
    void up_and_down(int);
    
    int main(void)
    {
        up_and_down(1);
        return 0;
    }
    
    void up_and_down(int n)
    {
        printf("Level %d: n location %p
    ",n , &n); /* %p格式化输出地址,&n取对象地址 */
        if (n < 4) {
            up_and_down(n + 1);
        }
        printf("LEVEL %d: n location %p
    ",n , &n); /* %p格式化输出地址,&n取对象地址 */
    }

    输出

    Level 1: n location 0x7ffeefbff46c

    Level 2: n location 0x7ffeefbff44c

    Level 3: n location 0x7ffeefbff42c

    Level 4: n location 0x7ffeefbff40c

    LEVEL 4: n location 0x7ffeefbff40c

    LEVEL 3: n location 0x7ffeefbff42c

    LEVEL 2: n location 0x7ffeefbff44c

    LEVEL 1: n location 0x7ffeefbff46c

    Program ended with exit code: 0

    书中介绍,略

    9.3.2 递归的基本原理

    9.3.3 尾递归

    最简单的递归形式是把递归调用置于函数的末尾,即正好在return语句之前。这种形式的递归被称为尾递归(tail recursion),因为递归调用在函数的尾巴。尾递归是最简单的递归形式,因为它相当于循环。

    示例代码,求一个数的阶乘(factorial)

    #include <stdio.h>
    
    long fact(int);
    long rfact(int);
    
    int main(void){
        int num;
        
        printf("This program calculates factorials.
    ");
        printf("Enter a value in the range 0-12 (q to quit):
    ");
        while (scanf("%d", &num) == 1) {
            if (num < 0) {
                printf("No negative numbers, please.
    ");
            }
            else if (num > 12)
                printf("Keep input under 13.
    ");
            else {
                printf("loop: %d factorial = %ld
    ",
                       num, fact(num));
                printf("recursion: %d factorial = %ld
    ",
                       num, rfact(num));
                
            }
        }
        
        return 0;
    }
    
    long fact(int n)
    {
        long ans;
        
        for (ans = 1; n >1; n--) {
            ans *=n;
        }
        
        return ans;
    }
    
    long rfact(int n)
    {
        long ans;
        if (n > 0) {
            ans = n * rfact(n -1);
        }
        else
            ans = 1;
        
        return ans;
    }

    输出

    This program calculates factorials.

    Enter a value in the range 0-12 (q to quit):

    10

    loop: 10 factorial = 3628800

    recursion: 10 factorial = 3628800

    7

    loop: 7 factorial = 5040

    recursion: 7 factorial = 5040

    q

    Program ended with exit code: 0

    一般使用循环比较好。首先,每次递归都会创建一组变量,所有递归使用内存更多,而且每次递归调用都会把创建的一组新变量放在栈中。递归调用的数量受限与内存空间。其次每次函数调用要花费一定的时间,所以递归的执行速度比较满。

    9.3.4 递归和倒序计算。

     本节书中用了10进制转换成2进制,用了除2取余的方式取二进制。示例代码

    #include <stdio.h>
    
    void to_binary(unsigned long);
    
    int main(void)
    {
        unsigned long number;
        printf("Enter an integer (q to quit): 
    ");
        while (scanf("%lu", &number) == 1) {
            printf("Binary equivalent: ");
            to_binary(number);
            putchar('
    ');
            printf("Enter an integer (q to quit):
    ");
            
        }
        printf("Done.
    ");
        
        return 0;
    }
    
    void to_binary(unsigned long n)
    {
        int r;
        r = n % 2;         // 取余数,最后一个数字,先进后出
        if (n >= 2) {
            to_binary(n / 2);
        }
        putchar(r == 0 ? '0' : '1');
        
        return;
    }

    上面写了一个注释,很好的使用了递归的特性,先进后出的逻辑。

    9.3.5  递归的优缺点

    书中介绍了斐波那契数列,运用双递归的方式(double recursion),内存的占用将指数倍的增长,确实夸张。

    递归的优点,确实为某些编程问题提供了最简单的解决方案。但缺点是一些递归算法会快速消耗计算机的内存资源。

    9.4 编译多源代码文件的程序。

    C语言蛮好,同一个文件下,不用倒包,最多用一个头文件就好了,里面可以定义一些常量,还有一些函数原型。

    书中案例一个项目下面,用了两个C的文件,一个.h的头文件。

    源代码

    //
    //  usehotel.c
    //  sd
    //
    //  Created by sidian on 2020/12/23.
    //  Copyright © 2020 sidian. All rights reserved.
    //
    
    #include <stdio.h>
    #include "hotel.h"    // 倒入自定义的头文件
    
    int main(void)
    {
        int nights;
        double hotel_rate;
        int code;
    
        while ((code = menu()) != QUIT) {
            switch (code) {
                case 1:
                    hotel_rate = HOTEL1;
                    break;
                case 2:
                    hotel_rate = HOTEL2;
                    break;
                case 3:
                    hotel_rate = HOTEL3;
                    break;
                case 4:
                    hotel_rate = HOTEL4;
                    break;
                default:
                    hotel_rate = .0;
                    printf("Oop!
    ");
                    break;
            }
            nights = getnights();
            showprice(hotel_rate, nights);
        }
        
        printf("Thank you and goodbye.
    ");
        
        return 0;
    }
    //
    //  hotel.c
    //  sd
    //
    //  Created by sidian on 2020/12/23.
    //  Copyright © 2020 sidian. All rights reserved.
    //
    //
    #include <stdio.h>
    #include "hotel.h"
    
    int menu(void)
    {
        int code, status;
        printf("
    %s%s
    ", STARS, STARS);
        printf("Enter the number of the desired hotel:
    ");
        printf("1) Fairfield Arms       2) Hotel Olympic
    ");
        printf("3) Chertworthy Plaza    4) The Stockton
    ");
        printf("5) quit
    ");
        printf("%s%s
    ", STARS, STARS);
        while ((status = scanf("%d", &code)) != 1 ||
               (code < 1 || code >5))
        {
            if (status != 1) {
                scanf("%*s");     // 处理非整数的输入,中间不能有空格
            }
            printf("Enter an integer from 1 to 5, please.
    ");
        }
        
        return code;
    }
    
    int getnights(void)
    {
        int nights;
        
        printf("How mant nights are needed? ");
        while (scanf("%d", &nights) != 1) {
            scanf("%*s");
            printf("Please enter an integer, such as 2.
    ");
        }
        
        return nights;
    }
    
    void showprice(double rate, int nights)
    {
        int n;
        double total = .0;
        double factor = 1.0;
        
        for (n = 1; n <= nights; n++, factor *= DISCOUNT) {
            total += rate * factor;
        }
        printf("The total cost will be $%0.2f
    ", total);
    }
    //
    //  hotel.h
    //  sd
    //
    //  Created by sidian on 2020/12/23.
    //  Copyright © 2020 sidian. All rights reserved.
    //
    
    #ifndef hotel_h
    #define hotel_h
    
    
    #endif /* hotel_h */
    
    #define QUIT   5
    #define HOTEL1 180.00
    #define HOTEL2 225.00
    #define HOTEL3 255.00
    #define HOTEL4 355.00
    #define DISCOUNT 0.95
    #define STARS "****************"
    
    // 显式选择列表 原型函数
    int menu(void);
    
    // 返回预订天数
    int getnights(void);
    
    // 根据费率、入住天数计算费用,并显式结果
    void showprice(double rate, int nights);

    书中介绍了用scanf返回的值来判断是否接受到了正确的值,并且用scanf(%*s)来处理错误的输入。

    我个人感觉不是非常好的方法,当输入sada 2的时候,会处理前面的英文,然后读取后面的2的输入,但错误提示还是会有,这样的用户体验应该不好。

    我个人觉的用getchar来处理更加合适,当读取到' '停止

    9.5 查找地址: &运算符

    指针(pointer)是C语言最重要的(有时也是最复杂的)概念之一,用于存储变量的地址。

    ------------恢复内容开始------------

    9.1 复习函数

    函数(function)是完成特定任务的独立程序代码单元,函数具备两个功能,执行某些动作,返回一个值供程序是使用。

    9.11创建并使用简单函数

    #include <stdio.h>
    #define NAME "GICATHINK, INC."
    #define ADDRESS "101 Megabuck Plaza"
    #define PLACE "Megapolis, CA 94904"
    #define WIDTH 40
    
    void statbar(void);   /* 函数原型 */
    
    int main(void)
    {
        statbar();
        printf("%s
    ", NAME);
        printf("%s
    ", ADDRESS);
        printf("%s
    ", PLACE);
        statbar();
        
        return 0;
    }
    
    void statbar(void)      /* 定义函数 */
    {
        int count;
        
        for (count = 1; count <= WIDTH; count++) {
            putchar('*');
        }
        putchar('
    ');
    }

    输出

    ****************************************

    GICATHINK, INC.

    101 Megabuck Plaza

    Megapolis, CA 94904

    ****************************************

    Program ended with exit code: 0

    9.1.2分析程序

    9.1.3函数参数

    使用带有参数的函数

    #include <stdio.h>
    #include <string.h>      /* 为strlen()提供原型 */
    #define NAME "GICATHINK, INC."
    #define ADDRESS "101 Megabuck Plaza"
    #define PLACE "Megapolis, CA 94904"
    #define WIDTH 40
    #define SPACE ' '
    
    void show_n_char(char ch, int num);
    
    int main(void){
        int spaces;
        
        show_n_char('*', WIDTH);
        putchar('
    ');
        show_n_char(SPACE, 12);
        printf("%s
    ", NAME);
        spaces = (WIDTH - strlen(ADDRESS)) / 2;  /* 计算需要跳过的空格 */
        
        show_n_char(SPACE, spaces);
        printf("%s
    ", ADDRESS);
        show_n_char(SPACE, (WIDTH - strlen(PLACE)) / 2);
        
        printf("%s
    ", PLACE);
        show_n_char('*', WIDTH);
        putchar('
    ');
        
        return 0;
    
    }
    
    void show_n_char(char ch, int num){
        int count;
        for (count = 1; count <= num; count++) {
            putchar(ch);
        }
    }

    输出

    ****************************************

                GICATHINK, INC.

               101 Megabuck Plaza

              Megapolis, CA 94904

    ****************************************

     

    9.1.4 定义带形式参数的函数

    9.1.5 声明带形式参数函数的原型

    void show_n_char(char, int);

    可以在里面不写变量名,在原型中使用变量名并没有实际创建变量。

    9.1.6 调用带实际参数的函数

    9.1.7 黑盒视角

    函数的形参,内部定义变量都属于局部变量,在主函数中定义相同变量,不会影响局部变量,局部变量也不会影响主函数的变量。

    9.1.8 使用return从函数中返回值

     示例代码

    #include <stdio.h>
    
    int imin(int, int);
    
    int main(void)
    {
        int evil1, evil2;
        
        printf("Enter a pair of integers (q to quit):
    ");
        
        while (scanf("%d %d", &evil1, &evil2) == 2) {
            printf("The lesser of %d and %d is %d.
    ",
                   evil1,evil2, imin(evil1, evil2));
            printf("Enter a pair of integers (q to quit):
    ");
        }
        printf("Bye.
    ");
        return 0;
    }
    
    int imin(int n, int m){
        int min;
        if (n < m) {
            min = n;
        }
        else
            min = m;
        
        return min;
    }

    输出

    Enter a pair of integers (q to quit):

    50 100

    The lesser of 50 and 100 is 50.

    Enter a pair of integers (q to quit):

    20 10

    The lesser of 20 and 10 is 10.

    Enter a pair of integers (q to quit):

    qBye.

    改进用条件表达式就只用一条语句

    #include <stdio.h>
    
    int imin(int, int);
    
    int main(void)
    {
        int evil1, evil2;
        
        printf("Enter a pair of integers (q to quit):
    ");
        
        while (scanf("%d %d", &evil1, &evil2) == 2) {
            printf("The lesser of %d and %d is %d.
    ",
                   evil1,evil2, imin(evil1, evil2));
            printf("Enter a pair of integers (q to quit):
    ");
        }
        printf("Bye.
    ");
        return 0;
    }
    
    int imin(int n, int m){
        return (n < m) ? n : m;
    }

    还可以改进用两个return,代码略

    return可以后面不加东西,会终止函数。

    9.1.9 函数类型

    要正确的使用函数,程序在第1次使用函数之前就必须知道函数的类型。方法之一是,把完整的函数定义放在第1次调用函数的前面。第二种就是提前声明函数,把函数信息告知编译器。

    9.2 ANSI C函数原型

    在ANSI C标准之前,申明函数只需要声明函数的类型,不用声明烦人喝参数。

    比如 int imin();

    9.2.1问题所在

    错误示例代码

    #include <stdio.h>
    
    int imax();
    
    int main(void)
    {
        printf("The maxinum of %d and %d is %d.
    ", 3, 5, imax(3));
        printf("The maxinum of %d and %d is %d.
    ", 3, 5, imax(3.0 , 5.0));
        
        return 0;
    }
    
    int imax(n, m)
    int n, m;
    {
        return (n >m ? n : m);
    }

    错误输出

    The maxinum of 3 and 5 is 3.

    The maxinum of 3 and 5 is 0.

    Program ended with exit code: 0

    这个是因为主函数把它的参数存储在被称为栈(stack)的临时存储区,被调函数从栈中读取这些参数。对于该例子,这两个过程并未相互协调。主调函数根据函数调用中的实际参数决定传递的类型,而被调函数根据它的形参读取值。这就出现问题了,int是32为的,double是128位的,float作为参数传参时会升级为double

    9.2.2ANSI的解决方案。

    就是在函数原型(function prototype)来声明函数的返回类型,参数的数量和每个参数的类型。

    #include <stdio.h>
    
    int imax(int, int);
    
    int main(void)
    {
        printf("The maxinum of %d and %d is %d.
    ", 3, 5, imax(3, 5));
        printf("The maxinum of %d and %d is %d.
    ", 3, 5, imax(3.0 , 5.0));
        
        return 0;
    }
    
    int imax(n, m)
    int n, m;
    {
        return (n >m ? n : m);
    }

    这样在函数在调用的时候,会把实际参数的类型转换承形式参数的类型。

    9.2.3 无参数和未指定参数

    用void print_name(void);

    后面将介绍通过*接受不定长参数,如printf,scanf

    9.2.4 函数原型的有点

    函数原型好处很多,要用起来,如果为较小的函数,可以写在被调用之前,此时函数的定义也相当于函数原型

    int imax(int a, int b) { return a > b ? a: b}

    int main(void)

    {

        ...

        z = imax......

        return 0;

    }

    9.3 递归

    本人对递归看到就头痛,递归有时候更简洁,但效率没有循环高。

    9.3.1演示递归

    #include <stdio.h>
    
    void up_and_down(int);
    
    int main(void)
    {
        up_and_down(1);
        return 0;
    }
    
    void up_and_down(int n)
    {
        printf("Level %d: n location %p
    ",n , &n); /* %p格式化输出地址,&n取对象地址 */
        if (n < 4) {
            up_and_down(n + 1);
        }
        printf("LEVEL %d: n location %p
    ",n , &n); /* %p格式化输出地址,&n取对象地址 */
    }

    输出

    Level 1: n location 0x7ffeefbff46c

    Level 2: n location 0x7ffeefbff44c

    Level 3: n location 0x7ffeefbff42c

    Level 4: n location 0x7ffeefbff40c

    LEVEL 4: n location 0x7ffeefbff40c

    LEVEL 3: n location 0x7ffeefbff42c

    LEVEL 2: n location 0x7ffeefbff44c

    LEVEL 1: n location 0x7ffeefbff46c

    Program ended with exit code: 0

    书中介绍,略

    9.3.2 递归的基本原理

    9.3.3 尾递归

    最简单的递归形式是把递归调用置于函数的末尾,即正好在return语句之前。这种形式的递归被称为尾递归(tail recursion),因为递归调用在函数的尾巴。尾递归是最简单的递归形式,因为它相当于循环。

    示例代码,求一个数的阶乘(factorial)

    #include <stdio.h>
    
    long fact(int);
    long rfact(int);
    
    int main(void){
        int num;
        
        printf("This program calculates factorials.
    ");
        printf("Enter a value in the range 0-12 (q to quit):
    ");
        while (scanf("%d", &num) == 1) {
            if (num < 0) {
                printf("No negative numbers, please.
    ");
            }
            else if (num > 12)
                printf("Keep input under 13.
    ");
            else {
                printf("loop: %d factorial = %ld
    ",
                       num, fact(num));
                printf("recursion: %d factorial = %ld
    ",
                       num, rfact(num));
                
            }
        }
        
        return 0;
    }
    
    long fact(int n)
    {
        long ans;
        
        for (ans = 1; n >1; n--) {
            ans *=n;
        }
        
        return ans;
    }
    
    long rfact(int n)
    {
        long ans;
        if (n > 0) {
            ans = n * rfact(n -1);
        }
        else
            ans = 1;
        
        return ans;
    }

    输出

    This program calculates factorials.

    Enter a value in the range 0-12 (q to quit):

    10

    loop: 10 factorial = 3628800

    recursion: 10 factorial = 3628800

    7

    loop: 7 factorial = 5040

    recursion: 7 factorial = 5040

    q

    Program ended with exit code: 0

    一般使用循环比较好。首先,每次递归都会创建一组变量,所有递归使用内存更多,而且每次递归调用都会把创建的一组新变量放在栈中。递归调用的数量受限与内存空间。其次每次函数调用要花费一定的时间,所以递归的执行速度比较满。

    9.3.4 递归和倒序计算。

     本节书中用了10进制转换成2进制,用了除2取余的方式取二进制。示例代码

    #include <stdio.h>
    
    void to_binary(unsigned long);
    
    int main(void)
    {
        unsigned long number;
        printf("Enter an integer (q to quit): 
    ");
        while (scanf("%lu", &number) == 1) {
            printf("Binary equivalent: ");
            to_binary(number);
            putchar('
    ');
            printf("Enter an integer (q to quit):
    ");
            
        }
        printf("Done.
    ");
        
        return 0;
    }
    
    void to_binary(unsigned long n)
    {
        int r;
        r = n % 2;         // 取余数,最后一个数字,先进后出
        if (n >= 2) {
            to_binary(n / 2);
        }
        putchar(r == 0 ? '0' : '1');
        
        return;
    }

    上面写了一个注释,很好的使用了递归的特性,先进后出的逻辑。

    9.3.5  递归的优缺点

    书中介绍了斐波那契数列,运用双递归的方式(double recursion),内存的占用将指数倍的增长,确实夸张。

    递归的优点,确实为某些编程问题提供了最简单的解决方案。但缺点是一些递归算法会快速消耗计算机的内存资源。

    9.4 编译多源代码文件的程序。

    C语言蛮好,同一个文件下,不用倒包,最多用一个头文件就好了,里面可以定义一些常量,还有一些函数原型。

    书中案例一个项目下面,用了两个C的文件,一个.h的头文件。

    源代码

    //
    //  usehotel.c
    //  sd
    //
    //  Created by sidian on 2020/12/23.
    //  Copyright © 2020 sidian. All rights reserved.
    //
    
    #include <stdio.h>
    #include "hotel.h"    // 倒入自定义的头文件
    
    int main(void)
    {
        int nights;
        double hotel_rate;
        int code;
    
        while ((code = menu()) != QUIT) {
            switch (code) {
                case 1:
                    hotel_rate = HOTEL1;
                    break;
                case 2:
                    hotel_rate = HOTEL2;
                    break;
                case 3:
                    hotel_rate = HOTEL3;
                    break;
                case 4:
                    hotel_rate = HOTEL4;
                    break;
                default:
                    hotel_rate = .0;
                    printf("Oop!
    ");
                    break;
            }
            nights = getnights();
            showprice(hotel_rate, nights);
        }
        
        printf("Thank you and goodbye.
    ");
        
        return 0;
    }
    //
    //  hotel.c
    //  sd
    //
    //  Created by sidian on 2020/12/23.
    //  Copyright © 2020 sidian. All rights reserved.
    //
    //
    #include <stdio.h>
    #include "hotel.h"
    
    int menu(void)
    {
        int code, status;
        printf("
    %s%s
    ", STARS, STARS);
        printf("Enter the number of the desired hotel:
    ");
        printf("1) Fairfield Arms       2) Hotel Olympic
    ");
        printf("3) Chertworthy Plaza    4) The Stockton
    ");
        printf("5) quit
    ");
        printf("%s%s
    ", STARS, STARS);
        while ((status = scanf("%d", &code)) != 1 ||
               (code < 1 || code >5))
        {
            if (status != 1) {
                scanf("%*s");     // 处理非整数的输入,中间不能有空格
            }
            printf("Enter an integer from 1 to 5, please.
    ");
        }
        
        return code;
    }
    
    int getnights(void)
    {
        int nights;
        
        printf("How mant nights are needed? ");
        while (scanf("%d", &nights) != 1) {
            scanf("%*s");
            printf("Please enter an integer, such as 2.
    ");
        }
        
        return nights;
    }
    
    void showprice(double rate, int nights)
    {
        int n;
        double total = .0;
        double factor = 1.0;
        
        for (n = 1; n <= nights; n++, factor *= DISCOUNT) {
            total += rate * factor;
        }
        printf("The total cost will be $%0.2f
    ", total);
    }
    //
    //  hotel.h
    //  sd
    //
    //  Created by sidian on 2020/12/23.
    //  Copyright © 2020 sidian. All rights reserved.
    //
    
    #ifndef hotel_h
    #define hotel_h
    
    
    #endif /* hotel_h */
    
    #define QUIT   5
    #define HOTEL1 180.00
    #define HOTEL2 225.00
    #define HOTEL3 255.00
    #define HOTEL4 355.00
    #define DISCOUNT 0.95
    #define STARS "****************"
    
    // 显式选择列表 原型函数
    int menu(void);
    
    // 返回预订天数
    int getnights(void);
    
    // 根据费率、入住天数计算费用,并显式结果
    void showprice(double rate, int nights);

    书中介绍了用scanf返回的值来判断是否接受到了正确的值,并且用scanf(%*s)来处理错误的输入。

    我个人感觉不是非常好的方法,当输入sada 2的时候,会处理前面的英文,然后读取后面的2的输入,但错误提示还是会有,这样的用户体验应该不好。

    我个人觉的用getchar来处理更加合适,当读取到' '停止

    9.5 查找地址: &运算符[终于到指针了,小激动]

    指针(pointer)是C语言最重要的(有时也是最复杂的)概念之一,用于存储变量的地址。

     同&符号也就是&variable是变量的地址

    示例代码

    #include <stdio.h>
    void mikado(int);
    
    int main(void)
    {
        int pooh = 2, bah = 5;
        
        printf("In main(), pooh = %d and &pooh = %p
    ", pooh, &pooh);
        printf("In main(), bah = %d and &pooh = %p
    ", bah, &bah);
        mikado(pooh);
        
        return 0;
    }
    
    void mikado(int bah){
        int pooh = 10;
        printf("In mikado(), pooh = %d and &pooh = %p
    ", pooh, &pooh);
        printf("In mikado(), bah = %d and &pooh = %p
    ", bah, &bah);
        
    }

    输出

    In main(), pooh = 2 and &pooh = 0x7ffeefbff488

    In main(), bah = 5 and &pooh = 0x7ffeefbff484

    In mikado(), pooh = 10 and &pooh = 0x7ffeefbff458

    In mikado(), bah = 2 and &pooh = 0x7ffeefbff45c

    C语言每个函数都有自己的变量,这个跟Python不一样,Python属于引用传参。

    9.6 更改主调函数中的变量

    示例代码

    9.7 指针介绍

    指针(poiner)是一个值为内存地址的变量(或数据对象)

    9.7.1 间接运算符: *

    &变量名是变量名的地址

    *地址给出了在指针指向地址上的值。

    9.7.2 声明指针

    通过 int * p 或者 char * c等方式声明指针。

    *与指针名之间的空格可有可无。通常,程序员在声明时使用空格,在解引用变量时省略空格。指针时一个新类型,所以格式化输出需要用%p来转换

    9.7.3 使用指针在函数间通信

    通过指针,调用函数修改外部的值,通过传入地址,然后*地址的方式修改外部函数的值。

    示例代码

    #include <stdio.h>
    
    void interchange(int *, int *);
    
    int main(void)
    {
        int x= 5, y = 10;
        printf("Originally x = %d and y = %d.
    ", x ,y);
        interchange(&x, &y);    // 传入变量地址
        printf("Now x = %d and y = %d.
    ", x ,y);
        
        return 0;
    }
    
    void interchange(int * u, int * v)
    {
        int temp;
        temp = *u;      // 这里的*u就时main函数的x的值
        *u = *v;
        *v = temp;
    }

    输出

    Originally x = 5 and y = 10.

    Now x = 10 and y = 5.

     

    简而言之,普通变量把值作为基数量,把地址作为通过&运算符获得的派生量,而指针变量把地址作为基数量,把值作为通过*运算符获得的派生量。

     

    9.8----9.9

     

    9.10 复习题

    第一题

    抄答案: 形式参数时定义在被调用函数中的变量。实际参数是出现在函数调用中的值,该值被赋给形式参数。

    可以把实际参数理解为在函数调用时初始化形式参数的值。

    第二题

    a. void dount(int n)

    b. int gear(int n, int m)

    c.int guess(void)

    d.void stuff_it(double d, double * pd)

    第三题

    a. char n_to_char(int n)

    b. int digit(double n, int m)

    c. double * which(double * n, double * m)

    d.int random(void)

    第四题

    int add(int u, int v)
    {
        return u + v;
    }

    第五题

    double add(double u, double v)
    {
        return u + v;
    }

    第六题

    void alter(int * u, int * v)
    {
        int m1, m2;
        m1 = *u;
        m2 = *v;
        *u = m1 + m2;
        *v = m1 - m2;
    
    }

    答案的写法:

    void alter(int * u, int * v)
    {
        *u = *u + *v;
        *v = *u - 2 * *p;
    
    }

    答案就是坏啊,都不用中间变量。

    第七题

    不正确

    第八题

    int max(int a, int b, int c)
    {
        int max_num;
        max_num = a > b ? a : b;
        max_num = max_num > c ? max_num : c;
        return max_num;
    
    }

    第九题

    #include <stdio.h>
    
    void display(void);
    int choice_range(int, int);
    
    int main(void)
    {
        
        int in_num;
        while (1) {
            display();
            in_num = choice_range(1, 4);
            switch (in_num) {
                case 1:
                    printf("copy files 
    ");
                    break;
                case 2:
                    printf("move files 
    ");
                    break;
                case 3:
                    printf("remove files 
    ");
                    break;
                case 4:
                    printf("Bye.
    ");
                    return 0;
                default:
                    break;
            }
        }
        
        return 0;
    }
    
    void display(void){
        printf("Please choose one of the following:
    ");
        printf("1) copy files              2) move files
    ");
        printf("3) remove files            4) quit
    ");
        printf("Enter the number or your choice:");
    }
    
    int choice_range(int min, int max){
        int in_num;
        
        while (scanf("%d", &in_num) == 1) {
            if (in_num > max || in_num < min) {
                display();
            }
            else
                return in_num;
        }
        
        return 4;
    }

    输出

    Please choose one of the following:

    1) copy files              2) move files

    3) remove files            4) quit

    Enter the number or your choice:12

    Please choose one of the following:

    1) copy files              2) move files

    3) remove files            4) quit

    Enter the number or your choice:3

    remove files 

    Please choose one of the following:

    1) copy files              2) move files

    3) remove files            4) quit

    Enter the number or your choice:2

    move files 

    Please choose one of the following:

    1) copy files              2) move files

    3) remove files            4) quit

    Enter the number or your choice:q

    Bye.

    Program ended with exit code: 0

    9.11 编程练习

     第一题

    #include <stdio.h>
    
    double min(double ,double);
    
    int main(void)
    {
        double x =5.5, y =6;
        double res;
        
        res = min(x, y);
        printf("res = %f.
    ", res);
        
        return 0;
    }
    
    
    double min(double x, double y)
    {
        return x > y ? y : x;
    }

    第二题

    #include <stdio.h>
    
    void print_ok(char, int, int);
    
    int main(void)
    {
        int x =3, y =6;
        char ch = '$';
        
        print_ok(ch, x, y);
        
        return 0;
    }
    
    
    void print_ok(char ch, int x, int y)
    {
        int row, column;
        for (row = 1; row <= x; row++) {
            for (column =1; column <= y; column++) {
                putchar(ch);
            }
            putchar('
    ');
        }
    }

    第三题

    #include <stdio.h>
    
    void print_ok(char, int, int);
    
    int main(void)
    {
        int x =3, y =6;
        char ch = '$';
        
        print_ok(ch, x, y);
        
        return 0;
    }
    
    
    void print_ok(char ch, int y, int x)
    {
        int row, column;
        for (row = 1; row <= x; row++) {
            for (column =1; column <= y; column++) {
                putchar(ch);
            }
            putchar('
    ');
        }
    }

    只不过在第二题的基础上,行与列的顺序换了以下而已。

    第四题

    #include <stdio.h>
    double aver_func(double, double);
    
    int main(void)
    {
        double res;
        res = aver_func(3, 2);
        printf("res is : %.2f.
    ", res);
        
        return 0;
    }
    
    double aver_func(double num1, double num2)
    {
        double res;
        
        res = 1 / ((1/num1 + 1/num2) / 2);
        
        return res;
    }

    输出

    res is : 2.40.

     第五题

    #include <stdio.h>
    
    void larger(double *, double *);
    
    int main()
    {
        double x = 1.98, y = -1.9;
        larger(&x, &y);
        printf("x is %f, y is %f.
    ", x ,y);
        
        return 0;
    }
    
    void larger(double * x, double * y)
    {
        double temp;
        temp = *x > *y ? *x : *y;
        *x = temp;
        *y = temp;
        
    }

    输出

    x is 1.980000, y is 1.980000.

    第六题

    #include <stdio.h>
    void myfunc(double *, double *, double *);
    
    int main(void)
    {
        double n1 = 9.1, n2 = 3.9, n3 = 2.5;
        myfunc(&n1, &n2, &n3);
        printf("n1 is %f, n2 is %f, n3 is %f.
    ", n1, n2, n3);
        
        return 0;
    }
    
    void myfunc(double * n1, double * n2, double * n3)
    {
        double min, middle, max;
        if (*n1 > *n2) {
            if (*n3 > *n1) {
                max = *n3;
                middle = *n1;
                min = *n2;
            }
            else
            {
                max = *n1;
                if (*n2 > *n3) {
                    middle = *n2;
                    min = *n3;
                }
                else{
                    middle = *n3;
                    min = *n2;
                }
            }
        }
        else{
            if (*n3 < *n1) {
                max = *n2;
                middle = *n1;
                min = *n3;
            }
            else
            {
                min = *n1;
                if (*n2 > *n3) {
                    max = *n2;
                    middle = *n3;
                }
                else{
                    max = *n3;
                    middle = *n2;
                }
            }
        }
        *n1 = min;
        *n2 = middle;
        *n3 = max;
        
    }

    输出

    n1 is 2.500000, n2 is 3.900000, n3 is 9.100000.

    第七题

    #include <stdio.h>
    #include <ctype.h>
    
    int char_index(char);
    
    int main(void)
    {
        char ch;
        int ch_index;
        printf("Content is :
    ");
        while ((ch = getchar()) != EOF) {
            ch_index = char_index(ch);
            printf("%c => %d	", ch, ch_index);
        }
        
    }
    
    int char_index(char ch)
    {
        char first_ch = 'a';
        int re_num;
        if (isalpha(ch)) {
            re_num = tolower(ch) - first_ch + 1;
        }
        else
            re_num = -1;
        
        return re_num;
    }

    第八题

    #include <stdio.h>
    
    double power(double, int);
    
    int main(void)
    {
        double x, xpow;
        int exp;
        printf("Enter a number and the positive integer power");
        printf(" to which
    the number will be raised. Enter q");
        printf(" to quit.
    ");
        while (scanf("%lf%d", &x, &exp) == 2) {
            xpow = power(x, exp);
            printf("%.3g to the power %d is %.5g
    ", x, exp, xpow);
            printf("Enter next pair of numbers or q to quit.
    ");
        }
        printf("Hope you enjoyed this power trip -- bye!
    ");
        
        return 0;
    }
    
    double power(double n, int p)
    {
        double pow = 1;
        int i;
        if (n == 0 && p != 0) {
            pow = 0;
        }
        if (p > 0) {
            for (i = 1; i <= p; i++) {
                pow *= n;
            }
        }
        else if (p < 0){
            p = -p;
            for (i = 1; i <= p; i++) {
                pow *= n;
            }
            pow = 1 / pow;
        }
        else{
            if (n == 0) {
                printf("0的0次方未定义,默认返回为1");
            }
            pow = 1;
        }
        
        return pow;
    }

    第9题

    #include <stdio.h>
    
    double power(double, int);
    
    int main(void)
    {
        double x, xpow;
        int exp;
        printf("Enter a number and the positive integer power");
        printf(" to which
    the number will be raised. Enter q");
        printf(" to quit.
    ");
        while (scanf("%lf%d", &x, &exp) == 2) {
            xpow = power(x, exp);
            printf("%.3g to the power %d is %.5g
    ", x, exp, xpow);
            printf("Enter next pair of numbers or q to quit.
    ");
        }
        printf("Hope you enjoyed this power trip -- bye!
    ");
        
        return 0;
    }
    
    double power(double n, int p)
    {
        double pow = 1;
    
    
        if (p >= 1) {    // 只要大于等于1就进去递归
            pow =  n * power(n, p - 1);
        }
        if (p < 0) {
            pow = 1/n * power(n, p + 1);
        }
        if (n == 0 && p == 0) {
            printf("0的0次方未定义,默认返回为1
    ");
        }
    
        return pow;
    }

    第十题

    #include <stdio.h>
    
    void to_binary(unsigned long,int);
    
    int main(void)
    {
        unsigned long number;
        printf("Enter an integer (q to quit): 
    ");
        while (scanf("%lu", &number) == 1) {
            printf("Binary equivalent: ");
            to_binary(number, 8);
            putchar('
    ');
            printf("Enter an integer (q to quit):
    ");
            
        }
        printf("Done.
    ");
        
        return 0;
    }
    
    void to_binary(unsigned long n,int m)
    {
        int r;
        r = n % m;         // 取余数,最后一个数字,先进后出
        if (n >= m) {
            to_binary(n / m, m);
        }
        printf("%d", r);
        
        return;
    }

    题目我改成了写死了的8进制,确实递归是我的若项,对照书本才写出的代码。

    第十一题

    #include <stdio.h>
    
    unsigned long long Fibonacci(int);
    
    int main(void)
    {
        int number;
        unsigned long long res;
        printf("Enter an fibonacci number (q to quit): 
    ");
        while (scanf("%d", &number) == 1) {
            res = Fibonacci(number);
            printf("The fibonacci resule is %llu.
    ", res);
        }
        printf("Done.
    ");
        
        return 0;
    }
    
    unsigned long long Fibonacci(int n)
    {
        long i, n1 = 1, n2 = 1;
        unsigned long long n3 = 1;
        if (n > 2) {
            n -= 2;
            for (i = 1; i <= n; i++) {
                n3 = n1 + n2;
                n1 = n2;
                n2 = n3;
            }
        }
        return n3;
    }

    第10章 数组和指针

     10.1 数组

    数组在声明的时候可以用过[]实现

    比如

    int main(void)
    {
        float candy[365];
        char code[12];
        int states[50];  
    
    }

    10.1.1 初始化数组

    只储存单个知道变量也称为标量变量(scalar variable)

    C使新的语法来初始化数组

    int main(void)
    {
        int powers[8] = {1,2,4,8,16,32,64}
    }

    示例代码

    #include <stdio.h>
    #define MONTHS 12
    
    int main(void)
    {
        int days[MONTHS] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 ,31};
        int index;
        
        for (index = 0; index < MONTHS; index++) {
            printf("Mnnth %2d has %2d days.
    ", index + 1, days[index]);
        }
        
        return 0;
    }

    初始化数失败的案例

    #include <stdio.h>
    #define SIZE 4
    
    int main(void)
    {
        int no_data[SIZE];
        int i;
        
        printf("%2s%14s
    ", "i", "nodata[i]");
        for (i = 0; i < SIZE; i++) {
            printf("%2d%14d
    ", i, no_data[i]);
        }
        
        return 0;
    }

    在我的mac下,用xcode直接跑出来的结果

     i     nodata[i]

     0             0

     1             0

     2             0

     3             0

     使用数组前必须先初始化它。

    使用部分初始化数组的情况

    #include <stdio.h>
    #define SIZE 4
    
    int main(void)
    {
        int no_data[SIZE] = {23,234};
        int i;
        
        printf("%2s%14s
    ", "i", "nodata[i]");
        for (i = 0; i < SIZE; i++) {
            printf("%2d%14d
    ", i, no_data[i]);
        }
        
        return 0;
    }

    输出

     i     nodata[i]

     0            23

     1           234

     2             0

     3             0

    当初始化列表的值少于数组元素个数时,编译器会把剩余的元素都初始化为0.也就是说,如果不初始数组,数组元素和未初始化的普通变量一样,其中储存的都是垃圾值,但是,如果部分初始化数组,剩余的元素就会被初始化为0。

    我们也可以省略方括号中的数字,让编译器自动匹配数组大小和初始化列表中的项数。

    #include <stdio.h>
    
    
    int main(void)
    {
        int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 ,31};
        int index;
        
        for (index = 0; index < sizeof(days) / sizeof(days[0]); index++) {
            printf("Mnnth %2d has %2d days.
    ", index + 1, days[index]);
        }
        
        return 0;
    }

    10.1.2 指定初始化器(c99)

    C99增加了一个新特性:指定初始化器(designated initialize)。利用该特性可以初始化指定的数组元素。

    C99规定,可以在初始化列表中使用带方括号的下标指名待初始化的元素:

    int arr[6] = {[5] = 212};  // 把arr[5]初始化为212

    对于一般的初始化,在初始化一个元素后,未初始化的元素都会被设置为0。

    示例代码

    #include <stdio.h>
    #define MONTHS 12
    
    int main(void)
    {
        int days[MONTHS] = {31, 28, [4]=31, 30, 31, [1]=29};
        int index;
        
        for (index = 0; index < MONTHS; index++) {
            printf("Mnnth %2d has %2d days.
    ", index + 1, days[index]);
        }
        
        return 0;
    }

    输出

    Mnnth  1 has 31 days.

    Mnnth  2 has 29 days.

    Mnnth  3 has  0 days.

    Mnnth  4 has  0 days.

    Mnnth  5 has 31 days.

    Mnnth  6 has 30 days.

    Mnnth  7 has 31 days.

    Mnnth  8 has  0 days.

    Mnnth  9 has  0 days.

    Mnnth 10 has  0 days.

    Mnnth 11 has  0 days.

    Mnnth 12 has  0 days.

    Program ended with exit code: 0

    示例说明了第一,如果指定初始化器后面的有更多的值,如该例的初始化列表中的片段:[4] = 31,30,31,那么后面这些值将被用于初始化指定元素后面的元素。

    第二,如果两次初始化指定的元素,那么最后的初始化将会取代之前的初始化。

    10.1.3 给数组元素赋值

    只能通过下标的方式给数组赋值,另外都是不允许的。

    10.1.4 数组边界

    这会是一个C语言很有意思的特性,可以允许你下标越界操作

    #include <stdio.h>
    #define SIZE 4
    
    int main(void)
    {
        int value1 = 44;
        int arr[SIZE];
        int value2 = 88;
        int i;
        
        printf("value1 = %d, value2 = %d
    ", value1, value2);
        for (i = -1; i <= SIZE; i++) {
            arr[i] = 2 * i + 1;
        }
        
        for (i = -1; i < 7; i++) {
            printf("%2d %d
    ", i , arr[i]);
        }
        printf("value1 = %d, value2 = %d
    ", value1, value2);
        printf("address of arr[-1]: %p
    ", &arr[-1]);
        printf("address of arr[4]: %p
    ", &arr[4]);
        printf("address of value1: %p
    ", &value1);
        printf("address of value2: %p
    ", &value2);
        
        return 0;
    }

    输出

    value1 = 44, value2 = 88

    -1 -1

     0 1

     1 3

     2 5

     3 7

     4 9

     5 32766

     6 1542127843

    value1 = 44, value2 = 88

    address of arr[-1]: 0x7ffeefbff46c

    address of arr[4]: 0x7ffeefbff480

    address of value1: 0x7ffeefbff468

    address of value2: 0x7ffeefbff464

    Program ended with exit code: 0

    我的实际操作与书中有所不同,我这里没有改变value1与value2的值。但越界操作还是实现了。

    这主要归功与C信任程序员的原则。不检查边界,C程序可以运行更快。

    10.1.5指定数组的大小

    在C99标准之前,声明数组时,只能在方括号使用整数常量表达式。所谓的整数常量表达式,是由整形常量构成的表达式。

    sizeof表达式被视为整数常量,另外表达式的值必须大于0。

    C90标准支持一下命令方式,叫变长数组(variable-length array)

    float a8[n];
    float a9[m];

    现在我还不是很了解具体用途,待后面学习

    10.2 多维数组

    书中介绍了二维数组,多维数组的理解就时数组里面的元素还是数组,示例代码如下

    #include <stdio.h>
    #define MONTHS 12
    #define YEARS 5
    
    int main(void)
    {
        const float rain[YEARS][MONTHS] =
        {
            {4.3, 4.3, 4.3, 3.0, 2.0, 1.2, 0.2, 0.2, 0.4, 2.4, 3.5, 6.6},
            {8.5, 8.2, 1.2, 1.6, 2.4, 0.0, 5.2, 0.9, 0.3, 0.9, 1.4, 7.3},
            {9.1, 8.5, 6.7, 4.3, 2.1, 0.8, 0.2, 0.2, 1.1, 2.3, 6.1, 8.4},
            {7.2, 9.9, 8.4, 3.3, 1.2, 0.8, 0.4, 0.0, 0.6, 1.7, 4.3, 6.2},
            {7.6, 5.6, 3.8, 2.8, 3.8, 0.2, 0.0, 0.0, 0.0, 1.3, 2.6, 5.2}
        };
        int year, month;
        float subtot, total;
        
        printf("YEAR    PAINFALL   (inches)
    ");
        for (year = 0, total = 0; year < YEARS; year++) {
            for (month = 0, subtot = 0; month < MONTHS; month++) {
                subtot += rain[year][month];
            }
            printf("%5d %15.1f
    ", 2010 + year, subtot);
            total += subtot;
        }
        printf("
    The yearly average is %.1f inches.
    
    ", total / YEARS);
        printf("MONTHLY AVERAGES:
    
    ");
        printf(" Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct ");
        printf(" Nov  Dec
    ");
        
        for (month = 0; month < MONTHS; month++) {
            for (year = 0, subtot = 0; year < YEARS; year++) {
                subtot += rain[year][month];
            }
            printf("%4.1f ",subtot / YEARS);    // 每个月的平均降水量
        }
        printf("
    ");
        
        return 0;
        
    }

    输出

    YEAR    PAINFALL   (inches)

     2010            32.4

     2011            37.9

     2012            49.8

     2013            44.0

     2014            32.9

     

    The yearly average is 39.4 inches.

     

    MONTHLY AVERAGES:

     

     Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec

     7.3  7.3  4.9  3.0  2.3  0.6  1.2  0.3  0.5  1.7  3.6  6.7 

    Program ended with exit code: 0

    10.2.1初始化二维数组

    一种时按照前面代码写的,每组少或者多元素不会影响其它小组,还有一种就是只有一个{},但元素少的时候会影响最后那些数组。

    10.2.2 其它所谓数组

    10.3 指针和数组

    数组名是数首元素的地址

    示例代码

    #include <stdio.h>
    #define SIZE 4
    int main(void)
    {
        short datas[SIZE];
        short * pti;
        short index;
        double bills[SIZE];
        double * ptf;
        pti = datas;        // 把数组地址给指针
        ptf = bills;
        printf("%23s %15s
    ","short", "doubule");
        for (index = 0; index < SIZE; index++) {
            printf("pointers + %d: %10p %10p.
    ", index, pti + index, ptf + index);
        }
        
        return 0;
        
    }

    输出

                      short         doubule

    pointers + 0: 0x7ffeefbff480 0x7ffeefbff460.

    pointers + 1: 0x7ffeefbff482 0x7ffeefbff468.

    pointers + 2: 0x7ffeefbff484 0x7ffeefbff470.

    pointers + 3: 0x7ffeefbff486 0x7ffeefbff478.

    Program ended with exit code: 0

    在C中,指针加1指的是增加一个存储单元。对数组而言,这意味者加1后的地址时下一个元素的地址,而不是下一个字节的地址。

    指针的值时它所指向对象的地址。

    在指针前面使用*运算符可以得到该指针所指向对象的值。

    指针加1,指针的值递增它所指向类型的大小(以字节为单位)

    dates + 2 == &dates[2]    // 相同的地址
    *(dates + 2) == dates[2]   // 相同的值

    实在代码,通过*地址的方式取值

    #include <stdio.h>
    
    
    int main(void)
    {
        int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 ,31};
        int index;
        
        for (index = 0; index < sizeof(days) / sizeof(days[0]); index++) {
            printf("Mnnth %2d has %2d days.
    ", index + 1, *(days + index));   // 这里用*的方式取地址值
        }
        
        return 0;
    }

    10.4 函数、数组和指针

    书中介绍了如何在函数中定义形参,传入指针的时候可以用过 int * ar的方式,也可以通过int ar[]来实现,效果都相同的。

    理解ar[1 ] ==*(at + 1)

    示例代码

    #include <stdio.h>
    #define SIZE 10
    int sum(int [], int n);
    
    int main(void)
    {
        int marbles[SIZE] = {20 , 10, 5, 39, 4, 16, 19, 26, 31, 20};
        long answer;
        
        
    //    printf("%zd
    ", sizeof marbles);
        answer = sum(marbles, SIZE);
        printf("The total number of marbles id %ld.
    ", answer);
        printf("the size of merbles is %zd bytes.
    ",
               sizeof(marbles));
        
        return 0;
        
    }
    
    int sum(int * ar, int n)
    {
        
        int i;
        int total = 0;
        
        for (i = 0; i < n; i++) {
            total += ar[i];
        }
        printf("The size of ar %zd bytes.
    ",sizeof(ar));      // 指针数据对象的大小
        
        return total;
    }

    The size of ar 8 bytes.

    The total number of marbles id 190.

    the size of merbles is 40 bytes.

    数值在定义函数中,我用int ar[]的方式来对形参进行设置,但由警告。

    10.4.1 使用指针形参。

    示例代码,直接用指针形参时变量操作。

    #include <stdio.h>
    #define SIZE 10
    int sum(int [], int []);   // 与 int * 效果相同
    
    int main(void)
    {
        int marbles[SIZE] = {20 , 10, 5, 39, 4, 16, 19, 26, 31, 20};
        long answer;
        
        
    //    printf("%zd
    ", sizeof marbles);
        answer = sum(marbles, marbles + SIZE);
        printf("The total number of marbles id %ld.
    ", answer);
        
        return 0;
        
    }
    
    int sum(int * start, int * end)    // 用ar[]会提示警告
    {
        
        int total = 0;
        
        while (start < end) {
            total += *start;
            start++;
        }
        
        return total;
    }

    这个函数在操作的时候越界了,越界了一个位置。

    但C保证在数组分配空间时,指向数组后面的第一个位置的指针仍是有效的指针[简单来说,就是还可以再超一位]

    求和的代码还可以简化为

    while (start < end) {
            total += *start++;
        }

    但最好应该写成这样

    while (start < end) {
            total += *(start++);
        }

    示例清单展示了* 与 ++ 的优先级示例。

    #include <stdio.h>
    int data[2] = { 100, 200};
    int moredata[2] = { 300, 400};
    int main(void)
    {
        int * p1, * p2, * p3;
        
        p1 = p2 = data;     // 指针赋值
        p3 = moredata;
        printf("  *p1 = %d,    *p2 = %d,    *p3 = %d
    ", *p1, *p2, *p3);
        printf("*p1++ = %d,  *++p2 = %d    (*p3)++ = %d
    ", *p1++, *++p2, (*p3)++);
        printf("  *p1 = %d,    *p2 = %d,    *p3 = %d
    ", *p1, *p2, *p3);
        
        return 0;
    }

    输出

      *p1 = 100,    *p2 = 100,    *p3 = 300

    *p1++ = 100,  *++p2 = 200    (*p3)++ = 300

      *p1 = 200,    *p2 = 200,    *p3 = 301

    Program ended with exit code: 0

    10.4.2 指针表示法和数组表示法

    对于C语言,ar[i]和*(ar+i)这两个表达式是等价的。

    10.5 指针操作

    示例代码

    #include <stdio.h>
    int main()
    {
        int urn[5] = { 100, 200, 300, 400, 500};
        int * ptr1, * ptr2, * ptr3;
        
        // 指针赋值
        ptr1 = urn;
        ptr2 = &urn[2];
        
        // 出现了指针的指针,指针变量也有自己的地址与值,也用&取地址
        printf("pointer value, dereferenced pointer, pointer address:
    ");
        printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p
    ", ptr1, *ptr1, &ptr1);
        
        // 指针加法
        ptr3 = ptr1 + 4;
        // 16进制的表示,4个位置刚好可以进位,所以这个地址刚好在十位数上面进位。
        printf("
     adding an int to a pointer:
    ");
        printf("ptr1 + 4 = %p, *(ptr1 + 4) = %d
    ", ptr1 + 4, *(ptr1 + 4));
        ptr1++;     // 递增指针
        printf("
    values after ptr1++:
    ");
        // 指针的指针地址不会变,因为指针对于它来说就是一个变量
        printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p
    ",ptr1, *ptr1, &ptr1);
        ptr2--;
        printf("
    values after --ptr2:
    ");
        printf("ptr2 = %p, *ptr2 = %d, &ptr2 = %p
    ",ptr2, *ptr2, &ptr2);
        --ptr1;
        ++ptr2;
        printf("
    Pointers reset to origianl values:
    ");
        printf("ptr1 = %p, ptr2 = %p
    ", ptr1, ptr2);
        // 一个指针减去另一个指针,结果代表两个元素的距离。
        printf("
    subtracting one pointer from another:
    ");
        printf("ptr2 = %p, ptr1 = %p, ptr2 - ptr1 = %td
    ",ptr2, ptr1, ptr2 - ptr1);
        // 一个指针减去一个整数
        printf("
    subtracting an int from a pointer:
    ");
        printf("ptr3 = %p, ptr3 -2 = %p
    ", ptr3, ptr3 -2);
        
        return 0;
        
    }

    输出

    pointer value, dereferenced pointer, pointer address:

    ptr1 = 0x7ffeefbff470, *ptr1 = 100, &ptr1 = 0x7ffeefbff460

     

     adding an int to a pointer:

    ptr1 + 4 = 0x7ffeefbff480, *(ptr1 + 4) = 500

     

    values after ptr1++:

    ptr1 = 0x7ffeefbff474, *ptr1 = 200, &ptr1 = 0x7ffeefbff460

     

    values after --ptr2:

    ptr2 = 0x7ffeefbff474, *ptr2 = 200, &ptr2 = 0x7ffeefbff458

     

    Pointers reset to origianl values:

    ptr1 = 0x7ffeefbff470, ptr2 = 0x7ffeefbff478

     

    subtracting one pointer from another:

    ptr2 = 0x7ffeefbff478, ptr1 = 0x7ffeefbff470, ptr2 - ptr1 = 2

     

    subtracting an int from a pointer:

    ptr3 = 0x7ffeefbff480, ptr3 -2 = 0x7ffeefbff478

    Program ended with exit code: 0

    书中介绍了,初始化的指针不要通过解引来赋值

    int * p;
    *p = 5;

    上面这种操作是不对的。创建一个指针时,系统只分配了存储指针本身的内存,并为分配存储数据的内存。因此,在使用指针之前,必须先用已分配的地址初始化它。

    10.6 保护数组中的数据

    数组在函数的操作中,必须传递指针,因为这样的效率高。如果一个函数按值传递数组,则必须分配足够的空间来存储原数组的副本,然后把原数组所有的数据拷贝到新的数组中。

    如果把数组的地址传递给函数,让数组直接处理原数组效率更高。但这个很有可能会改变原数组

    10.6.1 对形式参数使用const

    通过在原型函数和函数定义中使用const,告知该函数不能修改指向的数组中的内容。

    这样使用const并不是要求原数组是常量,而是该函数在处理数组时将其视为常量,不可修改。

    示例代码

    #include <stdio.h>
    #define SIZE 5
    void show_array(const double [], int);     // 定义不修改传入的数组
    void mult_array(double [], int, double);
    
    int main(void)
    {
        double dip[SIZE] = { 20.0, 17.66, 8.2, 15.3, 22.22};
        
        printf("The original dip array:
    ");
        show_array(dip, SIZE);
        mult_array(dip, SIZE, 2.5);
        printf("The dip array after calling mult_array():
    ");
        show_array(dip, SIZE);
        
        return 0;
    }
    
    void show_array(const double ar[], int n)
    {
        int i;
        
        for (i = 0; i < n; i++) {
            printf("%8.3f", ar[i]);
        }
        putchar('
    ');
    }
    
    void mult_array(double ar[], int n, double mult)
    {
        int i;
        for (i = 0; i < n; i++) {
            ar[i] *= mult;
        }
        
    }

    书中的示例代码非常不错的展示了,修改与不修改的情况下的运行。

    The original dip array:

      20.000  17.660   8.200  15.300  22.220

    The dip array after calling mult_array():

      50.000  44.150  20.500  38.250  55.550

    Program ended with exit code: 0

     10.6.2 const的其它内容

    书中介绍了一些const的用法,本人比较深刻的时初始化一个不能指向别处的指针,const写在指针的前面

    double rates[5] = {2 ,3, 4, 5, 6}
    double * const pc = rates
    pc = &rates[2];    // 不允许
    *pc = 1.9   // 没问题修改rates[0]的值

    10.7 指针和多维指针

    书中详细说明了二维数组的指针解引,很详细。示例代码如下

    #include <stdio.h>
    int main(void)
    {
        int zippo[4][2] = { {2, 4}, {6, 8}, {1, 3}, {5, 7}};
        
        printf("   zippo = %p,      ziipo + 1 = %p
    ", zippo, zippo + 1);
        printf("zippo[0] = %p,   zippo[0] + 1 = %p
    ", zippo[0], zippo[2] + 1);
        printf("  *zippo = %p,     *zippo + 1 = %p
    ", *zippo, *zippo + 1);
        printf("       zippo[0][0] = %d
    ", zippo[0][0]);
        printf("         *zippo[0] = %d
    ", *zippo[0]);
        printf("          **zipppo = %d
    ", **zippo);
        printf("       zippo[2][1] = %d
    ",zippo[2][1]);
        printf("*(*(zippo +2) + 1) = %d
    ", *(*(zippo +2) + 1));
        
        return 0;
    }

    输出

       zippo = 0x7ffeefbff460,      ziipo + 1 = 0x7ffeefbff468

    zippo[0] = 0x7ffeefbff460,   zippo[0] + 1 = 0x7ffeefbff474

      *zippo = 0x7ffeefbff460,     *zippo + 1 = 0x7ffeefbff464

           zippo[0][0] = 2

             *zippo[0] = 2

              **zipppo = 2

           zippo[2][1] = 3

    *(*(zippo +2) + 1) = 3

    通过输出可以了解到zippo与zippo[0]的关系*zippo = zippo[0],二维数组中各种指针的关系一目了然

    10.7.1 指向多维数组的指针

    前面的zippo的指针指向的是一个数组,这样指针该如何定义

    int (* pz)[2]

    说明指向的是一个内含两个int类型值的数组

    这个小括号不能少,书中有解释,因为中括号的优先级更高。

    示例代码

    #include <stdio.h>
    int main(void)
    {
        int zippo[4][2] = { {2, 4}, {6, 8}, {1, 3}, {5, 7}};
        
        int (* pz)[2];
        pz = zippo;
        
        printf("   pz = %p,      pz + 1 = %p
    ", pz, pz + 1);
        printf("pz[0] = %p,   pz[0] + 1 = %p
    ", pz[0], pz[2] + 1);
        printf("  *pz = %p,     *pz + 1 = %p
    ", *pz, *pz + 1);
        printf("       pz[0][0] = %d
    ", pz[0][0]);
        printf("         *pz[0] = %d
    ", *pz[0]);
        printf("          **zipppo = %d
    ", **pz);
        printf("       pz[2][1] = %d
    ",pz[2][1]);
        printf("*(*(pz +2) + 1) = %d
    ", *(*(pz +2) + 1));
        
        printf("%d
    ", *(pz + 1)[0]);  // 混合使用,通过指针表示法取二维数组第二个元素,通过数组表示取第一个元素。
        
        return 0;
    }

    输出

       pz = 0x7ffeefbff460,      pz + 1 = 0x7ffeefbff468

    pz[0] = 0x7ffeefbff460,   pz[0] + 1 = 0x7ffeefbff474

      *pz = 0x7ffeefbff460,     *pz + 1 = 0x7ffeefbff464

           pz[0][0] = 2

             *pz[0] = 2

              **zipppo = 2

           pz[2][1] = 3

    *(*(pz +2) + 1) = 3

    6

    pz代替了zippo的全部功能

    10.7.2 指针的兼容性

    不同类型的指针不能赋值,类型一定要相等。

    书中示例了二级解引,确实看到了指针让人头痛的方面。

    10.7.3 函数和多维数组

    编写处理二维数组的函数,要正确的理解写出声明函数的形参。

    可以通过前面初始化类型的写法

    int (* pr)[2]

    也可以

    void somefunction(int pt[][4])

    示例代码

    #include <stdio.h>
    #define ROWS 3
    #define COLS 4
    void sum_rows(int ar[][COLS], int rows);    // 待参数名
    void sum_cols(int [][COLS], int);       // 不带参数名
    int sum2d(int (* ar)[COLS], int rows);
    
    int main(void)
    {
        int junk[ROWS][COLS] = {
            {2, 4, 6, 8},
            {3, 5, 7, 9},
            {12, 10 ,8 ,6},
        };
        
        sum_rows(junk, ROWS);
        sum_cols(junk, ROWS);
        printf("Sum of all elemets = %d
    ", sum2d(junk, ROWS));
        
        return 0;
    }
    
    void sum_rows(int ar[][COLS], int rows)       // 每一行求和
    {
        int r;
        int c;
        int tot;
        for (r= 0; r < rows; r++) {
            tot = 0;
            for (c = 0; c < COLS; c++) {
                tot += ar[r][c];
            }
            printf("row %d: sum = %d
    ", r, tot);
        }
    }
    
    void sum_cols(int ar[][COLS], int rows)
    {
        int r;
        int c;
        int tot;
        
        for (c = 0; c < COLS; c++) {
            tot = 0;
            for (r = 0; r < rows; r++) {
                tot += ar[r][c];
            }
            printf("col %d: sum = %d
    ",c, tot);
        }
    }
    
    int sum2d(int ar[][COLS], int rows)
    {
        int r;
        int c;
        int tot = 0;
        for (c = 0; c < COLS; c++)
            for (r = 0; r < rows; r++) {
                tot += ar[r][c];
            }
        return tot;
    }

    输出

    row 0: sum = 20

    row 1: sum = 24

    row 2: sum = 36

    col 0: sum = 17

    col 1: sum = 19

    col 2: sum = 21

    col 3: sum = 23

    Sum of all elemets = 80

    Program ended with exit code: 0

    定义数组的时候,COLS与ROWS都用了常量。在defina的时候定义。

    10.8变长数组(VLA)

     对于数组的定义,前面应该讲过必须通过defina定义常量,但c99新增了变长数组(variable-length array),允许使用变量表示数组的维度。

    比如

    #include <stdio.h>
    
    int main(void)
    {
        int quartes = 4;
        int regions = 5;
        double sales[regions][quartes];
        
        return 0;
    }

    声明一个二维变长数组函数

    int  sum(int rows, int cols, int ar[rows][cols]);

    C99与C11标准规定,可以省略形参名,那只能通过星号来代替省略的维度

    int sum2d(int, int, int ar[*][*]);

    但函数的定义,头部还是跟第一种声明一样。

    一个以变长数组作为形参的函数既可以处理传统C数组,也可处理变长数组。

    示例代码

    #include <stdio.h>
    #define ROWS 3
    #define COLS 4
    int sum2d(int rows, int cols, int ar[rows][cols]);
    
    int main(void)
    {
        int i, j;
        int rs =3;
        int cs = 10;
        int junk[ROWS][COLS] = {
            {2, 4, 6, 8},
            {3, 5, 7, 9},
            {12, 10, 8, 6}
        };
        
        int morejunk[ROWS-1][COLS+2] = {
            {20, 30, 40, 50, 60, 70},
            {5, 6, 7, 8, 9, 10}
        };
        
        int varr[rs][cs];
        
        for (i = 0 ; i < rs; i++) {
            for (j = 0; j < cs; j++) {
                varr[i][j] = i * j + j;
            }
        }
        printf("3x5 array
    ");
        printf("Sum of all elements = %d
    ", sum2d(ROWS, COLS, junk));
        
        printf("2x6 array
    ");
        printf("Sum of all elements = %d
    ", sum2d(ROWS-1, COLS+2, morejunk));
        
        printf("3x10 array
    ");
        printf("Sum of all elements = %d
    ", sum2d(rs, cs, varr));
        
        
        return 0;
    }
    
    
    int sum2d(int rows, int cols, int ar[rows][cols]){
        int r, c;
        int tot = 0;
        for (r = 0; r < rows; r++) {
            for (c = 0; c < cols; c++) {
                tot += ar[r][c];
            }
        }
        return tot;
    }

    输出

    3x5 array

    Sum of all elements = 80

    2x6 array

    Sum of all elements = 315

    3x10 array

    Sum of all elements = 270

    Program ended with exit code: 0

    10.9 复合字面量

  • 相关阅读:
    Unity --- sharedMaterial 、material
    lua --- Module
    lua --- 点号 和 冒号
    lua --- __newindex 的使用规则
    DirectX之顶点法线的计算
    DirectX学习之第一个可运行的工程
    java--select*
    java--Servlet做控制器实现代码和UI分离
    java--JSTL取代%
    java--entity层的引入
  • 原文地址:https://www.cnblogs.com/sidianok/p/14166033.html
Copyright © 2011-2022 走看看