• 小朋友学C语言(8)


    求圆周率

     

    (一)圆周率简介

    圆周率(Pi)是圆的周长与直径的比值,一般用希腊字母π表示,是一个在数学及物理学中普遍存在的数学常数。π也等于圆形之面积与半径平方之比。是精确计算圆周长、圆面积、球体积等几何形状的关键值。
    圆周率是一个无理数,即无限不循环小数。在日常生活中,通常都用3.14代表圆周率去进行近似计算。而用十位小数3.141592654便足以应付一般计算。即使是工程师或物理学家要进行较精密的计算,充其量也只需取值至小数点后几百个位。

    (二)计算公式

    1965年,英国数学家约翰·沃利斯(John Wallis)出版了一本数学专著,其中他推导出一个公式,发现圆周率等于无穷个分数相乘的积。2015年,罗切斯特大学的科学家们在氢原子能级的量子力学计算中发现了圆周率相同的公式:
    pi/4 = 1/1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 + ……

    (三)利用公式编程求圆周率的值

    #include <stdio.h>
    #include <math.h>
    
    int main()
    {
        float pi = 0;
        int sign = 1;       // 正负符号 
        float deno = 1;     // 分母
        float item = 1;     // 项 
        // fabs是求绝对值的函数,在math.h中声明,在math.c中定义
        // 1e-6中的"-"左右两侧不能有空格;等价于0.000001。同理1e-3等价于0.001 
        while(fabs(item) >= 1e-6)
        {
            pi += item;
            sign = -sign;   // 根据公式,每计算一项,就得变动一次正负号 
            deno +=2;       // 分母每次都自加2 
            item = sign / deno; // 第几项的值,用于下一轮计算 
        }
        
        pi = 4 * pi;
        printf("pi = %f", pi);
        
        return 0;
    }
    

    运行结果:

    pi = 3.141594
    

    注意,这里是精确到小数点后六位,这意味着小数点后的最后一位数字可能是不准确的。

    三目运算符

    三目运算符(ternary operator),又称条件运算符、三元运算符,是计算机语言(c,c++,java等)的重要组成部分。它是唯一有3个操作数的运算符。
    三目运算符的形式为:

    <表达式1> ? <表达式2> : <表达式3>
    

    这里先对表达式1进行判断,假如表达式1为真,则执行表达式2;假如表达式1假,则执行表达3。

    例1:输入两个数,求最大的数

    #include <stdio.h>
    
    int main()
    {
        int num1, num2, max;
        printf("Please input 2 numbers, separated by space: ");
        scanf("%d %d", &num1, &num2);
        
        // 以0或负数作为循环结束的条件 
        while(num1 > 0 && num2 > 0)
        {
            max = num1 > num2 ? num1 : num2;
            printf("max = %d
    ", max);
            printf("Please input 2 numbers, separated by space: ");
            scanf("%d %d", &num1, &num2);
        }
        
        return 0;
    }
    

    运行结果:

    Please input 2 numbers, separated by space: 3 2
    max = 3
    Please input 2 numbers, separated by space: 5 10
    max = 10
    Please input 2 numbers, separated by space: 0 1 
    Loop end! Program will be finished!

    main函数的参数

    C/C++中的main()函数,可以带形式参数,也可以不带形式参数。这两种形式分别为:
    1)int main()
    2)int main(int argc, char *argv[]) 或者 int main(int argc, char **argv)
    因为main()函数通常是由操作系统调用的,所以平时写程序,基本上不用带参数。但是从学习的角度来看,了解一下参数也是有益处的。

    第一个参数,argc是argument count的缩写,表示“参数个数”。
    第二个参数,argv[]是argument vector的缩写,表示指向字符串(即字符数组)的指针数组。咱们之前学过指针,那么指针数组是什么意思呢?顾名思义,指针数组是表示一组指针。
    指向字符串的指针数组就表示为:有一组指针,这组指针里的每个指针,都指向了一个字符串。

    下面用一个具体的程序,来帮助理解。这个程序是在Win 10系统下用Dev C++编的。

    #include <stdio.h>
    
    int main(int argc, char *argv[]) 
    {
        printf("There are %d arguments in total
    ", argc);
        
        int i;
        for(i = 0; i < argc; i ++)
        {
            printf("Argument %d = %s
    ", i+1, argv[i]);
        }
        
        return 0;
    }
    

    程序编完之后保存,但不要编译,可以看到源文件有两个:


     
    1.png

    点击“编译”按纽后,多了test.o和Test.exe文件。


     
    2.png

    test.o为目标文件,这里的o即为Object的缩写。
    test.o进一步转化为Test.exe。Test.exe是Windows操作系统下的可执行文件。exe是Executable的缩写。


     
    3.png

    点击“Run”按纽,实际上就是运行Test.exe文件


     
    4.png

    运行结果为


     
    5.png

    exe文件后面是可以带参数的。下面,用另一种方式运行Test.exe,并且后面带上参数。
    打开命令行窗口,输入F:C_ProjectsTest.exe how are you?
    这种形式是可执行文件后面带了三个参数,以分隔号来表示。


     
    6.png

    如果不加任何参数,在命令行窗口执行exe文件,就相当于点击编译器里的“Run”按纽


     



    原码、反码、补码

    一、机器数和真值

    在学习原码, 反码和补码之前, 需要先了解机器数和真值的概念.

    1. 机器数

    一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1.

    比如,十进制中的数 +3 ,计算机字长为8位,转换成二进制就是00000011。如果是 -3 ,就是 10000011 。
    那么,这里的 00000011 和 10000011 就是机器数。

    2. 真值

    因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如上面的有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(10000011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。

    例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1

    二、原码, 反码, 补码的基础概念和计算方法

    在探求为何机器要使用补码之前, 让我们先了解原码, 反码和补码的概念.对于一个数, 计算机要使用一定的编码方式进行存储. 原码, 反码, 补码是机器存储一个具体数字的编码方式.

    1. 原码

    原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:
    [+1]原 = 0000 0001
    [-1]原 = 1000 0001

    第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:
    [1111 1111 , 0111 1111]

    [-127 , 127]

    原码是人脑最容易理解和计算的表示方式.

    2. 反码

    反码的表示方法是:
    正数的反码是其本身
    负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.

    [+1] = [00000001]原 = [00000001]反
    [-1] = [10000001]原 = [11111110]反

    可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算.

    3. 补码

    补码的表示方法是:
    正数的补码就是其本身
    负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

    [+1] = [00000001]原 = [00000001]反 = [00000001]补
    [-1] = [10000001]原 = [11111110]反 = [11111111]补

    对于负数, 补码表示方式也是人脑无法直观看出其数值的. 通常也需要转换成原码在计算其数值.

    三、为何要使用原码, 反码和补码

    现在我们知道了计算机可以有三种编码方式表示一个数. 对于正数因为三种编码方式的结果都相同:
    [+1] = [00000001]原 = [00000001]反 = [00000001]补
    所以不需要过多解释.

    但是对于负数:
    [-1] = [10000001]原 = [11111110]反 = [11111111]补
    可见负数的原码, 反码和补码是完全不同的.

    既然原码才是被人脑直接识别并用于计算表示方式, 为何还会有反码和补码呢?

    首先, 因为人脑可以知道第一位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. (真值的概念在本文最开头). 但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.

    于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码:

    计算十进制的表达式: 1-1=0
    1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2

    如果用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数.

    为了解决原码做减法的问题, 出现了反码:

    计算十进制的表达式: 1-1=0
    1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0
    结果为-0,这是很怪异的。虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0.

    于是补码的出现, 解决了0的符号以及两个编码的问题:
    1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
    (这里进行加法运算后,最右边的1因为超过存储限制而被截断了)

    这时得到的值就是0了,而不是反码运算中的-0。
    并且可以用[1000 0000]表示-128

    使用补码, 不仅仅修复了0存在两个编码的问题, 而且还能够多表示一个最低数.
    所以,使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127].

    四、原码,反码, 补码的再深入

    计算机巧妙地把符号位参与运算, 并且将减法变成了加法, 背后蕴含了怎样的数学原理呢?

    将钟表想象成是一个1位的12进制数. 如果当前时间是6点, 我希望将时间设置成4点, 需要怎么做呢?我们可以:

    1. 往回拨2个小时: 6 - 2 = 4
    2. 往前拨10个小时: (6 + 10) mod 12 = 4
    3. 往前拨10+12=22个小时: (6+22) mod 12 =4
      2,3方法中的mod是指取模操作, 16 mod 12 =4 即用16除以12后的余数是4.
      所以钟表往回拨(减法)的结果可以用往前拨(加法)替代!

    现在的焦点就落在了如何用一个正数, 来替代一个负数.

    首先介绍一个数学中相关的概念:
    同余的概念
    两个整数a,b,若它们除以整数m所得的余数相等,则称a,b对于模m同余
    记作 a ≡ b (mod m)
    读作 a 与 b 关于模 m 同余。

    举例说明:
    4 mod 12 = 4
    16 mod 12 = 4
    28 mod 12 = 4
    所以4, 16, 28关于模 12 同余.

    负数取模

    正数进行mod运算是很简单的. 但是负数呢?
    下面是关于mod运算的数学定义:

    x mod y = x - y * ⌊x/y⌋
    

    注:数学里,符号⌊⌋表示向下取整,比如⌊2.36⌋ = 2, ⌊-2.36⌋ = -3
    符号⌈⌉表示向上取整,比如⌈2.36⌉ = 3, ⌈-2.36⌉ = -2

    例子:-3 mod 2
    -3 mod 2
    = -3 - 2 * ⌊-3/2⌋
    = -3 - 2 * ⌊-1.5⌋
    = -3 - 2 * (-2)
    = -3 + 4 = 1

    所以:
    (-2) mod 12 = 12-2=10
    (-4) mod 12 = 12-4 = 8
    (-5) mod 12 = 12 - 5 = 7

    再回到时钟的问题上:
    回拨2小时 = 前拨10小时
    回拨4小时 = 前拨8小时
    回拨5小时= 前拨7小时
    注意, 这里发现的规律!

    结合上面学到的同余的概念.实际上:
    (-2) mod 12 = 10
    10 mod 12 = 10
    -2与10是同余的.

    (-4) mod 12 = 8
    8 mod 12 = 8
    -4与8是同余的.

    另外,我们观察到
    7 = 7 mod 12
    (-2) = 10 mod 12
    7 -2 = (7 + 10 ) mod 12

    接下来回到二进制的问题上, 看一下: 2-1=1的问题.
    2-1=2+(-1) = [0000 0010]原 + [1000 0001]原= [0000 0010]反 + [1111 1110]反
    先到这一步, -1的反码表示是1111 1110,数值上为-126。不考虑符号的话,数值为126

    发现有如下规律:
    (2 - 1) mod 127 = 126
    (2 + 126) mod 127 = 126
    2-1 与 2+126的余数结果是相同的! 而这个余数, 正式我们的期望的计算结果: 2-1=1

    所以说一个数的反码, 实际上是这个数对于一个模的同余数. 而这个模就是我们的二进制所能表示的最大值!
    这就和钟表一样, 转了一圈后总能找到在可表示范围内的一个正确的数值!
    而2+126很显然相当于钟表转过了一轮, 而因为符号位是参与计算的, 正好和溢出的最高位形成正确的运算结果.

    既然反码可以将减法变成加法, 那么现在计算机使用的补码呢? 为什么在反码的基础上加1, 还能得到正确的结果?

    2-1=2+(-1) = [0000 0010]原 + [1000 0001]原 = [0000 0010]补 + [1111 1111]补
    如果把[1111 1111]当成原码, 去除符号位, 则:
    [0111 1111]原 = 127
    其实, 在反码的基础上+1, 只是相当于增加了模的值:
    (-1) mod 128 = 127
    127 mod 128 = 127
    2-1 = 2+127 (mod 128)



    宏定义

    一、什么是宏定义

    宏定义是C/C++提供的预处理功能中的一种。
    宏定义又称为宏代换、宏替换,简称“宏”。
    格式:

    #define 标识符 字符串
    

    其中的“标识符”就是所谓的符号常量,也称为“宏名”。
    “字符串”可以是常数、表达式、格式串等。

    预处理(预编译)工作也叫做宏展开:将宏名替换为字符串。
    掌握"宏"概念的关键是“换”。一切以换为前提、做任何事情之前先要换,准确理解之前就要“换”

    二、为什么要使用宏定义

    宏定义的作用,主要是为了避免重复定义。
    什么是重复定义呢?比如有个C或C++的项目,多个地方会用到圆周率。圆周率PI是个无限不循环小数,如果项目中的有些地方取值3.14,有些地方取值3.14159,有些地方取值3.1415926535,显然会出现混乱。这个时候,就可以用宏定义来统一PI的值,然后无论是哪行代码用到了圆周率,都要使用PI,而不是具体的数字。

    三、例子

    例1

    #include<stdio.h>
    #define PI 3.14159
    
    int main()
    {
        float r = 1;
        float s = PI * r * r;
        printf("s = %f", s);
        
        return 0;
    }
    

    运行结果

    S = 3.141590
    

    例2

    #include<stdio.h>
    #define DOUBLE(x) x + x
    
    int main()
    {
        int a = 4;
        printf("min = %d
    ", DOUBLE(a));
        
        return 0;
    }
    

    运行结果

    8
    

    例3

    #include<stdio.h>
    #define DOUBLE(x) x + x
    
    int main()
    {
        int a = 4;
        int b = 5 * DOUBLE(a);
        printf("%d
    ", b);
        
        return 0;
    }
    

    预期运行结果
    5 * (4 + 4) = 40

    实际运行结果

    24
    

    原因:
    宏只是做简单替换,5 * DOUBLE(4) = 5 * 4 + 4 = 24

    改正方法:宏后面的式子加括号

    #define DOUBLE(x) (x + x)
    

    例4

    #include<stdio.h>
    #define DOUBLE(x) (x + x)
    
    int main()
    {
        int a = 4;
        int b = 5 * DOUBLE(a);
        printf("%d
    ", b);
        
        return 0;
    }
    

    运行结果

    40
    

    例5

    #include<stdio.h>
    #define SQUARE(x) (x * x)
    
    int main()
    {
        int a = 2;
        int b = 3;
        int c =  SQUARE(a + b);
        printf("%d
    ", c);
        
        return 0;
    }
    

    预期运行结果
    (2 + 3) * (2 + 3) = 25

    实际运行结果

    11
    

    原因分析:
    宏只是作简单的替换,SQUARE(a + b) = (a + b * a + b) = (2 + 3 * 2 + 3) = 11

    改正方法:将参数x用括号括起来

    #define SQUARE(x) ((x) * (x))
    

    例6

    #include<stdio.h>
    #define SQUARE(x) ((x) * (x))
    
    int main()
    {
        int a = 2;
        int b = 3;
        int c =  SQUARE(a + b);
        printf("%d
    ", c);
        
        return 0;
    }
    

    运行结果

    25
    

    例7:用undef取消宏定义

    #include<stdio.h>
    #define MAX 100
    #undef MAX
    
    int main()
    {
        int a = MAX;
        printf("a = %d", a);
        
        return 0;
    }
    

    编译的时候报错,提示MAX没有没定义。

    小结

    在用宏定义时,宏参数和整个宏都要用括号括起来
    比如,定义求最小值时,下面的定义都是不严谨的,都只能得0分

    #define MIN(a, b) a > b ? b : a
    #define MIN(a, b) (a > b ? b : a)
    #define MIN(a, b) (a) > (b) ? (b) : (a)
    

    正确的定义如下

    #define MIN(a, b) ((a) > (b) ? (b) : (a))
    

    四、说明

    (1)宏名一般用大写
    (2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。例如:数组大小常用宏定义
    (3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。
    (4)宏定义末尾不加分号;
    (5)宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。
    (6)可以用#undef命令终止宏定义的作用域
    (7)宏定义不可以嵌套
    (8)字符串" "中永远不包含宏
    (9)宏定义不分配内存,变量定义分配内存。
    (10)宏定义不存在类型问题,它的参数也是无类型的

    五、缺陷

    不少语言没有宏定义,比如java和python。
    这是因为宏定义有一些缺点,有些语言干脆就不设计宏定义了。

    宏定义的缺点有:
    (1)无法对宏定义中的变量进行类型检查。

    (2)如果使用不当,会带来阅读上的困难。
    比如#define ABC(a, b) ((a) > (b) ? (b) : (a))
    不仔细看,还真看不出ABC(a, b)实际上就是求最小值
    再比如#define MAX(a, b) ((a) > (b) ? (b) : (a))
    乍一看,以为MAX求的是最大值,但看了定义之后才明白是求最小值。写反了都。

    如果程序里大量使用了宏定义,会使程序的阅读性严重下降。

    结构体

    一、结构体

    C语言中的数组存储的是相同数据类型的数据项,结构体是 C 编程中一种用户自定义的可用的数据类型,它允许你存储不同类型的数据项。

    结构体用于表示一条记录,假设你想要跟踪图书馆中书本的动态,你可能需要跟踪每本书的下列属性:

    Title
    Author
    Subject
    Book ID
    

    我们可以定义结构:

    struct Book
    {
       char  title[50];
       char  author[50];
       char  subject[100];
       int   book_id;
    };
    

    为了访问结构的成员,我们使用成员访问运算符(.)。
    成员访问运算符是结构体变量名称和我们要访问的结构体成员之间的一个句号。
    你可以使用 struct 关键字来定义结构类型的变量。
    下面的实例演示了结构体的用法:

    例1

    #include <stdio.h>
    #include <string.h>
    
    struct Book
    {
       char  title[50];
       char  author[50];
       char  subject[100];
       int   book_id;
    };
    
    int main( )
    {
       struct Book book1;        /* 声明 book1,类型为 Book */
       struct Book book2;        /* 声明 book2,类型为 Book */
    
       /* 给book1赋值 */
       strcpy( book1.title, "西游记");
       strcpy( book1.author, "吴承恩");
       strcpy( book1.subject, "文学");
       book1.book_id = 1001;
    
       /* 给book2赋值 */
       strcpy( book2.title, "野性的呼唤");
       strcpy( book2.author, "杰克伦敦");
       strcpy( book2.subject, "文学");
       book2.book_id = 1002;
    
       /* 输出 book1 信息 */
       printf( "book 1 title : %s
    ", book1.title);
       printf( "book 1 author : %s
    ", book1.author);
       printf( "book 1 subject : %s
    ", book1.subject);
       printf( "book 1 book_id : %d
    ", book1.book_id);
    
       /* 输出 book2 信息 */
       printf( "book 2 title : %s
    ", book2.title);
       printf( "book 2 author : %s
    ", book2.author);
       printf( "book 2 subject : %s
    ", book2.subject);
       printf( "book 2 book_id : %d
    ", book2.book_id);
    
       return 0;
    }
    

    运行结果:

    book 1 title : 西游记
    book 1 author : 吴承恩
    book 1 subject : 文学
    book 1 book_id : 1001
    book 2 title : 野性的呼唤
    book 2 author : 杰克伦敦
    book 2 subject : 文学
    book 2 book_id : 1002
    

    二、结构体做为函数参数

    你可以把结构作为函数参数,传参方式与其他类型的变量或指针类似。你可以使用上面实例中的方式来访问结构体变量

    例2

    #include <stdio.h>
    #include <string.h>
    
    struct Book
    {
       char  title[50];
       char  author[50];
       char  subject[100];
       int   book_id;
    };
    
    /* 函数声明 */
    void printBook( struct Book book );
    
    int main( )
    {
       struct Book book1;        /* 声明 book1,类型为 Book */
       struct Book book2;        /* 声明 book2,类型为 Book */
    
       /* 给book1赋值 */
       strcpy( book1.title, "西游记");
       strcpy( book1.author, "吴承恩");
       strcpy( book1.subject, "文学");
       book1.book_id = 1001;
    
       /* 给book2赋值 */
       strcpy( book2.title, "野性的呼唤");
       strcpy( book2.author, "杰克伦敦");
       strcpy( book2.subject, "文学");
       book2.book_id = 1002;
    
       /* 输出 book1 信息 */
       printBook( book1 );
    
       /* 输出 book2 信息 */
       printBook( book2 );
    
       return 0;
    }
    
    void printBook( struct Book book )
    {
       printf( "Book title : %s
    ", book.title);
       printf( "Book author : %s
    ", book.author);
       printf( "Book subject : %s
    ", book.subject);
       printf( "Book book_id : %d
    ", book.book_id);
    }
    

    三、指向结构体的指针

    你可以定义指向结构的指针,方式与定义指向其他类型变量的指针相似,如下所示:

    struct Book *pointer;
    

    现在,你可以在上述定义的指针变量中存储结构变量的地址。为了查找结构变量的地址,请把 & 运算符放在结构名称的前面,如下所示:

    pointer = &book1;
    

    为了使用指向该结构的指针访问结构的成员,你必须使用 -> 运算符,如下所示:

    pointer->title;
    

    让我们使用结构指针来重写上面的实例,这将有助于你理解结构体指针的概念:

    例3

    #include <stdio.h>
    #include <string.h>
    
    struct Book
    {
       char  title[50];
       char  author[50];
       char  subject[100];
       int   book_id;
    };
    
    /* 函数声明 */
    void printBook( struct Book *book );
    
    int main( )
    {
       struct Book book1;        /* 声明 book1,类型为 Book */
       struct Book book2;        /* 声明 book2,类型为 Book */
    
       /* 给book1赋值 */
       strcpy( book1.title, "西游记");
       strcpy( book1.author, "吴承恩");
       strcpy( book1.subject, "文学");
       book1.book_id = 1001;
    
       /* 给book2赋值 */
       strcpy( book2.title, "野性的呼唤");
       strcpy( book2.author, "杰克伦敦");
       strcpy( book2.subject, "文学");
       book2.book_id = 1002;
    
       /* 输出 book1 信息 */
       printBook( &book1 );
    
       /* 输出 book2 信息 */
       printBook( &book2 );
    
       return 0;
    }
    
    void printBook( struct Book *book )
    {
       printf( "Book title : %s
    ", book->title);
       printf( "Book author : %s
    ", book->author);
       printf( "Book subject : %s
    ", book->subject);
       printf( "Book book_id : %d
    ", book->book_id);
    }
     

    typedef

    一、typedef

    C 语言提供了 typedef 关键字,你可以使用它来为类型取一个新的名字。
    下面的实例为单字节数字定义了一个新类型 BYTE:

    typedef unsigned char BYTE;
    

    在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char 的缩写,例如:

    BYTE  b1;
    

    按照惯例,定义时会用大写字母,以便提醒用户类型名称是一个自定义的名字。
    当然你也可以使用小写字母:

    typedef unsigned char byte;
    

    你也可以使用 typedef 来为用户自定义的数据类型取一个新的名字。例如,你可以对结构体使用 typedef 来定义一个新的数据类型名字,然后使用这个新的数据类型来直接定义结构体变量,如下:

    例1

    #include <stdio.h>
    #include <string.h>
    
    typedef struct Book
    {
       char  title[50];
       char  author[50];
       char  subject[100];
       int   book_id;
    } BK;
    
    int main( )
    {
       BK book1;        /* 声明 book1,类型为 BK,即struct Book */
    
       /* 给book1赋值 */
       strcpy( book1.title, "西游记");
       strcpy( book1.author, "吴承恩");
       strcpy( book1.subject, "文学");
       book1.book_id = 1001;
    
       /* 输出 book1 信息 */
       printf( "book 1 title : %s
    ", book1.title);
       printf( "book 1 author : %s
    ", book1.author);
       printf( "book 1 subject : %s
    ", book1.subject);
       printf( "book 1 book_id : %d
    ", book1.book_id);
    
       return 0;
    }
    

    运行结果:

    book 1 title : 西游记
    book 1 author : 吴承恩
    book 1 subject : 文学
    book 1 book_id : 1001
    

    二、typedef与#define的区别

    #define是 C 指令,用于为各种数据类型定义别名,与 typedef 类似,但是它们有以下几点不同:
    1) typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 TRUE。
    2)typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。(预编译又称为预处理,是做些代码文本的替换工作。比如拷贝#include包含的文件代码,#define宏定义的替换等)

    下面是 #define 的最简单的用法:

    例2

    #include <stdio.h>
    
    #define TRUE  1     // 数值1的别名为TRUE
    #define FALSE 0     // 数值0的别名为FALSE
    
    int main( )
    {
       printf( "TRUE 的值: %d
    ", TRUE);
       printf( "FALSE 的值: %d
    ", FALSE);
    
       return 0;
    }
    

    运行结果:

    TRUE 的值: 1
    FALSE 的值: 0

    getchar与putchar

    例1

    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        char c1;
        scanf("%c", &c1);
    printf("%c", c1);
    
        return 0;
    }
    

    运行结果:

    x(输入)
    x(输出)
    
     
    1.png

    例2

    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        char c2;
        c2 = getchar();
        putchar(c2);
    
        return 0;
    }
    

    运行结果:

    x(输入)
    x(输出)
    

    说明:
    (1)C语言输入单个字符有两种方式,scanf和getchar;对应的输出单个字符为printf和putchar。
    (2)scanf可以通过scanf(“%c%c%c…”, c1, c2, c2…)或scanf(“%s”, str)读取多个字符(字符串)
    getchar只能读取一个字符
    (3)getchar在输入回车键之前,输入的字符是被放到缓冲区(缓冲区是一块连续的内存区域,用来临时保存输入的字条)。当按下回车键时,gechar才会从stdio流(标准输入输出流)中读取第一个字符。所以即使你输入的是“abcde ”,被getchar读取的也只是第一个字符“a”。


    循环语句中continue和break的区别

    例1:continue

    #include <stdio.h>
    
    int main()
    {
        int i = 0;
        for(; i < 5; i++)
        {
            if(2 == i)
            {
                continue;
            }
    
            printf("i = %d
    ", i);
        }
    
        return 0;
    }
    

    运行结果:

    i = 0
    i = 1
    i = 3
    i = 4
    

    例2:break

    #include <stdio.h>
    
    int main()
    {
        int i = 0;
        for(; i < 5; i++)
        {
            if(2 == i)
            {
                break;
            }
    
            printf("i = %d
    ", i);
        }
    
        return 0;
    }
    

    运行结果:

    i = 0
    i = 1
    

    从以上两个例子可以看出,continue是结束本次循环,继续执行下次循环。
    break则是结束当前整个循环。


    关注微信公众号请扫二维码



  • 相关阅读:
    Windows系统架构
    UVa10006-Carmichael Numbers
    Android打开系统设置
    C语言与汇编“硬在哪里”——什么是面向硬件?
    javaSocket与C通信
    小智慧25
    sqlplus中显示sql执行计划和统计信息
    记一次修复被篡改的IE首页
    UIView的生命周期
    [置顶] 关于UBUNTU 12.04, 在THINKPAD E430C上WIFI连接不上的问题
  • 原文地址:https://www.cnblogs.com/alan-blog-TsingHua/p/9604097.html
Copyright © 2020-2023  润新知