2.1 数据类型-常量-变量(整型-浮点-字符)
- 数据类型
- 常量
- 变量(整形-浮点-字符)
1.数据类型
C语言中有许多关键字,在后面的章节中将详细介绍这些关键字(不用去记),避免命名变量时与关键字重名(完全不用担心,CLion开发环境重名后会提醒)。
2.常量
常量是指在程序运行过程中,其值不发生变化的量。常量又可分为整型、实型(也称浮点型)、字符型和字符串型。
整型常量、实型常量、字符型常量是在编译时可以直接编入代码段的常量;例如,在字符串"你好"中,双引号中间的汉字就是字符串型常量,无论双引号中间的内容是ASCI码字符,还是汉字或其他国家的文字等,都是字符串型常量(考研不会考汉字的字符串常量,无需掌握)。
- 字符型必须单引号 ' ' 括住,且单引号内必须有且只有一个字符。
3.变量
变量代表内存中具有特定属性的一个存储单元,它用来存放数据,即变量的值。这些值在程序的执行过程中是可以改变的。
变量名实际上以一个名字代表一个对应的存储单元地址。编译、链接程序时,由编译系统为每个变量名分配对应的内存地址(就是空间)。从变量中取值实际上是通过变量名找到内存中存储单元的地址,并从该存储单元中读取数据。
变量的命名规定如下:C语言规定标识符只能由字母、数字和下画线三种字符组成,并且第一个字符必须为字母或下画线。
正确示范:sum,total,month,Student_name,lotus_1_2_3,BASIC,li_ling
错误示范:M.D.John,¥123,3D64,a>b
编译系统认为大写字母和小写字母是不同的字符,因此C语言要求对所有用到的变量做强制定义,即“先定义,后使用”。同时在选择变量名和其他标识符时,应尽量做到“见名知意”,即选择具有含义的英文单词(或其缩写)作为标识符。注意,变量名不能与关键字同名!
4.整型数据
4.1符号常量
定义一个整型变量时要使用关键字int。
- 错误:左操作数必须为左值(变量)
最终输出结果是7,原因是符号常量PI是直接替换的效果,因此不可以写PI = 8。
3+2*2 = 7
- #define处不需要加 ;
#include <stdio.h>
#define PI 3+2
int main()
{
int i = PI*2;
printf("i=%d\n", i);
}
4.2整型变量
- 变量 i 是4个字节。
#include <stdio.h>
//符号常量
#define PI 3+2
int main() {
//PI = 20;
int i = PI*2;
printf("i=%d\n", i);
printf("i size=%d\n", sizeof(i));
return 0;
}
5.浮点型数据
5.1浮点型常量
表示浮点型常量的形式有两种,其中e代表10的幂次,幂次可正可负。
小数形式 | 0.123 |
指数形式 | 3e-3(为3x10^-3,即0.003) |
- 字母e(或E)之前必须有数字,且e后面的指数必须为整数。
正确示例:1e3,1.8e-3,-123e-6,-.1e-3 (小数点前的0可以省略)
错误示例:e3,2.1e3.5,.e3,e
5.2浮点型变量
通过float f来定义浮点变量
- f 占用4个字节的空间。
#include <stdio.h>
int main() {
float f = 3e-3;
float fl = -.1e-3;
printf("f=%f\n",f);
printf("fl=%f\n",fl);
return 0;
}
6.字符型数据
6.1字符型常量
用单引号括起来的一个字符是字符型常量,且只能包含一个字符!例如,'a','A','1',' '是正确的字符型常量,而 'abc'、"a”、" "是错误的字符型常量。下表中给出了各种转义字符及其作用。以“\”开头的特殊字符称为转义字符,转义字符用来表示回车、退格等功能键。
转义字符 | 作用 |
\n | 换行 |
\b | 退格 |
\\ | 反斜杠 |
6.2字符数据在内存中的存储形式及其使用方法
字符型变量使用关键字char进行定义,一个字符型变量占用1字节大小的空间。一个字符常量存放到一个字符型变量中时,实际上并不是把该字符的字型放到内存中,而是把该字符的ASCII码值放到存储单元中。打印字符型变量时,如果以字符形式打印,那么计算机会到ASCII码表中查找字符型变量的ASCII码值,查到对应的字符后会显示对应的字符,这样,字符型数据和整型数据之间就可以通用。字符型数据既可以以字符形式输出,又可以以整数形式输出,还可以通过运算获取想要的各种字符。
#include <stdio.h>
//大写变小写
int main() {
char c = 'A';
printf("%c\n", c+32);
printf("%d\n",c);
return 0;
}
对于字符型变量,无论是赋ASCII码值还是赋字符,使用%c打印输出时得到的都是字符,使用%d打印输出时得到的都是ASCII码值。将小写字母转换为大写字母时,由课件最后的ASCII码表发现小写字母与大写字母的差值为32,因此将c减去32就可以得到大写字母A。
#include <stdio.h>
int main() {
char c;
scanf("%c", &c);
printf("c = %c",c);
return 0;
}
char定义的字符型变量,有且仅有一个字符。
%c为字符值,%d为十进制数值值
7.字符串型常量
字符串型常量是由一对双引号括起来的字符序列。例如,"CHINA”、"a"和"$123.45"是合法的字符串型常量,我们可用语句printf("How do you do.")输出一个字符串。但要注意的是,'a'是字符型常量,而"a"是字符串型常量,二者是不同的。
例如,如果先用语句char c定义字符型变量c,后令c="a"或c="CHINA",那么这样的赋值都是非法的,原因是不可以将字符串型常量赋值给字符型变量。C语言中没有定义字符串型变量的关键字,介绍字符数组时我们将详细讲解如何存放字符串。
C语言规定,在每个字符串型常量的结尾加一个字符串结束标志,以便系统据此判断字符串是否结束。C语言规定以字符 '\0' 作为字符串结束标志。
例如,字符串型常量"CHINA"在内存中的存储结果如下图所示,它占用的内存单元不是5个字符,而是6个字符,即大小为6字节,最后一个字符为 '\0' 。然而,在输出时不输出 '\0' ,因为 '\0' 无法显示。
8.ASCII表
2.2 混合运算-printf讲解
- 混合运算
- printf讲解
1.混合运算
类型强制转换场景
整型数进行除法运算时,如果运算结果为小数,那么存储浮点数时一定要进行强制类型转换。
#include <stdio.h>
int main() {
int i = 5;
float f = i/2;
printf("f=%f\n",f);
float k = (float)i/2;
printf("k=%f\n",k);
return 0;
}
f得到的值为2,k得到的值是2.5,因为当我们整数做除法时,默认进行整除,要得到小数,需要首先进行强制类型转换操作。
2.printf函数介绍
printf函数可以输出各种类型的数据,包括整型、浮点型、字符型、字符串型等,实际原理是printf函数将这些类型的数据格式化为字符串后,放入标准输出缓冲区,然后将结果显示到屏幕上。
printf语法
#include <stdio.h>
int printf(const char *format, ...);
printf 函数根据 format 给出的格式打印输出到 stdout(标准输出)和其他参数中。
字符串格式(format)由两部分组成:显示到屏幕上的字符和定义 printf 函数显示的其他参数。我们可以指定一个包含文本在内的format 字符串,也可以是映射到 printf 的其他参数的“特殊”字符。
#include <stdio.h>
int main() {
int age = 21;
printf("Hello %s, you are %d years old\n", "Bob", age);
return 0;
}
其中,%s表示在该位置插入首个参数(一个字符串),%d表示第二个参数(一个整数)应该放在哪里。不同的%codes表示不同的变量类型,也可以限制变量的长度。
格式化输出
代码 | 格式 |
%c | 字符 |
%d | 带符号整数(正负零) |
%f | 浮点数 |
%s | 一串字符 |
%u | 无符号整数 |
%x | 无符号十六进制数,用小写字母 |
%X | 无符号十六进制数,用大写字母 |
%p | 一个指针 |
%% | 一个‘%’符号 |
位于%和格式化命令之间的一个整数被称为最小字段宽度说明符,通常会加上空格来控制格式。
- 用 %f 精度修饰符指定想要的小数位数。例如,%5.2f会至少显示5位数字并带有2位小数的浮点数。
- 用 %s 精度修饰符简单地表示一个最大的长度,以补充句点前的最小字段长度。
%0.2f 保留两位小数,但不限制浮点数输出的整体长度
printf 函数的所有输出都是右对齐的,除非在%符号后放置了负号。例如,%-5.2f 会显示5位字符、2位小数位的浮点数并且左对齐。
#include <stdio.h>
int main() {
int i = 10;
float f = 96.3;
printf("student number=%3d, score=%5.2f\n", i, f); //默认右对齐,加一个负号为左对齐
printf("student number=%-3d, score=%5.2f\n", i, f);
i = 100;
f = 98.31;
printf("student number=%3d, score=%5.2f\n", i, f);
return 0;
}
2.3 整型进制转换
- 整型常量的不同进制表示
整型常量的不同进制表示
计算机中只能存储二进制数,即 0 和 1 ,而在对应的物理硬件上则是高、低电平,为了更方便地观察内存中的二进制数情况,除我们正常使用的十进制数外,计算机还提供了十六进制数和八进制数。
首先,在计算机中,1字节(byte)为8位(bit),1位即二进制的1位,它存储0或1。int型常量的大小为4字节,即32位。
设有二进制数 0100 1100 0011 0001 0101 0110 1111 1110,其最低位是2的零次方,代表数值的最高位是2的 30 次方,最高位为符号位,符号位为1时是补码,将在高级阶段的补码部分讲解。
上面的二进制数对应的八进制数是011414253376,它以 0 开头标示,数位的变化范围是 0~7 。由于实际编程时,识别八进制数时前面需要加 0,所以在前面加了一个0。
上面的二进制数对应的十六进制数是0x4C3156FE,它以 0x 开头标示,数位的变化范围是0~9 和 A~ F ,其中A代表10,F代表15,对应的二进制数每 4 位转换为 1 位十六进制数。十六进制在观察内存时需要频繁使用。
上面的二进制数对应的十进制数是1278301950,具体计算需要以 2 的幂次相加依次来计算(为1的位置就需要 2 的幂次,为 0 不需要),最好通过计算器来进行。
目前我们执行到语句int i=123,变量 i 会在内存上被分配空间,大小为4字节,其中 i 的值变为7b(我们以十六进制方式查看内存),其十进制值为7x16+11=123。i 的值是0x0000007b。
十进制:123
二进制:0000 0000 0000 0000 0000 0000 0111 1011
八进制:173
十六进制:7B
那么十进制数 123 如何转换为二进制数呢?方法是让 123 不断地除以 2 ,并把余数写在右边,把商写在下方,直到商为0,然后逆序写出所有余数,即可得到转换后的二进制数1111011,详细过程如下图所示。对应的十六进制数为 7b ,十进制数转换为十六进制数的方式是除以 16 ,十进制数转换为八进制数的方式是除以 8 。
#include <stdio.h>
int main() {
int i = 123;
printf("%d\n", i); //十进制输出
printf("%o\n", i); //八进制输出
printf("%x\n", i); //十六进制输出
return 0;
}
单步调试
本应为0000007b,为什么显示结果为7b000000呢?原因是英特尔的CPU采用了小端方式进行数据存储,因此低位在前、高位在后。
- 赋值以十进制为主,八进制最前面加0,十六进制最前面加0x
计算器的使用
手动转换一个数的进制后,若不知道转换后的进制数是否正确,则可在 Windows操作系统下选择“开始”→“计算器”,打开“计算器”,然后选择菜单项“查看”→“程序员”,得到如下图所示的计算器。输入一个十进制数后,单击“十六进制”“八进制”或“二进制”,即可得到对应进制的转换结果。
2.4 scanf读取标准输入
- scanf函数的原理
- 多种数据类型混合输入
1.常用的数据输入/输出函数
如下图所示,程序员可以给程序输入数据,程序处理后会返回一个输出。C语言通过函数库读取标准输入,然后通过对应函数处理将结果打印到屏幕上。前面我们学习了 printf 函数,理解了通过 printf 函数可以将结果输出到屏幕上。下面详细讲解标准输入函数 scanf 。
1.1scanf函数的原理
C语言未提供输入/输出关键字,其输入和输出是通过标准函数库来实现的。C语言通过scanf 函数读取键盘输入,键盘输入又被称为标准输入。当 scanf 函数读取标准输入时,如果还没有输入任何内容,那么scanf函数会被卡住(专业用语为阻塞)。
- 输入输出全是字符串类型,用“%?”的形式是告知 scanf 将接收到的字符串传唤成什么类型,与后面“&?”的类型对应一致。
- 只定义但不赋值,断点不会停留(没有赋值,实际上不需要执行)
- 在scanf处断点(阻塞),点击控制台输入。输入后必须回车,回车才能触发scanf函数的执行。
- 输入10之后回车 ——> 10\n
- scanf %d 读走10,剩余\n
- scanf %c 读走\n
Ctrl + / 快捷注释
scanf %d %f 发现里边有\n空格,忽略
scanf %c 不忽略内容,会读取\n
执行时输入20,然后回车,显示结果如图所示。为什么第二个scanf函数不会被阻塞呢?其实是因为第二个scanf函数读取了缓冲区中的 '\n' ,即 scanf("%c", &c) 实现了读取,打印其实输出了换行,所以不会阻塞。
#include <stdio.h>
//scanf用来读取标准输入,scanf把标准输入的内容,
//需要放到某个变量空间里,因此变量必须取地址。
int main() {
int i;
char c;
scanf("%d", &i); //注意一定要取地址
printf("i=%d\n", i);
scanf("%c", &c);
printf("c=%c\n", c); //输出字符变量c
return 0;
}
但是如果我们将上面例子中注释的fflush(stdin)打开,就会发现第二个scanf("%c", &c)会阻塞,这是什么原因呢?下面介绍缓冲区原理。
行缓冲:在这种情况下,当在输入和输出中遇到换行符时,将执行真正的 I/O 处理操作。这时,我们输入的字符先存放到缓冲区中,等按下回车键换行时才进行实际的 I/O 操作。典型代表是标准输入缓冲区(stdin)和标准输出缓冲区(stdout),printf使用的是stdout。
#include <stdio.h>
int main() {
int i;
char c;
scanf("%d", &i); //注意一定要取地址
printf("i=%d\n", i); //把标准缓冲区的整型数读走
fflush(stdin); //清空标准输入缓冲区
scanf("%c", &c);
printf("c=%c\n", c); //输出字符变量c
return 0;
}
如上面的例子所示,我们向标准输入缓冲区中放入的字符为 '20\n' ,输入 '\n'(回车)后,scanf函数才开始匹配,scanf函数中的 %d 匹配整型数20,然后放入变量 i 中,接着进行打印输出,这时 '\n' 仍然在标准输入缓冲区(stdin)内,如果第二个 scanf 函数为 scanf("%d", &i) ,那么依然会发生阻塞,因为scanf函数在读取整型数、浮点数、字符串(后面介绍数组时讲解字符串)时,会忽略 '\n'(回车符)、空格符等字符(忽略是指 scanf 函数执行时会首先删除这些字符,然后再阻塞)。scanf函数匹配一个字符时,会在缓冲区删除对应的字符。因为在执行scanf("%c", &c) 语句时,不会忽略任何字符,所以scanf("%c", &c)读取了还在缓冲区中残留的 '\n' 。
- 输入20之后回车 ——> 20\n
- scanf %d 读走20,剩余\n
- scanf %f 匹配浮点数,不匹配字符,先清空缓冲区,然后才会阻塞
scanf会阻塞,因为标准输入缓冲区为空。
#include <stdio.h>
int main() {
int i;
char c;
float f;
scanf("%d", &i); //注意一定要取地址
printf("i=%d\n", i); //把标准缓冲区的整型数读走
scanf("%f", &f);
printf("f=%f\n", f);
return 0;
}
多写一句。
#include <stdio.h>
//scanf会卡住,因为标准输入缓冲区为空。
int main() {
int i;
char c;
float f;
scanf("%d", &i); //注意一定要取地址
printf("i=%d\n", i); //把标准缓冲区的整型数读走
fflush(stdin); //清空标准输入缓冲区
scanf("%c", &c);
printf("c=%c\n", c); //输出字符变量c
printf("c=%d\n", c);
return 0;
}
1.2多种数据类型混合输入
当我们让 scanf 函数一次读取多种类型的数据时,对于字符型数据要格外小心,因为当一行数据中存在字符型数据读取时,读取的字符并不会忽略空格和 '\n'(回车符),所以使用方法如下例所示。编写代码时,我们需要在 %d 与 %c 之间加入一个空格。输入格式和输出效果如下图所示,scanf函数匹配成功了4个成员,所以返回值为4,我们可以通过返回值来判断 scanf 函数匹配成功了几个成员,中间任何有一个成员匹配出错,后面的成员都会匹配出错。
正确读入。
错误读入。
调试
scanf有返回值,返回值为匹配成功的个数,ret
解决方案:%d %c%f
%d与%c之间空格,输入时为100 a 98.2,输入格式的空格和输入的空格匹配上。
#include <stdio.h>
int main() {
int i, ret;
float f;
char c;
ret = scanf("%d %c%f", &i, &c, &f); //ret是指scanf匹配成功的个数
printf("i=%d, c=%c, f=%5.2f\n", i, c, f);
return 0;
}
2.5 OJ网站如何使用及本节课OJ作业说明
- OJ网站如何使用
- 本节课的OJ作业说明
1.OJ网站如何使用
OJ 是英文 Online Judge 的缩写,中文翻译过来是在线判题,当用户将自己编写的代码贴入网页中后,点击提交,就可以自动判断是否正确。在浏览器输入 http://oj.lgwenda.com/ 就可以看到王道C督学营的 OJ 网站。
点击Problem做题。
2.第②章的OJ作业说明
第②章《数据的类型、数据的输入输出》,3道题。讲解视频为3.1。
2.6 本章习题
判断
2.1 数据类型-常量-变量(整型-浮点-字符)
1. int来定义整型变量,float定义浮点型变量,char用来定义字符变量。
A. 正确 B. 错误
解释:这个是语法基础,需要记住。
2. 常量在程序执行过程中可以被修改。
A. 正确 B. 错误
解释:常量在执行过程中不可以被修改的,变量在执行过程中才可以修改。
3. Long-ge是一个正确的变量名。
A. 正确 B. 错误
解释:变量命名只可以用字母,数字,下划线,而Long-ge中含有了横线。
4. int占用4个字节空间,float占用4个字节空间,char占用1个字节空间。
A. 正确 B. 错误
解释:不同类型变量占用空间大小需要记住。
5. 字符串常量“hello”占用的空间是5个字节。
A. 正确 B. 错误
解释:字符串常量“hello”占用的空间是6个字节,因为除了hello这5个字母占用5个字节的空间外,还有一个结束符 '\0' ,也需要占用1个字节的空间,因此总计6个字节。
2.2 混合运算-printf讲解
1. int i = 5;float f = i/2;那么f的值为2.5。
A. 正确 B. 错误
解释:因为i是整型,所以除2是整除,得到的值是2,如果要得到2.5,是(float)i/2。
2. printf 的 format 参数中含有%c代表要输出字符,%d代表整型,%f代表浮点,%s代表字符串。
A. 正确 B. 错误
解释:printf的输出格式需要记住,这样OJ才能熟练应对,包括对于机试是很重要的。
3. printf的输出默认是左对齐。
A. 正确 B. 错误
解释:printf的输出默认是右对齐,不是左对齐。如果需要左对齐,那么加入负号。%-3d
2.3 整型进制转换
1. 程序运行时,整型是以二进制在内存中存储的,十进制,八进制,十六进制是为了人类使用在设计的。
A. 正确 B. 错误
解释:正确的,这个需要记住,计算机是只能识别0和1的,其他进制的设计是为了方便我们使用而设计。
2. 十进制是0-9,八进制是0-8,十六进制是0-9,A-F,请问是否正确。
A. 正确 B. 错误
解释:八进制是0-7,总计8种变化情况,并不是0-8。
3. 整型数124对应的十六进制值是0x7c
A. 正确 B. 错误
解释:把一个10转为16进制,只要不断除16即可,124除16,商是7,余数是12,而12就是c,因此是0x7c。
2.4 scanf读取标准输入
1. scanf读取标准输人,%d 用来匹配 int 整型,%f 匹配 float 类型,%c 匹配字符。
A. 正确 B. 错误
解释:正确的,这个需要记住。
2. 有如下代码,int i; scanf("%d", i);想读取一个数据到变量 i 中,是否正确。
A. 正确 B. 错误
解释:通过scanf读取标准输人时,我们需要对变量 i 进行取地址,代码是scanf("%d", &i),因为scanf函数是把对应的数据放人变量所在的空间中,因此需要对应变量的地址。
3. scanf("%d", &i),当我们输入10回车后,i 读取到了10,那么标准缓冲区中已经空了。
A. 正确 B. 错误
解释:这时标准缓冲区中并没有空,里边还有\n字符。
4.
int i;
char c;
float f;
scanf("%d %c%f", &i, &c, &f);
当混合读取时,因为 %c 不能忽略空格和n所以需要在期前面加一个空格。
A. 正确 B. 错误
解释:这种操作要记住,对于做OJ的题目,考研机试非常重要。
作业1
要求:
代码:
#include <stdio.h>
int main() {
printf("hello wangdao\n");
return 0;
}
截图:
作业2
要求:
代码:
#include <stdio.h>
int main() {
int a, b;
scanf("%d %d", &a, &b);
// printf("%d+%d\n", a, b); //输出1+4
printf("%d\n", a+b);
return 0;
}
截图:
作业3
要求:
代码:
#include <stdio.h>
//无需判断输入值是否合法
int main() {
int a;
scanf("%d", &a);
printf("%c\n",a);
return 0;
}