【C】C系列一 - 基本认识

C 语言是全世界最流行的编程语言之一,在过去几年中长期霸占语言热度排行榜前 3 名(TIOBE)

一、认识 C 语言

1.1. 标准

  • 目前最新的标准是:ISO/IEC 9899:2011,简称为C11标准
  • 还有部分开发人员在使用ISO/IEC 9899:1999,简称为C99标准

1.2. 用途

基本上大部分的计算机语言都是基于 C 语言进行扩展的,它的性能极其优越,其他编程语言无法取代

  • 操作系统(Linux、Unix 等操作系统就是利用 C 语言编写的)
  • 数据库开发
  • 高性能服务器开发
  • 嵌入式开发
  • 游戏开发
  • ……

1.3. 开发工具

  • 记事本(txt)
    • 功能单一、体验差、易出错、开发效率低
  • IDE(Integrated Development Environment):集成开发环境
    • 智能提示、高亮识别、语法检测、开发效率高等强大功能
    • 常见的 C 语言 IDE:Visual Studio、Qt Creator、CLion、Dev-C++、Xcode(Mac 操作系统)

二、程序的结构

任何一个 C 语言程序都由一个或多个函数构成,每个函数都由自己的功能,所以以后编写的 C 语言代码,基本上都是写在函数中。

2.1. main 函数

每一个函数都有自己的名称(标识符),并且每一个函数的名称都是唯一的。

大部分程序运行时都有一个入口,而这个入口就是main函数,C 语言就是这样,如果没有 main 函数,程序就无法运行。

2.2. Hello World

1
2
3
4
5
6
#include <stdio.h>

int main() {
printf("Hello World");
return 0;
}
  • printf :将双引号中的文字显示到屏幕上。

2.3. 语法须知

  • C 语言源代码文件的扩展名是.c
  • 每一条语句后面都要以分号;结尾;
  • 括号都是成对出现的,小括号(),中括号[],大/花括号{}
  • 区分大小写;
  • 代码中的符号必须是英文符号(注释、字符、字符串等内容除外,其实所有编程语言代码中的符号都是只支持英文符号的,除了易语言)。

三、计算机关联

3.1. 计算机 0 和 1 由来

计算机是由电路机演变而来的,电路的逻辑只有 0 和 1 两种状态,0 表示低电平,1 表示高电平,因此现在的电子计算机只能识别 0 和 1。

  • 除了电子计算机、还有光子计算机、量子计算机、生物计算机、纳米计算机等高精端计算机(这些高端计算机不是只能识别 0 和 1 的)
  • 我们平时说的计算机,就是指的电子计算机

3.2. 计算机如何识别 C 语言代码

我们编写好的代码交给计算机后,计算机是怎么识别出的?

上面提到计算机只能识别 0 和 1,要想计算机能够正常识别代码,就需要经过以下流程:

源代码(.c) -> 编译(compile) -> 编译文件(.o) -> 链接(link) -> 可执行文件(*.exe)

  • 编译:将 C 语言源代码文件编译成目标文件(二进制文件),由编译器完成;
  • 链接:将所有的目标文件以及所需要的库文件合并成一个可执行文件,由链接器完成。

3.3. 常见的 C 语言编译器(已内置链接器)

  • MSVC:微软出品(用在 Windows 中)
  • GCC:GNU Compiler Collection 缩写,GNU 出品
  • MinGW:Minimalist GNU for windows 的缩写,GNU 出品
  • LLVM:常用在苹果开发工具中
  • ……

同一分源代码,用不同的编译器编译出来的目标文件是不一样的,主要体现在体积、格式、运行效率等。

不同操作系统最后链接产生的可执行文件(应用程序)格式也是不同的:

  • Windows:PE 格式(常以.exe作为文件扩展名)
  • Linux:ELF 格式
  • Mac:Mach-O 格式

3.4. 什么是 BUG?

  • BUG 一般指的是程序漏洞、缺陷、错误,程序出现 Bug 后可能会导致程序无法正常运行;
  • 来源:很早之前一位计算机科学家在调试设备时出现了故障,后来发现是有只飞蛾跑进了设备内部,卡住了机器的电路,导致机器无法运行,于是后来就把程序故障成为 Bug(臭虫),排除程序故障排叫做 DeBug。

