邂逅C语言——重要知识点第二章(个人整理版)
二.C语言数据类型和变量
1.数据类型介绍

• C 语言原来并没有为布尔值单独设置一个类型,而是使用整数 0 表示假,非零值表示真。
在 C99 中也引入了布尔类型 ,是专门表示真假的。
• 布尔类型的使用得包含头文件
• 布尔类型变量的取值是: true 或者 false
#include
#include
int main() {
bool flag = true;
if (flag)
printf("i like C
");
return 0;
}
• sizeof操作符
①sizeof 是⼀个关键字,也是操作符,专门是用来计算 sizeof 的操作符数的类型长度的,单位是字
节。 sizeof 操作符的操作数可以是类型,也可是变量或者表达式。
②sizeof 的操作数如果不是类型,是表达式或变量的时候,可以省略掉后边的括号的。
int a = 10;
sizeof a;//合法,计算变量a的大小
sizeof(int);//合法,计算int类型的大小
sizeof int;//错误!类型必须加括号
③sizeof 后边的表达式是不真实参与运算的,根据表达式的类型来得出大小。
int a = 10;
sizeof(a++); // 计算int类型大小(4字节),a++不会执行
printf("%d", a); // 输出10,a的值未变
④sizeof 的返回值类型是 size_t 。
注:sizeof 运算符的返回值,C 语言只规定是无符号整数,并没有规定具体的类型,而是留给系统自己去决定, sizeof 到底返回什么类型。不同的系统中,返回值的类型有可能是 unsigned int ,也有可能是 unsigned long ,甚至是 unsigned long long,对应的 printf() 占位符分别是 %u 、 %lu 和 %llu 。这样不利于程序的可移植性。C 语言提供了⼀个解决方法,创造了⼀个类型别名 size_t ,用来统一表示sizeof 的返回值类型。对应当前系统的 sizeof 的返回值类型,可能是 unsigned int ,也可能是unsigned long long。
#include
int main()
{
int a = 10;
printf("%zd
", sizeof(a));
printf("%zd
", sizeof a);//a是变量的名字,可以省略掉sizeof后边的()
printf("%zd
", sizeof(int));
printf("%zd
", sizeof(3 + 3.5));
return 0;
}
在C语言中%zd是用于格式化输出 ssize_t 类型的格式符,%zu格式化输出 size_t 类型的格式符
ssize_t 是有符号整数(可负) size_t 是无符号整数(非负)
2.signed 和 unsigned
• C 语言使用 signed 和 unsigned 关键字修饰字符型和整型类型的。signed 关键字,表示一个类型带有正负号,包含负值;unsigned 关键字,表示该类型不带有正负号,只能表示零和正整数。对于 int 类型,默认是带有正负号的,也就是说 int 等同于 signed int 。由于这是默认情况,关键字 signed 一般都省略不写,但是写了也不算错。int 类型也可以不带正负号,只表示非负整数。这时就必须使用关键字 unsigned 声明变量。
• 整数变量声明为 unsigned 的好处是,同样长度的内存能够表示的最大整数值,增大了⼀倍。比如,16位的 signed short int 的取值范围是:-32768~32767,最大是32767;而unsigned short int 的取值范围是:0~65535,最大值增大到了65,535。32位的 signed int 的取值范围可以参看 limits.h (定义了基本数据类型的取值范围)中给出的定义。
• 字符类型 char 也可以设置 signed 和 unsigned 。
signed char c; // 范围为 -128 到 127
unsigned char c; // 范围为 0 到 255
• 注意:C 语言规定 char 类型默认是否带有正负号,由当前系统决定。这就是说,char 不等同于 signed char ,它有可能是 signed char ,也有可能是 unsigned char。这一点与 int 不同,int 就是等同于 signed int 。
3.变量
• 变量的分类:
• 全局变量:在大括号外部定义的变量就是全局变量全局变量的使用范围更广,整个工程中想使用,都是有办法使用的。
• 局部变量:在大括号内部定义的变量就是局部变量局部变量的使用范围是比较局限,只能在自己所在的局部范围内使用的。
#include
int global = 2023;//全局变量
int main()
{
int local = 2018;//局部变量
printf("%d
", local);
printf("%d
", global);
return 0;
}
• 如果全局变量和局部变量名字相同呢?
#include
int n = 1000;
int main()
{
int n = 10;
printf("%d
" n);//打印的结果是多少呢?
return 0;
}
其实当局部变量和全局变量同名的时候,局部变量优先使用。
• 全局变量和局部变量在内存中存储在哪里呢?
一般我们在学习C/C++语言的时候,我们会关注内存中的三个区域:栈区、堆区、静态区。
①. 局部变量是放在内存的栈区
②. 全局变量是放在内存的静态区
③. 堆区是用来动态内存管理的
4.算术操作符
• C语言中为了方便运算,提供了⼀系列操作符,其中有⼀组操作符叫:算术操作符。分别是: + - * / % ,这些操作符都是双目操作符。
注:操作符也被叫做:运算符,是不同的翻译,意思是⼀样的。
• 重点介绍一下 / 和 %
①.运算符 / 用来完成除法,除号的两端如果是整数,执行的是整数除法,得到的结果也是整数。
#include
int main()
{
float x = 6 / 4;
int y = 6 / 4;
printf("%f
", x); // 输出 1.000000
printf("%d
", y); // 输出 1
return 0;
}
上面示例中,尽管变量 x 的类型是 float (浮点数),但是 6 / 4 得到的结果是 1.0 ,而不是1.5 原因就在于 C 语言里面的整数除法是整除,只会返回整数部分,丢弃小数部分。
• 要想得到浮点数的结果,两个运算数必须至少有⼀个浮点数,这时 C 语言就会进行浮点数除法。
#include
int main()
{
float x = 6.0 / 4; // 或者写成 6 / 4.0
printf("%f
", x); // 输出 1.500000
return 0;
}
上面示例中, 6.0 / 4 表示进行浮点数除法,得到的结果就是 1.5 。
• 再看一个例子:
#include
int main()
{
int score = 5;
score = (score / 20) * 100;
return 0;
}
上面代码,你可能觉得经过运算, score 会等于 25 ,但是实际上 score 等于 0 。这是因为 score / 20 是整除,会得到⼀个整数值 0 ,所以乘以 100 后得到的也是 0 。为了得到预想的结果,可以将除数 20 改成 20.0 ,让整除变成浮点数除法。
#include
int main()
{
int score = 5;
score = (score / 20.0) * 100;
return 0;
}
②.运算符 % 表示求模(余)运算,即返回两个整数相除的余值。这个运算符只能用于整数,不能用于浮点数。
#include
int main()
{
int x = 6 % 4; // 2
return 0;
}
• 负数求模的规则是,结果的正负号由第⼀个运算数的正负号决定
#include
int main()
{
printf("%d
", 11 % -5); // 1
printf("%d
",-11 % -5); // -1
printf("%d
",-11 % 5); // -1
return 0;
}
上面示例中,第⼀个运算数的正负号( 11 或 -11 )决定了结果的正负号。
5.赋值操作符
• 在变量创建的时候给⼀个初始值叫初始化,在变量创建好后,再给⼀个值,这叫赋值。
• 赋值操作符 = 是⼀个随时可以给变量赋值的操作符。
• 赋值操作符也可以连续赋值,如:
int a = 3;
int b = 5;
int c = 0;
c = b = a+3;//连续赋值,从右向左依次赋值的。
• C语言虽然支持这种连续赋值,但是写出的代码不容易理解,建议还是拆开来写,这样方便观察代码的执行细节。
• 重点区分:前置自增,后置自增;前置自减,后置自减
int a = 10;
int b = ++a;//++的操作数是a,是放在a的前⾯的,就是前置++
printf("a=%d b=%d
",a , b);
int a = 10;
int b = a++;//++的操作数是a,是放在a的后⾯的,就是后置++
printf("a=%d b=%d
",a , b);
int a = 10;
int b = --a;//--的操作数是a,是放在a的前⾯的,就是前置--
printf("a=%d b=%d
",a , b);//输出的结果是:9 9
int a = 10;
int b = a--;//--的操作数是a,是放在a的后⾯的,就是后置--
printf("a=%d b=%d
",a , b);//输出的结果是:9 10
• 强制类型转换
int a = (int)3.14;//意思是将3.14强制类型转换为int类型,这种强制类型转换只取整数部分
6.printf 介绍
• printf() 的作用是将参数文本输出到屏幕。它名字里面的 f 代表 format (格式化),表示可以
定制输出文本的格式。
• printf() 不会在行尾自动添加换行符,运行结束后,光标就停留在输出结束的地方,不会自动换
行。为了让光标移到下一行的开头,可以在输出文本的结尾,添加⼀个换行符
。
• 占位符列举:
printf() 的占位符有许多种类,与 C 语言的数据类型相对应。下面按照字母顺序,列出常用的占位
符,方便查找,具体含义在后面章节介绍。
• %a :十六进制浮点数,字母输出为小写。
• %A :十六进制浮点数,字母输出为大写。
• %c :字符。
• %d :十进制整数。// int
• %e :使用科学计数法的浮点数,指数部分的 e 为小写。
• %E :使用科学计数法的浮点数,指数部分的 E 为大写。
• %i :整数,基本等同于 %d 。
• %f :小数(包含 float 类型和 double 类型)。//float %f double - %lf
• %g :6个有效数字的浮点数。整数部分一旦超过6位,就会自动转为科学计数法,指数部分的 e
为小写。
• %G :等同于 %g ,唯⼀的区别是指数部分的 E 为大写。
• %hd :十进制 short int 类型。
• %ho :八进制 short int 类型。
• %hx :十六进制 short int 类型。
• %hu :unsigned short int 类型。
• %ld :十进制 long int 类型。
• %lo :八进制 long int 类型。
• %lx :十六进制 long int 类型。
• %lu :unsigned long int 类型。
• %lld :十进制 long long int 类型。
• %llo :八进制 long long int 类型。
• %llx :十六进制 long long int 类型。
• %llu :unsigned long long int 类型。
• %Le :科学计数法表示的 long double 类型浮点数。
• %Lf :long double 类型浮点数。
• %n :已输出的字符串数量。该占位符本身不输出,只将值存储在指定变量之中。
• %o :八进制整数。
• %p :指针(用来打印地址)。
• %s :字符串。
• %u :无符号整数(unsigned int)。
• %x :十六进制整数。
• %zd : size_t 类型。
• %% :输出⼀个百分号。
• 限定宽度:printf() 允许限定占位符的最小宽度。
#include
int main()
{
printf("%5d
", 123); // 输出为 " 123"
return 0;
}
上面示例中, %5d 表示这个占位符的宽度至少为5位。如果不满5位,对应的值的前面会添加格。输出的值默认是右对齐,即输出内容前面会有空格;如果希望改成左对齐,在输出内容后面添加空格,可以在占位符的 % 的后面插入一个 - 号。
#include
int main()
{
printf("%-5d
", 123); // 输出为 "123 "
return 0;
}
上面示例中,输出内容 123 的后面添加了空格。
• 对于小数,这个限定符会限制所有数字的最小显示宽度。
#include
int main()
{
printf("%12f
", 123.45);// 输出 " 123.450000"
return 0;
}
上面示例中, %12f 表示输出的浮点数最少要占据12位。由于小数的默认显示精度是小数点后6位,所以 123.45 输出结果的头部会添加2个空格。
• 限定小数位数:输出小数时,有时希望限定小数的位数。举例来说,希望小数点后面只保留两位,占位符可以写成 %.2f 。
// 输出 Number is 0.50
#include
int main()
{
printf("Number is %.2f
", 0.5);
return 0;
}
上面示例中,如果希望小数点后面输出3位( 0.500 ),占位符就要写成 %.3f 。
• 这种写法可以与限定宽度占位符,结合使用。
// 输出为 " 0.50"
#include
int main()
{
printf("%6.2f
", 0.5);
return 0;
}
上面示例中, %6.2f 表示输出字符串最小宽度为6,小数位数为2。所以,输出字符串的头部有两个空格。
• 最小宽度和小数位数这两个限定值,都可以用 * 代替,通过 printf() 的参数传入。
#include
int main()
{
printf("%*.*f
", 6, 2, 0.5);
return 0;
}
// 等同于printf("%6.2f
", 0.5);
上面示例中, % * . * f 的两个星号通过 printf() 的两个参数 6 和 2 传入。
• 输出部分字符串:%s 占位符用来输出字符串,默认是全部输出。如果只想输出开头的部分,可以用 %.[m]s 指定输出的长度,其中 [m] 代表⼀个数字,表示所要输出的长度。
// 输出 hello
#include
int main()
{
printf("%.5s
", "hello world");
return 0;
}
上面示例中,占位符 %.5s 表示只输出字符串“hello world”的前5个字符,即“hello”。
7.scanf介绍

• 基本用法:
scanf() 函数用于读取用户的键盘输入。
程序运行到这个语句时,会停下来,等待用户从键盘输入。
用户输入数据、按下回车键后, scanf() 就会处理用户的输入,将其存入变量。
它的原型定义在头文件 stdio.h 。
scanf() 的语法跟 printf() 类似。
• scanf() 处理数值占位符时,会自动过滤空白字符,包括空格、制表符、换行符等。所以,用户输入的数据之间,有一个或多个空格不影响 scanf() 解读数据。另外,用户使用回车键,将输入分成几行,也不影响解读。
1
-20
3.4
-4.0e3
上面示例中,用户分成四行输入,得到的结果与一行输入是完全一样的。每次按下回车键以后,scanf() 就会开始解读,如果第一行匹配第一个占位符,那么下次按下回车键时,就会从第二个占位符开始解读。scanf() 处理用户输入的原理是,用户的输入先放入缓存,等到按下回车键后,按照占位符对缓存进行解读。
• 解读用户输入时,会从上一次解读遗留的第一个字符开始,直到读完缓存,或者遇到第一个不符合条件的字符为止。
#include
int main()
{
int x;
float y;
// ⽤⼾输⼊ " -13.45e12# 0"
scanf("%d", &x);
printf("%d
", x);
scanf("%f", &y);
printf("%f
", y);
return 0;
}
上面示例中, scanf() 读取用户输入时, %d 占位符会忽略起首的空格,从 - 处开始获取数据,读取到 -13 停下来,因为后面的 . 不属于整数的有效字符。这就是说,占位符 %d 会读到 -13 。第二次调用 scanf() 时,就会从上一次停止解读的地方,继续往下读取。这一次读取的首字符是 . ,由于对应的占位符是 %f ,会读取到 .45e12 ,这是采用科学计数法的浮点数格式。后面的# 不属于浮点数的有效字符,所以会停在这里。
• 由于 scanf() 可以连续处理多个占位符,所以上卖你的例子也可以写成下面这样。
#include
int main()
{
int x;
float y;
// ⽤⼾输⼊ " -13.45e12# 0"
scanf("%d%f", &x, &y);
return 0;
}
• scanf的返回值:
scanf() 的返回值是⼀个整数,表示成功读取的变量个数。
如果没有读取任何项,或者匹配失败,则返回 0 。
如果在成功读取任何数据之前,发⽣了读取错误或者遇到读取到⽂件结尾,则返回常量 EOF(-1)。
EOF - end of file 文件结束标志。
#include
int main()
{
int a = 0;
int b = 0;
float f = 0.0f;
int r = scanf("%d %d %f", &a, &b, &f);
printf("a=%d b=%d f=%f
", a, b, f);
printf("r = %d
", r);
return 0;
}
输入输出测试:

• 如果输入2个数后,按 ctrl+z (输入结束符),提前结束输入:

在VS环境中按3次 ctrl+z ,才结束了输入,我们可以看到r是2,表示正确读取了2个数值。
• 如果一个数字都不输入,直接按3次 ctrl+z ,输出的r是-1,也就是EOF

• scanf() 常用的占位符如下,与 printf() 的占位符基本一致。
• %c :字符。
• %d :整数。
• %f : float 类型浮点数。
• %lf : double 类型浮点数。
• %Lf : long double 类型浮点数。
• %s :字符串。
• %[ ] :在方括号中指定一组匹配的字符(比如 %[0-9] ),遇到不在集合之中的字符,匹配将会
停止。
上面所有占位符之中,除了 %c 以外,都会自动忽略起首的空白字符。 %c 不忽略空白字符,总是返回当前第一个字符,无论该字符是否为空格。如果要强制跳过字符前的空白字符,可以写成scanf(" %c", &ch) ,即 %c 前加上一个空格,表示跳过零个或多个空白字符。下面要特别说一下占位符 %s ,它其实不能简单地等同于字符串。它的规则是,从当前第一个非空白字符开始读起,直到遇到空白字符(即空格、换行符、制表符等)为止。因为 %s 不会包含空白字符,所以无法用来读取多个单词,除非多个 %s ⼀起使用。这也意味着,scanf() 不适合读取可能包含空格的字串,比如书名或歌曲名。另外, scanf() 遇到 %s 占位符,会在字符串变量末尾存储一个空字符

