• 数据类型和输入输出


    1.存放数据的小箱子——变量

    在《二进制思想以及数据的存储》一节中讲到:
    1. 计算机要处理的数据(诸如数字、文字、符号、图形、音频、视频等)是以二进制的形式存放在内存中的;
    2. 我们将8个比特(Bit)称为一个字节(Byte),并将字节作为最小的可操作单元。
     
    编程中我们会经常处理各种数据,与内存打交道。我们不妨先从最简单的整数说起,看看它是如何放到内存中去的。
     
    现实生活中我们会找一个小箱子来存放物品,一来显得不那么凌乱,二来方便以后找到。计算机也是这个道理,我们需要先在内存中找一块区域,规定用它来存放整数,并起一个好记的名字,方便以后查找。这块区域就是“小箱子”,我们可以把整数放进去了。
     
    C语言中这样在内存中找一块区域:
    1. int a;
    int 又是一个新单词,它是 Integer 的简写,意思是整数。a 是我们给这块区域起的名字;当然也可以叫其他名字,例如 abc、mn123 等。
     
    这个语句的意思是:在内存中找一块区域,命名为 a,用它来存放整数。
    注意 int 和 a 之间是有空格的,它们是两个词。也注意最后的分号,int a表达了完整的意思,是一个语句,要用分号来结束。
    不过int a;仅仅是在内存中找了一块可以保存整数的区域,那么如何将 123、100、999 这样的数字放进去呢?
     
    C语言中这样向内存中放整数:
    1. a=123;
    = 是一个新符号,它在数学中叫“等于号”,例如 1+2=3,但在C语言中,这个过程叫做赋值(Assign)。赋值是指把数据放到内存的过程。
     
    把上面的两个语句连起来:
    1. int a;
    2. a=123;

    就把 123 放到了一块叫做 a 的内存区域。你也可以写成一个语句:
    1. int a=123;
    a 中的整数不是一成不变的,只要我们需要,随时可以更改。更改的方式就是再次赋值,例如:
    1. int a=123;
    2. a=1000;
    3. a=9999;
    第二次赋值,会把第一次的数据覆盖(擦除)掉,也就是说,a 中最后的值是9999,123、1000 已经不存在了,再也找不回来了。
     
    因为 a 的值可以改变,所以我们给它起了一个形象的名字,叫做变量(Variable)。
     
    int a;创造了一个变量 a,我们把这个过程叫做变量定义。a=123;把 123 交给了变量 a,我们把这个过程叫做给变量赋值;又因为是第一次赋值,也称变量的初始化,或者2.赋初值。
     
    你可以先定义变量,再初始化,例如:
    1. int abc;
    2. abc=999;
    也可以在定义的同时进行初始化,例如:
    1. int abc=999;
    这两种方式是等价的。

    2.在屏幕上显示整数——输出

    我们定义了一个变量 abc 并给它赋值,例如:
    1. int abc=100;
    现在我们希望运行一下,看看效果,怎么才能在屏幕上显示呢?
     
    puts 是 output string 的缩写,只能用来输出字符串,不能输出整数,我们需要用另外一种方法,那就是——printf。
     
    printf 比 puts 更加强大,不仅可以输出字符串,还可以输出整数、小数、单个字符等;输出格式也可以自己定义,例如:
    1. 以二进制、八进制、十六进制形式输出;
    2. 要求输出的数字占 n 个字符的位置;
    3. 控制小数的位数。
     
    printf 是 print format 的缩写,意思是“格式化打印”。这里所谓的“打印”就是在屏幕上显示内容,与“输出”的含义相同,所以我们一般称 printf 是用来格式化输出的。
     
    先来看一个简单的例子:
    1. printf("C语言中文网");
    这个语句可以在屏幕上显示“C语言中文网”,与puts("C语言中文网");的效果完全相同。
     
    输出变量 abc 的值:
    1. int abc=999;
    2. printf("%d", abc);
    这里就比较有趣了。先来看%d,d 是 decimal 的缩写,意思是十进制数,%d 表示以十进制的形式输出。输出什么呢?输出 abc 的值。%d 与 abc 是对应的,也就是说,会用 abc 的值来替换 %d。
     
    再来看个复杂点的:
    1. int abc=999;
    2. printf("The value of abc is %d !", abc);
    会在屏幕上显示:
    The value of abc is 999 !
     
    你看,字符串 "The value of abc is %d !" 中的 %d 被替换成了 abc 的值,其他字符没有改变。这说明 %d 比较特殊,不会原样输出,会被替换成对应的变量的值。
     
    再来看:
    1. int a=100;
    2. int b=200;
    3. int c=300;
    4. printf("a=%d, b=%d, c=%d", a, b, c);
    会在屏幕上显示:
    a=100, b=200, c=300
     
    再次证明了 %d 与后面的变量是一一对应的,第一个 %d 对应第一个变量,第二个 %d 对应第二个变量……
     
    我们把代码补充完整,体验一下:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int a=100;
    6. int b=200;
    7. int c=300;
    8. printf("a=%d, b=%d, c=%d ", a, b, c);
    9. system("pause");
    10. return 0;
    11. }
    输出结果:
    a=100, b=200, c=300
    请按任意键继续. . .
     
    我们也可以不用变量,直接将数据输出:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. printf("The output: %d, %s ", 1234, "Think you");
    6. system("pause");
    7. return 0;
    8. }

    输出结果:
    The output: 1234, Think you
     
    s 是 string 的简写,%s 表示输出字符串。

    3.更多类型的数据——小数和字符

    int 可以用来表示整数,但是对小数却无能为力。请看下面的代码:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int a=1234.888;
    6. printf("a=%d ", a);
    7. system("pause");
    8. return 0;
    9. }
    输出结果:
    a=1234
    请按任意键继续. . .
     
    看,小数点后面的数字被丢弃了。

    ①.小数

    在C语言中,我们使用 float 来表示小数,例如:
    1. float a=123.888;
    2. float b=0.302;
    3. float c=98.0;

    float 是“浮点数”的意思,我们暂时可以认为浮点数就是小数,不必深究,不影响我们的学习。
     
    输出浮点数用%f,请看下面的例子:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. float a=123.888;
    6. float b=0.302;
    7. float c=98.0;
    8. float d=1.23002398;
    9. float e=123; // 整数
    10. printf("a=%f b=%f c=%f d=%f e=%f ", a, b, c, d, e);
    11. system("pause");
    12. return 0;
    13. }
    输出结果:
    a=123.888000
    b=0.302000
    c=98.000000
    d=1.230024
    e=123.000000
     
    %f 默认保留六位小数,不足六位以 0 补齐,超过六位按四舍五入截断。也可以将整数赋值给 float 类型变量。

    ②.字符

     
    我们前面提到过字符串,它是多个字符的集合,例如 "abc123";当然也可以只包含一个字符,例如 "a"、"1"。
     
    但是为了使用方便,我们可以用 char 来专门表示一个字符,例如:
    1. char a='1';
    2. char b='$';
    3. char c='X';
    4. char d=' '; // 空格也是一个字符
    char 类型的字符用单引号 ' ' 来包围,而字符串用双引号" "包围,大家不要混淆,否则编译会出错。
    数据类型
     
    整数、浮点数、字符都是C语言中会使用到的数据,它们有一个专业的称呼,叫做数据类型(Data Type)。例如:
    1. int a=1;
    2. float b=10.09;
    3. char c='@';
    我们就可以说 a、b、c 的数据类型不同,a 是整型变量,b 是浮点型变量,c 是字符型变量。
     
    整型、浮点型、字符型是C语言中的基本数据类型,必须要掌握,后续我们还会介绍更多数据类型,甚至可以自己定义一种新的类型。

    注意:字符串比较特殊,C语言中没有这个数据类型,不能使用string a="abc123";这种方式来定义字符串,后面我们会讲解为什么。

    ③.字符与整数

     
    先看下面一段代码:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. char a=69;
    6. char b=0x46; // 十六进制数以 0x 或 0X开头
    7. char c='G';
    8. int d=72;
    9. printf("a1=%c, a2=%d, b=%c, c=%d, d=%c ", a, a, b, c, d);
    10. system("pause");
    11. return 0;
    12. }
    输出结果:
    a1=E, a2=69, b=F, c=71, d=H
    代码第6行中,//及其后面的内容为注释(Comments)。注释用来对程序进行说明,编译器会忽略它,不去执行。
    在ASCII码表中,E、F、G、H 的值分别是 69、0x46 (十进制为 70)、71、72。给 char 变量一个整数,然后以 %c 输出,它会根据 ASCII 码表转换成对应的字符,如果以 %d 输出,那么还是整数。同样,一个 char 变量以 %d 输出,会转换成对应的 ASCII 码值。反过来,int 变量以 %c 输出,会转换成对应的字符。
     
    可以看出,ASCII 码将字符和整数联系起来了,你可以给 char 变量一个整数,也可以给 int 变量一个字符,它们可以相互转换。

    4.小箱子有多大——取值范围


    2015年05月20日,在纳斯达克上市的苹果公司市值达到了 7628.40 亿美元,是有史以来市值最高的公司,并且最有希望成为世界上第一个市值破万亿的公司。乔布斯辞世后,很多人质疑库克能不能再创辉煌,数据不会说谎,苹果公司市值已经翻倍,乔布斯没有选错接班人,iPhone 6 也顺应了大屏手机的趋势。
     
    那么,我们不妨将苹果公司的市值输出一下,看看是多长的一串数字:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int value=7628400000000;
    6. // AAPL 是苹果公司的股票代码
    7. printf("The market value of AAPL is %d ! ", value);
    8. system("pause");
    9. return 0;
    10. }
    输出结果:
    The market value of AAPL is 538082304 !
     
    让人惊讶的是,市值瞬间蒸发,只剩下 5.38 亿美元,与达内的市值相当,刚刚够上市的体量。
     
    这是因为,一般情况下 int 类型在内存中占用 4 个字节的空间,也就是32位,理论上所能表示的最大数是 2^32 - 1 = 0xFFFFFFFF = 4,294,967,296 ≈ 42.95 亿,7628.40 亿显然超出了它的范围,会发生溢出(Overflow)。就像水一样,多出的会流到木桶外面,如果数字超过32位,比如35位,那么就会有3位丢失,当然不能表示原来的值。
     
    我们来验证一下 int 的最大值:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int value=0xffffffff;
    6. printf("The maximum value of "int" is %d . ", value);
    7. system("pause");
    8. return 0;
    9. }
    运行结果:
    The maximum value of "int" is -1 .
     
    这和我们期望的结果不一样:我们期望输出 4294967296,但实际输出的却是八竿子打不着的 -1。
     
    这是因为,整数有正负之分,我们需要在32位中选择一位用来标明到底是正数还是负数,这一位就是最高位。也就是说,0~30位表示数值,31 位表示正负号。如下图所示
     

    注意:在编程语言中,计数往往是从0开始,例如字符串 "abc123",我们称第 0 个字符是 a,第 1 个字符是 b,第 5 个字符是 3。这和我们平时从 1 开始计数的习惯不一样,大家要慢慢适应,培养编程思维。
    如果我们希望能表示更大些的数值,也可以不使用符号位,而用关键字unsigned来声明变量,例如:
    1. // 下面的两种写法都是正确的,使用 unsigned 时可以不写 int
    2. unsigned int a=100;
    3. unsigned a=999;

    这样,就只能表示正数了。unsigned 类型的整数需要用%u来输出。
     
    我们来验证一下上面的说法:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int a=1234;
    6. unsigned a1=1234;
    7. int b=0x7fffffff;
    8. int c=0x80000000; // 0x80000000 = 0x7fffffff + 0x1
    9. int d=0xffffffff;
    10. unsigned e=0xffffffff;
    11. printf("a=%d, a(u)=%u ", a, a);
    12. printf("a1=%d, a1(u)=%u ", a1, a1);
    13. printf("b=%d, b(u)=%u ", b, b);
    14. printf("c=%d, c(u)=%u ", c, c);
    15. printf("d=%d, d(u)=%u ", d, d);
    16. printf("e=%d, e(u)=%u ", e, e);
    17. system("pause");
    18. return 0;
    19. }

    输出结果:
    a=1234, a(u)=1234
    a1=1234, a1(u)=1234
    b=2147483647, b(u)=2147483647
    c=-2147483648, c(u)=2147483648
    d=-1, d(u)=4294967295
    e=-1, e(u)=4294967295
     
    我们发现,无论变量声明为 int 还是 unsigned,只有当以 %u 格式输出时,才会作为无符号数处理;如果声明为 unsigned,却以 %d 输出,那么也是有符号数。
     
    为了更加灵活的表示数据,合理利用内存资源,C语言还使用另外两个关键字来表示整数,那就是 short 和 long。short 表示短整型,long 表示长整型,这里的“长短”是针对 int 来说的。C语言规定,short 的长度不能大于 int,long 的长度不能小于 int。
     
    unsigned表示有无符号位,short、int、long表示所占内存的大小,不要混淆,请看下面的例子:
    1. long int a=1234;
    2. long b=299;
    3. unsigned long int c=999;
    4. unsigned long d=98720;
    5. short int m=222;
    6. short n=2094;
    7. unsigned short int p=3099;
    8. unsigned int q=68893;

    值得一提的是:C语言是70年代的产物,那个时候以及后面的十几年,是8086的天下,计算机的软硬件都是16位的,按照C语言的规定,short 一般占2个字节,int 也占2个字节,long 占4个字节。
     
    现在的计算机已经甩开8086几条街,CPU、操作系统、编译器都已经支持到32位或者64位,所以 short 一般占2个字节,int 占 4 个字节,long 也占 4 个字节。
     
    你看,C语言为了兼容硬件,追求效率,对数据类型的规定不是那么严谨,short 不一定真的“短”,long 也不一定真的“长”,这增加了我们学习和开发的成本。
    在Java中不存在这样的情况,数据类型的长度是板上钉钉的,short 一定是 2 个字节,int 是 4 个字节,long 是 8 个字节,多么简单。但这是以牺牲效率为代价的。在编程语言中,简单和效率一直是相互矛盾的,几个语句搞定的代码往往效率不高,追求高效的代码往往需要自己实现,可能要几百行。

    ①.其他数据类型的长度

     
    现在的计算机软硬件配置都已经支持到32位或者64位,这里以及后面提到的数据类型长度,都只考虑现代计算机,不再关心古董机器——16位机。
     
    char 的长度始终都是1个字节。
     
    float 的长度为 4 个字节,是单精度浮点数。为了更加精确,表示更大或者更小的浮点数,可以使用 double。double 占用 8 个字节,是双精度浮点数。
     
    最后,我们来总结一下现代计算机中各种数据类型的长度。
    说  明
    字符型    
    短整型    
    整型    
    长整型    
    单精度浮点型
    双精度浮点型
    数据类型
    char
    shortintlongfloatdouble
    所占字节
    124448
    注意:unsigned 表示有无正负号,不会影响数据类型长度。
    sizeof
     
    sizeof 用来计算数据所占用的内存空间,以字节计。如果你忘记了某个数据类型的长度,可以用 sizeof 求得,请看下面的代码:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int m=290;
    6. float n=23;
    7. printf("int = %d ", sizeof(m));
    8. printf("float = %d ", sizeof(n));
    9. printf("double = %d ", sizeof(double));
    10. printf("A = %d ", sizeof('A'));
    11. printf("23 = %d ", sizeof(23));
    12. printf("14.67 = %d ", sizeof(14.67));
    13. system("pause");
    14. return 0;
    15. }

    输出结果:
    int = 4
    float = 4
    double = 8
    A = 1
    23 = 4
    14.67 = 8
     
    sizeof 的操作数既可以是变量、数据类型,还可以是某个具体的数据。细心的读者会发现,sizeof(14.67) 的结果是8,不错,因为小数默认的类型就是double。

    5.加减乘除运算

    C语言也可以进行加减乘除运算,但是运算符号与数学中的略有不同,见下表。

    加法
    减法    
    乘法
    除法    
    求余数
    数学
    +
    -
    ×
    ÷
    C语言+-*/%
    加号、减号与数学中的一样,乘号、除号不同,另外C语言还多了一个求余数的运算符。
     
    我们先来看一段代码:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int a=12;
    6. int b=100;
    7. float c=8.9;
    8. int m=a+b;
    9. float n=b*c;
    10. double p=a/c;
    11. int q=b%a;
    12. printf("m=%d, n=%f, p=%lf, q=%d ", m, n, p, q);
    13. system("pause");
    14. return 0;
    15. }

    输出结果:
    m=112, n=889.999962, p=1.348315, q=4

    b*c 的结果很明显是890,但是却输出一个近似数,这里涉及到浮点数的运算原理,并且实际开发中较少使用浮点数运算,所以这里我们不再深究。

    你也可以让数字直接参与运算:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int a=12;
    6. int b=100;
    7. float c=8.9;
    8. int m=a-b; // 变量参与运算
    9. int n=a+239; // 有变量也有数字
    10. double p=12.7*34.3; // 数字直接参与运算
    11. printf("m=%d, n=%f, p=%lf ", m, n, p);
    12. printf("m*2=%d, 6/3=%d, m*n=%ld ", m*2, 6/3, m*n);
    13. system("pause");
    14. return 0;
    15. }

    输出结果:
    m=-88, n=-0.000000, p=0.000000
    m*2=-176, 6/3=2, m*n=-22088
    注意:除数不能是 0,所以诸如int a=3/0;这样的语句是错误的。
    再来看一个例子:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int a=12;
    6. int b=10;
    7. printf("a=%d ", a);
    8. a=a+8;
    9. printf("a=%d ", a);
    10. a=a*b;
    11. printf("a=%d ", a);
    12. system("pause");
    13. return 0;
    14. }

    输出结果:
    a=12
    a=20
    a=200
     
    第一次输出 a 原来的值;a=a+8;相当于用a+8的值替换原来 a 的值,所以第二次输出 20;第三次用a*b的值替换第二次的值,所以是 200。
     
    注意:a=a+8;可以写成a+=8;,a=a*b;可以写成a*=b。这仅是一种简写,不会影响效率,其他运算符也可以这样写。

    ①.加一和减一

     
    一个整数自身加一可以这样写:
    1. a+=1;

    它等价于 a=a+1;。
     
    但是在C语言中还有一种更简单的写法,就是 a++; 或者++a; 。这种写法叫做自加或自增;意思很明确,就是自身加一。
     
    相应的,也有a--和--a,叫做自减,表示自身减一。
     
    ++和--分别称为自增和自减运算符。
     
    那么,++ 在前面和后面有区别吗?
     
    请看下面的例子:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int a=12;
    6. int a1=a++;
    7. int b=10;
    8. int b1=++b;
    9. printf("a=%d, a1=%d ", a, a1);
    10. printf("b=%d, b1=%d ", b, b1);
    11. system("pause");
    12. return 0;
    13. }

    输出结果:
    a=13, a1=12
    b=11, b1=11
     
    我们来分析一下:
    1) 执行a1=a++;,a 的值并不会立马加 1,而是先把 a 原来的值交给 a1,然后才能加 1。a 原来的值是 12,所以 a1 的值是 12;而 a 因为加 1,最终的值是 13。
     
    2) 执行b1=++b;,b 的值会立马加1,变成11,然后才将 b 的值交给 b1。因为此时 b 的值已经是11,所以 b1 的值自然也就是 11 了。
     
    可以看出:a1=a++;会先进行赋值操作,再进行自增操作;而b1=++b;会先进行自增操作,再进行赋值操作。
     
    所以,我们把诸如a++这样的操作叫做后自增,也就是先进行其他操作,再进行自增操作;而把++a叫做前自增,会先进行自增操作,再进行其他操作。
     
    相应的,a--叫做后自减,--a叫做前自减,意思与++相同。
    自增自减非常方便,后续编程中会经常用到,大家要注意区分。
    为了强化记忆,我们再来看一个自增自减的综合示例:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int a=12;
    6. int b=1;
    7. int c=a-(b--); // ①
    8. int d=(++a)-(--b); // ②
    9. printf("c=%d, d=%d ", c, d);
    10. system("pause");
    11. return 0;
    12. }

    输出结果:
    c=11, d=14
     
    我们来分析一下:
    1) 执行语句①时,会先进行a-b运算,结果是11,然后 b 再自减,就变成了 0,最后再将a-b的结果(也就是11)交给 c,所以 c 的值是 11。
     
    2) 执行语句②之前,b 的值已经变成 0。对于d=(++a)-(--b),a 会先自增,变成 13,然后 b 再自减,变成 -1,最后再进行13-(-1),结果是14,交给 d,所以 d 最终是 14。

    6.更加优美的C语言输出

    虽然我们已经熟悉了 printf,但是还没有把它发挥到极致,printf 可以有更加“炫酷”的输出。
     
    假如现在老师要求我们用C语言输出一个 4×4 的整数矩阵,为了增强阅读性,数字要对齐,怎么办呢?我们显然可以这样来做:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int a1=20, a2=345, a3=700, a4=22;
    6. int b1=56720, b2=9999, b3=20098, b4=2;
    7. int c1=233, c2=205, c3=1, c4=6666;
    8. int d1=34, d2=0, d3=23, d4=23006783;
    9. printf("%d %d %d %d ", a1, a2, a3, a4);
    10. printf("%d %d %d %d ", b1, b2, b3, b4);
    11. printf("%d %d %d %d ", c1, c2, c3, c4);
    12. printf("%d %d %d %d ", d1, d2, d3, d4);
    13. system("pause");
    14. return 0;
    15. }

    运行结果:
    20        345       700       22
    56720     9999      20098     2
    233       205       1         6666
    34        0         23        23006783
    矩阵一般在大学的《高等数学》中会讲到,m×n 的数字矩阵可以理解为把 m×n 个数字摆放成 m 行 n 列的样子。
    看,这是多么地自虐,要敲那么多空格,还要严格控制空格数,否则输出就会错位。
     
    类似的需求随处可见,整齐的格式会更加美观,让人觉得生动有趣。我们大可不必像上面一样,printf 可以更好的控制输出格式。
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int a1=20, a2=345, a3=700, a4=22;
    6. int b1=56720, b2=9999, b3=20098, b4=2;
    7. int c1=233, c2=205, c3=1, c4=6666;
    8. int d1=34, d2=0, d3=23, d4=23006783;
    9. printf("%-9d %-9d %-9d %-9d ", a1, a2, a3, a4);
    10. printf("%-9d %-9d %-9d %-9d ", b1, b2, b3, b4);
    11. printf("%-9d %-9d %-9d %-9d ", c1, c2, c3, c4);
    12. printf("%-9d %-9d %-9d %-9d ", d1, d2, d3, d4);
    13. system("pause");
    14. return 0;
    15. }

    输出结果:
    20        345       700       22
    56720     9999      20098     2
    233       205       1         6666
    34        0         23        23006783
    这样写起来更加方便,即使改变某个数字,也无需修改 printf 语句。
     
    %-9d中,d表示以十进制输出,9表示最少占9个字符的宽度,宽度不足以空格补齐,-表示左对齐。综合起来,%-9d表示以十进制输出,左对齐,宽度最小为9个字符。大家可以亲自试试%9d的输出效果。
     
    printf 格式控制字符串的完整形式如下:
    %[flags][width][.precision]type
    1) type 也就是以什么类型输出,比如 %d、%f、%c,type 就分别对应 d、f、c;%-9d中 type 对应 d。type 必须有
     
    2) width 表示最小输出宽度,也就是占几个字符的位置;%-9d中 width 对应 9。

    注意:[xxx] 并不是C语言规定的格式,只是一种习惯写法,表示此处的内容可有可无,后面会经常见到这样的写法。
    对于整数和小数,默认右对齐,不足的宽度以空格补齐,例如:
    printf("%10d%12f", 234, 9.8);
    输出结果为:
           234    9.800000
    234 前面共有7个空格,9.8 前面有4个空格。
     
    3) .precision 表示输出精度。
     
    对于 %d,.precision 表示的其实是最小输出宽度,与 width 不同的是,不足的宽度以 0 补齐,例如:
    printf("%.10d ", 4309);
    输出结果为:
    0000004309
     
    对于 %f,.precision 表示小数的位数,不足以 0 补齐,也就是精度,例如:
    printf("%.10f %.3f ", 23.988, 2.9328745);
    输出结果为:
    23.9880000000 2.933
     
    4) flags 是标志字符,%-9d中 flags 对应-

    几种常见的标志字符
    标志字符
    含  义
    -
    左对齐
    +
    输出符号(正号或负号)
    空格
    输出值为正时冠以空格,为负时冠以负号
    #
    对c、s、d、u类无影响;
    对o类,在输出时加前缀o;
    对x类,在输出时加前缀0x;
    对e、g、f 类当结果有小数时才给出小数点。

    7.从键盘输入数据

    puts 可以输出字符串,printf 可以输出多种类型的数据,另外还有 putchar,可以输出单个字符,例如:
    1. putchar('a');
    2. putchar(7);
    3. putchar('x46');
    输出结果:
    1. aF // 同时会听到喇叭发出“嘟”的声音
    程序是人机交互的媒介,有输出必然也有输入。在C语言中,使用 scanf 从键盘获取用户输入的数据。
     
    scanf 是 scan format 的缩写,意思是格式化扫描,这里可以理解为从键盘获得用户输入。我们先来看一个例子:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int a, b;
    6. scanf("%d %d", &a, &b);
    7. printf("a+b=%d ", (a+b));
    8. system("pause");
    9. return 0;
    10. }
    运行结果:
    34 29 ↙
    a+b=63
     
    从键盘输入 34,空格,再输入 29,然后回车(↙表示回车),就会看到两个数相加的结果。
     
    scanf 和 printf 非常相似:
    1. scanf("%d %d", &a, &b); // 获取用户输入的两个整数,分别赋值给变量 a 和 b
    2. printf("%d %d", a, b); // 将变量 a 和 b 的是在显示器上输出。

    它们都有格式控制字符串,都有变量列表。不同的是,scanf 的变量前要带一个&符号;&称为取地址符,也就是获取变量在内存中的地址。
     
    在《二进制思想以及数据的存储》一节中讲到,数据是以二进制的形式保存在内存中的,字节(Byte)是最小的可操作单位。为了便于管理,我们给每个字节分配了一个编号,使用该字节时,只要知道编号就可以,就像每个学生都有学号,老师会随机抽取学号来让学生回答问题。字节的编号是有顺序的,从 0 开始,接下来是 1、2、3……
     
    下图是 4G 内存中每个字节的编号(以十六进制表示):
     
     
    这个编号,就叫做地址(Address)。int a; 会在内存中分配四个字节的空间,我们将第一个字节的地址称为变量 a 的地址,也就是 &a 的值。对于前面讲到的整数、浮点数、字符,都要使用 & 获取它们的地址,scanf 会根据地址把读取到的数据写入内存。
     
    我们不妨将它们的地址输出看一下:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int a='F';
    6. int b=12;
    7. int c=452;
    8. printf("&a=%#x, &b=%#x, &c=%#x ", &a, &b, &c);
    9. system("pause");
    10. return 0;
    11. }

    输出结果:
    &a=0x18ff48, &b=0x18ff44, &c=0x18ff40
     
     
    图:a、b、c 的内存地址
    注意:你看到的地址是虚拟地址,并不等于它在物理内存中的地址。虚拟内存是现代操作系统因内存管理的需要才提出的概念,dos 下没有这个概念,用户看到的都是真实的地址。CPU 操作的是物理内存地址,所以虚拟地址必须经过转换才能交给 CPU,这是 OS 的工作,对用户是透明的。
    再来看一个 scanf 的例子:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. int a, b, c, age;
    6. float scores;
    7. scanf("a=%d,b=%d,c=%d", &a, &b, &c);
    8. printf("a+b+c=%d ", (a+b+c));
    9. fflush(stdin); // 清空缓冲区
    10. scanf("Tom's age is %d, his scores is %f.", &age, &scores);
    11. printf("age=%d, scores=%f. ", age, scores);
    12. system("pause");
    13. return 0;
    14. }

    运行结果:
    a=230,b=400,c=5009↙
    a+b+c=5639
     
    Tom's age is 16, his scores is 94.5.↙
    age=16, scores=94.500000.
    先输入a=230,b=400,c=5009,回车,就可以得到 a+b+c 的值;再输入Tom's age is 16, his scores is 94.5.,回车,就可以得到年龄和成绩。
     
    fflush(stdin);用来清空输入缓冲区,这是什么意思呢?
     
    在内存中,有一块区域(比如512字节)专门用来保存用户输入的数据,遇到 scanf 时,程序会首先检查该区域是否有数据:
    1. 如果没有,就等待用户输入,用户从键盘输入的每个字符都会暂时保存到这里,直到按下回车键,输入结束,scanf 再从这里读取数据,赋值给变量。
    2. 如果有数据,哪怕是一个字符,scanf 也会直接读取,不会等待用户输入。
     
    这块内存区域,就叫做缓冲区(Buffer),或者缓存(Cache);又因为它是用来暂存用户输入的数据的,所以又叫输入缓冲区。
    缓冲区与普通的内存没有什么两样,都是物理内存上的若干字节,只是作用不同而已。
    上面的代码如果没有fflush(stdin);,运行时就会大有不同:
    a=23,b=900,c=399↙
    a+b+c=1322
     
    age=4239360, scores=0.000000.
    第一次输入后,程序并没有等待我们第二次输入,age 和 scores 都是无效值。这是因为,第一次输入的数据为a=23,b=900,c=399↙(包含最后的回车),回车后 scanf 读取到的数据是a=23,b=900,c=399,还有一个回车符留在缓冲区,遇到第二个 scanf 时,因为缓冲区中有数据,所以会直接读取,不给我们输入的机会。
     
    所以要用fflush(stdin);来清空缓冲区,这样遇到第二个 scanf 时因为缓冲区中没有数据,就会等待用户输入。
    这里重点是告诉大家 scanf 是带有缓冲区的,fflush(stdin);的用法不再展开讨论,请大家先记住,后续我们再讲解。

    ①.getchar

    scanf 用于接收用户输入的各种数据,如果仅仅是输入单个字符,也可以使用 getchar。请看下面的例子:
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int main()
    4. {
    5. char a, b, c;
    6. a=getchar();
    7. printf("a='%c' ", a);
    8. b=getchar();
    9. printf("b='%c' ", b);
    10. c=getchar();
    11. printf("c='%c' ", c);
    12. system("pause");
    13. return 0;
    14. }
    运行结果:
    XYZ↙
    a='X'
    b='Y'
    c='Z'
    你也可以这样输入:
    X↙
    a='X'
    b='
    '
    Y↙
    c='Y'
    第二次之所以是这样的结果,是因为 getchar 也带有缓冲区。输入 X 并回车,第一个 getchar 读取到 'X',接着第二个 getchar 读取到回车换行符,到第三个 getchar,缓冲区中已经没有数据,所以会等待用户输入。

    8.运算符的优先级和结合性

    先来看一个例子:
    1. #include <stdio.h>
    2. int main(){
    3. int a=10,b=1,c=2;
    4. a=b=c;
    5. printf( "12+3*5=%d ", 12+3*5);
    6. printf( "a=%d, c=%d ", a, c);
    7. return 0;
    8. }

    运行结果:
    12+3*5=27
    a=2, c=2
     
    1) 对于表达式12+3*5,很明显先进行乘法运算,计算3*5,结果为15,再进行加法运算,计算12+15,结果为27。也就是说,乘法的优先级比加法高,要先计算,这与数学中的规则是一样的。
     
    所谓优先级,就是当有多个运算符在同一个表达式中出现时,先执行哪个运算符。如果不想按照默认的规则执行,可以加( ),例如(12+3)*5的结果为 75,(2+5)*(10-4)的结果为 42。大部分情况下,它们的规则和数学中是相同的。
     
    2) 对于语句赋值语句a=b=c;,先执行b=c,再执行a=b,而不是反过来,这说明赋值操作符=具有右结合性。
     
    所谓结合性,就是当一个运算符多次出现时,先执行哪个运算符。先执行右边的叫右结合性,先执行左边的叫左结合性。
    表达式(Expression)和语句(Statement)的概念在C语言中并没有明确的定义:
    表达式可以看做一个计算的公式,往往由数据、变量、运算符等组成,例如3*4+5、a=c=d等,它的结果必定是一个值;
    语句的范围更加广泛,不一定是计算,不一定有值,可以是某个操作、某个函数、选择结构、循环等。
    值得注意的是:以分号;结束的往往称为语句,而不是表达式,例如3*4+5;、a=c=d;等。

    3) 像 +、-、*、/ 这样的运算符,它的两边都有数据,例如 3+4、a*3 等,有两个操作数,我们称这样的运算符为双目运算符。后面还会讲解单目运算符和三目运算符。

    9.几个重要的 C 语言概念

    ①.标识符

     
    定义变量时,我们使用了诸如“a”“abc”“mn123”这样的名字,它们都是程序员自己起的,一般能够表达出变量的作用,这叫做标识符(Identifier)。
     
    标识符就是程序员自己起的名字,除了变量名,后面还会讲到函数名、标号等。不过,名字也不能随便起,C语言规定,标识符只能由字母(A~Z, a~z)、数字(0~9)和下划线(_)组成,并且第一个字符必须是字母或下划线。
     
    以下标识符是合法的:
    a, x,  x3, BOOK_1, sum5
     
    以下标识符是非法的:
    3s    不能以数字开头
    s*T    出现非法字符*
    -3x    不能以减号(-)开头
    bowy-1    出现非法字符减号(-)
     
    在使用标识符时还必须注意以下几点:
    1. C语言虽然不限制标识符的长度,但是它受到不同编译器的限制,同时也受到具体机器的限制。例如在某个编译器中规定标识符前128位有效,当两个标识符前128位相同时,则被认为是同一个标识符。
    2. 在标识符中,大小写是有区别的,例如BOOK和book 是两个不同的标识符。
    3. 标识符虽然可由程序员随意定义,但标识符是用于标识某个量的符号,因此,命名应尽量有相应的意义,以便于阅读理解,作到“顾名思义”。

    ②.关键字

     
    关键字(Keywords)是由C语言规定的具有特定意义的字符串,通常也称为保留字,例如 int、char、long、float、unsigned 等。我们定义的标识符不能与关键字相同,否则会出现错误。
    你也可以将关键字理解为具有特殊含义的标识符,它们已经被系统使用,我们不能再使用了。
    标准C语言中一共规定了32个关键字。

    ③.注释

     
    注释(Comments)可以出现在代码中的任何位置,用来向用户提示或解释程度的意义。程序编译时,会忽略注释,不做任何处理,就好像它不存在一样。
     
    C语言支持单行注释和多行注释:
    1. 单行注释以//开头,直到本行末尾(不能换行);
    2. 多行注释以/*开头,以*/结尾,注释内容可以有一行或多行。
     
    一个使用注释的例子:
    1. /*
    2. Powered by: c.biancheng.net
    3. Author: xiao p
    4. Date: 2015-6-26
    5. */
    6. #include <stdio.h>
    7. int main()
    8. {
    9. /* puts 会在末尾自动添加换行符 */
    10. puts("http://c.biancheng.net");
    11. printf("C语言中文网 "); //printf要手动添加换行符
    12. return 0;
    13. }

    运行结果:
    http://c.biancheng.net
    C语言中文网
     
    在调试程序的过程中可以将暂时不使用的语句注释掉,使编译器跳过不作处理,待调试结束后再去掉注释。

    ④.函数和头文件

     
    在C语言中,有的语句使用时不能带括号,例如声明类型的关键字 int、char、long 等,有的语句必须带括号,例如 puts()、printf()、scanf() 等。带括号的称为函数(Function)。
     
    C语言发布时,它的开发者已经为我们编写了常用的代码(例如输入输出代码),我们可以拿来直接使用,不必再自己编写。这些代码,以函数(Function)的形式提供给我们,我们直接调用函数就可以。
     
    函数的一个明显特征就是使用时带括号( ),必要的话,括号中还要包含数据或变量。我们将在《函数》一章中详细介绍,请大家先记住这个概念。

    C语言中的函数和数学中的函数有很大的不同,最好不要拿两者对比。

    C语言包含了几百个函数,为了便于管理和使用,这些函数被分门别类地保存到了不同的文件,使用时要引入对应的文件。这样的文件称为头文件(Header File),后缀为.h。
     
    引入头文件使用#include命令,并将头文件放在< >之间,例如#include <stdio.h>、#include <stdlib.h>。
     
    stdio 是 standard input and output 的缩写,称为“标准输入输出文件”。stdio.h中包含了很多常用函数,如 puts、printf、scanf 等。
     
    需要注意的是:头文件必须在程度开头引入。

    ⑤.main 函数

     
    我们说过,编写的代码要位于main(){ }之中,main 也是一个函数,称为主函数。main 比较特殊,程序运行时,会从 main 函数开始,直到 main 函数结束,所以代码中只能有一个 main 函数,否则程序将不知道从哪里开始执行,出现错误。




  • 相关阅读:
    2020年捌月份生活随笔
    2020年柒月份生活随笔
    2020年陆月份生活随笔
    第二次:郑州银行杯|2019郑州国际马拉松
    第一次:海尔|2017年青岛马拉松
    专项测试技能和线上线下监控
    实用
    Oracle 数据库 有用的sql语句
    Qt demo
    springboot demo
  • 原文地址:https://www.cnblogs.com/caizheng/p/6155176.html
Copyright © 2020-2023  润新知