3.5. 硬盘和内存

在 CPU、硬盘、内存 3 者之中,对我们开发者来说,最需要关注的内存,因为内存在程序运行过程中才执行的,而程序是运行在内存中的

四、注释

4.1. 什么是注释?

  • 注释常用来解释某段代码的具体含义、作用
  • 注释并不会被当做正常代码进行编译

4.2. 注释有 2 种书写格式

  • 单行注释
  • 多行注释
1
2
3
4
5
6
7
8
9
10
11
12
// 导入C语言基础库(这是一段单行注释)
#include <stdio.h>

/*
这是程序入口函数
函数运行后会打印一段文字
(这就是多行注释)
*/
int main() {
printf("Hello World");
return 0;
}

4.3. 写注释的好处:

  • 方便回忆,检查旧代码
  • 方便程序员之间团队协作,提高开发效率
  • 方便旧项目的交接

五、变量

变量的作用,可以存储程序运行过程中会变化的数据。

5.1. 如何声明一个变量?

格式:

1
变量类型 变量名;

示例:

1
2
3
int year;
int month;
int day
  • 变量类型决定了变量能够存储什么类型的数据(上面的示例:int 类型决定了变量能够存储整数)。

5.2. 如何给变量赋值?

赋值:将数据交给变量去存储

1
变量名 = 数据;
  • 这个等号表示赋值,会把右边的数据赋值给左边的变量
1
2
int month = 12
int day = 30
  • 在变量声明完成后,直接通过变量名访问变量(不需要再带上变量类型(也不能带上变量类型))

  • 变量可以被多次赋值,新值会覆盖旧值

1
2
3
int num = 10;
num = 20;
printf("%d", num); // 输出:20
  • 变量的第一次赋值,一般叫做初始化(init)
1
2
3
4
5
6
7
// 声明一个变量,未初始化
int num;
// 初始化
num = 20;
// 不是初始化,是赋值
num = 30;
printf("%d", num); // 输出:30
  • 变量声明的同时也可以进行初始化(开发中大部分操作)
1
2
// 声明变量num,并对变量进行初始化,变量初始值为10
int num = 10;
  • 变量在未初始化之前,他的值是不确定的
  • 变量在使用之前必须先进行初始化
  • 可以同时声明多个同类型的变量
1
int year, month, day = 30;
  • 可以将一个变量的值赋给另一个变量
1
2
3
4
int num1 = 10;
int num2 = 20;
num1 = num2;
printf("%d--%d", num1, num2); // 输出:20--20

5.3. 变量的作用域

变量的作用域,就是指变量的作用范围,有效使用范围

  • 从声明变量的那条语句开始,直到变量所在的大括号结束
  • 在同一个作用域内,不允许有同名的变量

六、字符串

字符串由若干个字符组成的一串数据。

  • 字符串用双引号包裹
1
printf("Hello World");
  • 如果字符串中出现了变量名,是不会去访问变量的,使用字符串时,会保留原来的字符内容
1
2
int day = 30;
printf("day = %d", day); // 输出:day = 30
  • 字符串中的%d,是整数的占位符,到时会用对应的整数取代占位符,生成一个新的字符串

七、标识符

标识符,由开发者自定义的一些名称(比如变量名、函数名等)。

7.1. 标识符的命名规则如下:

  • 不限长度
  • 可以使用数字、下划线、英文字符
  • 可以使用\u 及\U 转义记号指定的 Unicode 字符(C99 开始)
  • 不能以数字开头
  • 不能使用关键字(关键字,也叫做保留字,是编程语言内部已经定义好的一些有特殊含义的符号)

7.2. 命名规范:

  • 小驼峰(第一个单词的首字母小写,其他单词的首字母大写),myNameAndAge
  • 大驼峰(所有单词的首字母大写),MyNameAndAge
  • 下划线连接,my_name_and_age,也有大写MY_NAME_AND_AGE

八、字面量

顾名思义,就是一个固定值。

1
2
3
int age = 20;
double height = 1.88;
char word = "a";

上面的代码中,等号右边的固定值都叫做字面量。

九、进制

什么是进制?进制是一种数字的表示形式;是一种带进位的计数方法。

计算机领域常见的进制有:十进制、二进制、八进制、十六进制。

9.1. 十进制(逢十进一)

由十个数字符号构成所有的数值:0、1、2、3、4、5、6、7、8、9

9.2. 八进制(逢八进一)

由八个数字符号构成所有的数值:0、1、2、3、4、5、6、7

9.3. 二进制(逢二进一)

由两个数字符号构成所有的数值:0、1

9.4. 十六进制(逢十六进一)

由十六个数字及字母符号构成所有的数值:0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F。

  • A、B、C、D、E、F 分别表示十、十一、十二、十三、十四、十五
  • 也可以用小写字母表示:a、b、c、c、e、f

同一个数可以用不同的进制来表示:比如三十三

  • 十进制:33
  • 二进制:100001
  • 八进制:41
  • 十六进制:21

9.5. 基数

进制中有个叫“基数”(或者底数)的概念

  • 基数是指可以使用的数字符号的数目
  • 十进制的基数是十、二进制的基数是二、八进制的基数是八、十六进制的基数是十六

9.6. 进制转换(任意 R 进制 -> 十进制)

一个 R 进制表示的数转为十进制:$ k_3 k_2 k_1 k_0.s_1 s_2 s_3 $

  • 公式:$$ (k_3 _ R^3) + (k_2 _ R^2) + (k_1 _ R^1) + (k_0 _ R^0) + (s_1 _ R^{-1}) + (s_2 _ R^{-2}) + (s_3 * R^{-3})$$

  • 二进制 -> 十进制

    • $$(1101.011)_2 = (1 * 2^3) + (1 * 2^2) + (0 * 2^1) + (1 * 2^0) + (0 * 2^{-1}) + (1 * 2^{-2}) + (1 * 2^{-3}) = 13.375$$
  • 八进制 -> 十进制

    • $$(537.34)_8 = (5 * 8^2) + (3 * 8^1) + (7 * 8^0) + (3 * 8^{-1}) + (4 * 8^{-2}) = 351.4375$$
  • 十六进制 -> 十进制

    • $$(4DA.CB)_16 = (4 * 16^2) + (13 * 16^1) + (10 * 16^0) + (12 * 16^{-1}) + (11 * 16^{-2}) \approx 1242.79$$

9.7. 十进制 -> 任意 R 进制

十进制数转换成 R 进制数,须将整数部分和小数部分分别转换

  • 整数转换(除 R 取余法)
    • 用十进制数的整数部分除以 R、取余数作为转换后的 R 进制数据的整数部分最低位数字;
    • 再用所得的商除以 R、取余数作为转换后的 R 进制数据的高一位数字;
    • 重复上面第二次的操作,一直到商为 0 结束。

求 115 的二进制:$$115 = (1110011)_2$$

  • 小数转换(乘 R 取整法)
    • 十进制数的小数部分乘以 R、取乘积的整数部分作为转换后 R 进制数据的小数点后第一位数字;
    • 再用所得的乘积的小数部分乘以 R、然后取新乘积的整数部分作为转换后的 R 进制小数的低一位数字;
    • 重复上面第二次的操作,一直到乘积的小数部分为 0 为止,或者一直到已得到要求的精度数位为止。

求 0.625 的二进制:$$0.625 \approx (0.101)_2$$

  • 小数转换(整数退位法)
    • 0.321 换成二进制数,保留 7 位
      $$0.321 * 2^7 = 41.088$$
    • 取整数 41 $$41 = (101001)_2$$
    • 只有 6 位但要求保留 7 位,退位,在前面补 0,所以是 0.0101001

9.8. 二进制与十六进制之间的转换

4 位二进制数恰好有 16 种组合状态,也就是说,1 位十六进制数与 4 位二进制数是一一对应的

计算:

  • $$(111010110.11)_2 = (0001 1101 0110.1100)2 = (1D6.C){16}$$
  • $$(4AF8B.E)_{16} = (0100 1010 1111 1000 1011.1110)_2 = (1001010111110001011.111)_2$$

9.9. 二进制与八进制之间的转换

3 位二进制数恰好有 8 种组合状态,也就是说,1 位八进制数与 3 位二进制数是一一对应的

计算:

  • $$(111010110.11)_2 = (011 010 110.110)_2 = (326.6)_8$$
  • $$(2476.4)_8 = (010 100 111 110.100)_2 = (10100111110.1)_2$$

9.10. 代码中的进制书写形式

C 语言标准规定

  • 默认是十进制,比如 89,表示【八十九】
  • 以 0 开头的是八进制,比如 024,表示【二十】
  • 以 0x 或 0X 开头的是十六进制,比如 0x2B,表示【四十三】

C 语言标准并不支持二进制的书写形式

  • 但是部分编译器支持,比如 GCC 规定以 0b 或 0B 开头的是二进制
1
2
3
4
5
int i1 = 20; // 十进制
int i2 = 024; // 八进制
int i3 = 0x14; // 十六进制
int i4 = 0b10100; // 二进制
printf("%d %d %d %d", i1, i2, i3, i4) // 输出:20 20 20 20

十、内存

10.1. 存储单位

字节(Byte,B)是一种基本存储单位(在计算机领域用来计量存储容量)。

单位换算:
1 字 = 2B(字的英文是 word,1 字等于 2 字节)

  • 1KB = 1024B(千字节)
  • 1MB = 1024KB(兆字节)
  • 1GB = 1024MB(千兆字节)
  • 1TB = 1024GB(太字节)
  • 1PB = 1024TB(拍字节)
  • 1EB = 1024PB, 1ZB = 1024EB, 1YB = 1024ZB,
  • 1BB = 1024YB, 1NB = 1024BB, 1DB = 1024NB,

10.2. 内存地址

4GB 的内存一共有多少字节?
4GB = 41024MB = 410241024KB = 4102410241024B = 4294967296B = 2^32B

内存中的每一个字节都有自己的编号

  • 上面图示是 4GB 内存空间
  • 如果把内存比作是酒店,
    • 那么字节就是酒店的房间,字节的编号就是房间号
    • 相邻的 2 个房间,房号是连续的,那么相邻的 2 个字节,编号是连续的
    • 房间是拿来住人的,存放物品的,那么字节是拿来存储数据的
  • 每一个字节的编号,也叫做字节的内存地址

10.3. 变量的存储空间

变量是用来存储数据的,凡是数据,或多或少会占用一定的存储空间。

变量的数据是存储在硬盘还是内存中呢?

  • 内存。因为变量是用来存储程序运行过程中会变化的数据

一个变量会占用多大的存储空间呢?(占用多少字节)

  • 取决于变量所存储数据的类型(也就是变量类型)
  • 比如 C 语言标准规定,一个 int 类型的变量至少占用 2 个字节的内存空间
  • 在 Win64 API 中,一个 int 类型的变量占用 4 个字节的内存空间

在 C 语言中,怎么查看变量占用了多少内存空间呢?

10.4. sizeof 运算符

使用sizeof可以获得某一数据类型的大小(所占多少字节的内存空间)。

1
2
3
4
int age = 20;
printf("%d", sizeof(age)); // 输出:4
printf("%d", sizeof age); // 输出:4
printf("%d", sizeof(int)); // 输出:4

10.5. 变量的内存地址

1
2
3
4
int a = 10;
int b = 20;
printf("%p\n", &a); // 输出:0x00bff578
printf("%p\n", &b); // 输出:0x00bff574

  • 变量的内存地址:变量首字节(地址值最小的那个字节)的内存地址
  • 越晚定义的变量,内存地址越小

十一、位(Bit)

  • 由于计算机只能识别 0 和 1,所以每一个字节中的数据都是以二进制形式存储的
  • 一个字节包含 8 个二进制位,位是计算机领域最小存储单位,每一个二进制存储一个 0 或 1
  • 1Byte = 8bit, 简写: 1B = 8b

11.1. 有符号数的二进制表示方法

int类型属于有符号整数类型

  • 可以表示正数、负数
1
2
int a = 20;
int b = -20;
  • 有符号数的二进制有 3 种表示方法:源码、反码、补码
  • 三种表示方法均有符号位数值位两部分
    • 符号位:最高位作为符号位,用 0 表示“正”,用 1 表示“负”
    • 数值位:三种表示方法各不相同

11.2. 原码

  • 最高位:作为符号位,用 0 表示“正”,用 1 表示“负”
  • 其余位:作为数值位,表示绝对值的大小(所以原码又称为带符号的绝对值)

11.3. 反码

  • 正数的反码:和原码一样
  • 负数的反码:原码保持符号位不变,数值位按位取反(0 变 1,1 变 0),得到反码

11.4. 补码

  • 正数的补码:和原码一样
  • 负数的补码:反码的末位加 1,得到补码

在计算机中,数值一律用补码来表示和存储。
比如,下面 2 个变量在内存中存储的二进制数值是(假设int类型的变量占用 4 字节)

1
2
int a = 20;
int b = -20;
  • a: 0000 0000 0000 0000 0000 0000 0001 0100
  • b: 1111 1111 1111 1111 1111 1111 1110 1100

十二、大小端模式

大小端模式:决定了多字节数据的字节存储顺序

  • 大端模式:高低低高

    • 高字节放在低地址,低字节放在高地址
  • 小端模式:高高低低

    • 高字节放在高地址,低字节放在低地址

不同 CPU 架构的模式不一样,比如 x86 架构师小端模式,有些 CPU 架构师大端模式,目前比较常见的是小端模式。

十三、char 类型

char类型属于字符类型。

  • 字符用单引号
  • 打印的占位符:%c
1
2
3
char c1 = 'k';
char c2 = '9';
printf("%c %c", c1, c2); // 输出:k 9
  • 一个char类型的变量占用 1 个字节的内存,所以它只能存储 1 个单字节字符
  • 26 个英文字母(a ~ z,A ~ Z),10 个阿拉伯数字(0~9)都是单字节字符

下面的代码有问题:

1
2
3
4
5
6
7
8
// 不能存储超过1个单字节字符
char c1 = 'idbeny';

// 一个中文字符至少占用2个字节
char c2 = '你';

// 字符串是双引号,字符是用单引号
char c3 = "i";

13.1. 字符的存储细节

计算机中的数据都是以二进制形式存储的,字符数据也不例外。每一个字符都会被转化成对应的整数值进行存储。

在 1967 年,美国发布了 ASCII 码表,里面规定了 128 个单字节字符对应的整数值(ASCII 码值)。

  • 数字 0 对应的码值是 48
  • 大写字母 A 对应的码值是 65
  • 小写字母 a 对应的码值是 97
1
2
3
4
5
6
7
8
9
10
11
12
int i = 97;
char c1 = '0';
char c2 = 'a';
char c3 = 'A';
printf(" %p\n %p\n %p\n %p\n", &i, &c1, &c2, &c3);
/*
输出:
0x00b7EC19
0x00b7EC1A
0x00b7EC1B
0x00b7EC2C
*/

十四、ASCII 码表

ASCII 被译为“美国信息交换标准码”,是一种标准的单字节字符编码方案。

共 128 个字符,码值范围:0~127(也就是 0x00 ~ 0x7F)

  • 33 个是控制字符或通信专用字符,码值范围:0~31,127

    • 控制字符:LF(换行)、DEL(删除)、BS(退格)等
    • 通信专用字符:SOH(文头)、EOT(文尾)、ACK(确认)等
  • 95 个可显示字符,码值范围:32~126

    • 48 ~ 57:十个阿拉伯数字(0~9)
    • 65 ~ 90:26 个大写英文字母(A~Z)
    • 97 ~ 122:26 个小写英文字母(a~z)
    • 其余为标点符号、运算字符等

没有必要记住所有字符码值,只需要记住常用的两个就行了:

  • 小写字母 a 的 ASCII 码值:97
  • 小写字母的 ASCII 码值 = 大写字母 ASCII 码值 + 32
  • 大写字母的 ASCII 码值 = 小写字母 ASCII 码值 - 32
  • 阿拉伯数字 0 的 ASCII 码值:48

注意使用细节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
char c1 = 'a';
char c2 = 'A';
printf("%d %d\n", c1, c2); // 输出:97 65

int i1 = 97;
int i2 = 65;
printf("%c %c\n", i1, i2); // 输出:a A

char c3 = 'A';
printf("%d\n", c3 + 10); // 输出:75

int i3 = 67;
printf("%c\n", i3 + 10); // 输出:M

int i4 = 'a';
char c4 = 97;
printf("%d %d\n", i4, c4); // 输出:97 97
printf("%c %c\n", i4, c4); // 输出:a a

// 完全可以将char类型当做整数类型来使用
char c5 = 10;
char c6 = c5 + 20;
char c7 = c5 + c6 + 30;
printf("%d", c7); // 输出:70

大小写字母之间的转换

1
2
3
4
5
6
char c1 = 'n';
char c2 = c1 - 32;

char c3 = 'Q';
char c4 = c3 + 32;
printf("%c %c", c2, c4); // 输出:N q

14.1. 数字整数、数字字符在内存中的区别

1
2
3
int i = 9;
char c = '9';
printf("%d %d", i, c); // 输出:9 57

  • 变量 i 在内存中利用 4 个字节存储数值 9
  • 变量 c 在内存中利用 1 个字节存储数值 57

疑问:中文、日文、韩文等非英文字符是如何存储在计算机中的?
可以肯定的是,必然是以二进制的形式存储在计算机中的,只是每个字符对应的二进制数值取决于具体的编码方案(GBK、UTF-8、Unicode 等),
GBK 主要支持的是 CJK 汉字字符(C-中国、J-日本、K-朝鲜),而 UTF-8 支持几乎世界上所有的字符。

14.2. 转义序列

转义序列,一般也叫作转义字符,是一些有特殊含义的字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
printf("我叫'idbeny',在\"1024星球\"等你"); // 输出:我叫'idbeny',在"1024星球"等你
char c1 = '\'';
char c2 = '"';
printf("\n%c\t%c\\", c1, c2); // 输出:' "\

char c3 = '\063';
printf("%d %c", c3, c3); // 输出:51 3

// "\0115""\011""5"的组合
// "\011"就是水平制表符"\t"
printf("\0115"); // 输出: 5

错误写法:
char c1 = '\X4A'; // 不能是大写X
char c1 = '\0x4A'; // 不能是0x
char c1 = '\0X4A'; // 不能是0X

十五、scanf 函数

  • printf函数的功能是:输出(output),将数据显示在屏幕上
  • scanf函数的功能是:输入(input),读取数据(比如读取通过键盘输入的数据)
1
2
3
int age = 10;
scanf("%d", &age); // 终端输入:20
printf("age: %d", age); // 输出:age: 20
  • scanf 函数开始执行后,会等待用户输入
    • 程序会卡在 scanf 函数,不会执行 scanf 函数后面的代码
    • 当用户敲 Enter 键(回车键)时,表示输入完毕
    • 程序才会开始执行 scanf 函数后面的代码

15.1. 匹配的细节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
int i = 10;
scanf("%d", &i);
printf("你输入的整数是: %d", i);
/*
输入:30idbeny
输出:你输入的整数是: 30

输入:22.88
输出:你输入的整数是: 22

输入:a
输出:你输入的整数是: 10 // 原因:变量i默认值是10

输入:你好1024
输出:你输入的整数是: 10
*/

char c = 'a';
scanf("%c", &c);
printf("你输入的字母是:%c\n", c);
printf("对应ASCII码值是:%d", c);
/*
输入:i
输出:
你输入的字母是:i
对应ASCII码值是:105

输入:hello
输出:
你输入的字母是:h
对应ASCII码值是:104

输入:97
输出:
你输入的字母是:9
对应ASCII码值是:57
*/

总结:

  • 从左到右开始字符匹配,当中途匹配失败时,将结束匹配
  • 当尝试把输入的数字赋值给字符变量时,scanf 把输入的数字当成是数字字符来处理,而不是把输入的数字当成字符的 ASCII 码值来处理

15.2. scanf 函数的空白字符

空白字符包括:空格(’ ‘)、Tab(’\t’)、Enter(’\n’)

  • 如果在输入整数的开头,有一段任意长(长度 ≥0)连续空白字符,是不需要与格式化字符串中的字符进行匹配的。

    1
    2
    3
    4
    5
    6
    7
    int age = 10;
    scanf("%d", &age);
    printf("age is %d", age);
    /*
    输入: 30
    输出:age is 30
    */
  • 在格式化字符串中,任意长(长度 ≥0)连续空白字符,能匹配输入的任意长(长度 ≥0)连续空白字符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    int age = 10;
    scanf(" %d", &age); // %d左边有空格
    printf("age is %d", age);
    /*
    输入: 30
    输出:age is 30
    */

    int age = 20;
    scanf("%d ", &age); // %d右边有空格
    printf("age is %d", age);
    /*
    输入: 30
    输出:age is 30
    */