嵌入式开发学习(第二阶段 C语言基础)
C语言:第4天笔记
内容提要
- 流程控制
- C语句
- 数据的输入与输出
流程控制
C语句
定义
- C程序是以函数为基础单位的。
- 一个函数的执行部分是由若干条语句构成的。
- C语言都是用来完成一定操作的任务。
- C语句必须依赖于函数存在。
C程序结构
C语句分类
1.控制语句
作用:用来完成一定的控制,功能
举例:
if…else…、for…、while…、do…while…、continue…、break…、switch、return、goto
2.函数调用语句
由一个函数调用+分号组成,例如:
printf("这是一个函数调用语句");
tesr();
3,表达式语句
由一个表达式+分号组成,最经典的表达式语句就是赋值语句,例如:
a = 3; //表达式
score >= 90;//表达式
score = 90; //表达式语句
4,空语句
==只有一个分号,==什么都不做,例如:
;
for(;;)
5.符合语句
用=={…}==括起来的若干个语句,例如;
{z = z + y;t = z / 100;printf("%f\n",t);//超过1条语句就是复合语句
}
数据的输入输出
数据的输入与输出是相对而言,其中:
- 从计算机向外部设备输送称数据之为输出(output)。通常的输入输出设备包括:显示器、打印机等。
- 从外部设备向计算机输送数据称之为输入(input)。通常输入设备包括:键盘、鼠标、扫描仪等。
在C语言中。输入输出设备需要使用标准的输入输出库(stdio.h)中的输入函数(scanf())、输 出函数(printf())实现,后期可以扩展其他输入输出函数。本章节接触函数属于外设IO
库函数已经被编译成了目标文件,在连接时编译源程序得到的目标文件相链接,生成可执行程序。
注意:在使用系统库函数的时候,需要用预处理指令#include 将有关的文件包含到用户资源文件中(一般放在程序的开头位置)头文件中包含了调用函数时需要的有关信息,具体的函数在编译的 时候再去链接对应的系统库。
输入输出缓冲机制
缓冲区的概念
-
缓冲区(缓存)是内存空间的一部分,用于缓存输入或输出的数据。
-
在进行输入操作时,系统从外部设备(如键盘)读取数据,先放入缓冲区,程序再从缓冲区读取数据。
输入时数据流向:外部设备→输入缓冲区→程序
-
在进行输入操作时,系统先将数据放入缓冲区,然后在特条件下(如缓冲区满、遇到特定字 符、手动刷新等)再将缓冲区中的数据输出到外部设备(如屏幕)。
输出时数据流向:程序→输入缓冲区→外部设备
缓冲区的类型
C语言的缓冲区有三种类型:
- 全缓冲:当缓冲区填满后,才进行实际的输入输出操作。例如:对磁盘文件的读写。—window全缓冲区的大小是4096字节,Linux全缓冲区大小是1024字节。
- **行缓冲:**当在输入和输出中遇到换行符(==\n或者回车)时,执行实际的输入输出操作。例如:标准输入(stdin)和(stdout)。
- **无缓冲:**不进行缓冲直接进行输入输出操作。例如:标准错误流(stderr)
缓冲区的刷新条件
缓冲区的刷新(将缓冲区的数据实际输出到外部设备)通常发生在以下情况:
- **缓冲区满:**当缓冲区写满时,会自动刷新。
- 遇到特定字符:如换行字符(\n)等
- **手动刷新:**使用fflush (stdout)函数手动刷新输出缓冲区。
- **程序关闭时:**当程序结束时,缓冲区中的数据会被刷新。
缓冲区的实际应用
- **提高效率:**通过缓冲区,可以减少与外部设备的交互次数,提高数据传输的效率
- **处理输入输出:**例如:使用scanf和printf函数时,数据先被放入缓冲区,然后按照特定的规则 从缓冲区读取或输出。
实现原理图:
简单的输入与输出
用printf函数输出数据
语法:
printf("格式控制",输出列表);
引入文件:
#include <stdio.h>
举例:
printf("i=%d,x=%d,y=%d\n",i,34,i+1);
注意:格式控制中格式化符号(%d)要和输出的数据一一对应。
参数:
- 格式控制:用一对英文双引号括起来,包含两种信息:
- 格式说明:由
%
和特定字符
组成,如%d,%f,%c等,这是格式说明 - 普通字符:作为说明性的文字、符号等,按照输入原样输出
- 格式说明:由
- 输出列表:输出列表中的各项目指出了所要输出的内容,可以是常量(字面量、符号常量、const修饰的变量)、变量、表达式。输出列表的个数,由格式控制中的格式化符号来决定。
基本的格式化符
%d
按有符号十进制整型(int)数据的实际长度输出。(十进制(0)、八进制(00)、十六进 制(0x00))
%u
按无符号十进制整型(int)数据的实际长度输出。
%c
仅输出一个字符(char)
%s
输出结果是字符串,举例:printf(“%s\n”,“CHINA”);,输出结果:CHINA
%f
以小数形式输出一个实数(涵单双精度)。整数部分全部输出,小数部分输出6位。
%e
也可以写作%E,以指数形式输出一个实数(涵单双精度)。小数点前1位非0数字,并输出6位小数。
%hd
短整型(short int/short)
%hhd
字符型的ASCII码,char数据对应的ASCII码的值,举例:char a = ‘A’;printf(“%hhd\n”,a);,输 出结果:65
%lf
双精度浮点型(double)
%ld
长整型(long int/long)
%lld
长长整型(long long int/long long)
%x
十六进制,但是十六进制的前缀0x不会打印出来,举例:printf(“%x,%#x\n”,198,198);,输 出结果:c6,0xc6
%#x
十六进制,并且十六进制的前缀0x也会打印出来,举例:0x05
%o
八进制,前缀0不会打印出来,举例:5
%#o
八进制,并且八进制的前缀0也会打印出来,举例:05
%p
打印内存的地址
%%
输出%本身
案例:
/*************************************************************************> File Name: demo01.c> Author: 小刘> Description: > Created Time: 2025年05月07日 星期三 09时57分32秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[])
{printf("%x,%#x\n",255,255);// 输出16进制整数 printf("%o,%#o\n",255,255); // 输出8进制整数 printf("%d%c",5,'%'); // 输出5%printf("%%\n");;// 输出 %printf("%f,%1f,%e\n",99.78945,99.78945,99.78945); // 输出小数return 0;
}
运行结果:
用scanf函数输入数据
基础用法
语法:
scanf("格式控制",地址列表);
注意:地址列表不能传变量、常量、表达式,只能传与之对应的内存地址(首地址),通过&+变量名
获取。
举例:
int a = 10; // 定义了一个变量a
scanf("%d",&a);//&在这里称作取地址符,&a的意思是获取变量a对应的内存地址(首地址)
作用:
将从键盘输入的数据存入所占的存储单元,存储单元有地址标识。
参数:
- ==格式控制:==含义等同于printf函数的格式控制,说明输入的数据应该使用的格式。
- ==地址列表:==是由若干个地址组成,可以是变量的地址或者字符串的地址。&是取地址变量的地址。与格式化输出一样,在格式控制中,用于说明数据格式的格式说明符以%开 头,后面紧跟具体的格式。
案例:
需求:从键盘输入整数给变量
代码:
/*************************************************************************> File Name: demo02.c> Author: 刘孟丹> Description: > Created Time: 2025年05月07日 星期三 14时25分48秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[])
{//定义三个变量a,b,c,用来接收控制台的输入int a,b,c;// 因为这里需要接收控制台键盘的输入,所以无需赋初始值// 每次在使用键盘输入之前,在控制台提示一下printf("请输入三个数字:\n");scanf("%d%d%d",&a,&b,&c);//注意:scanf格式化控制不要加\n,因为回车后会自动产生一个\n//测试printf("a=%d,b=%d,c=%d\n",a,b,c);return 0;
}
运行结果:
总结:输入多个数据的时候可以用空格、Tab键、回车键中的任意一种。
说明:
-
scanf函数中的“格式控制”后面应当是变量的地址,由取地址&和变量名共同组成,举例:
scanf("%f%d", &a, &b);
-
如果“格式控制”中除了格式说明以外还有其他字符,则在输入数据时必须在对应位置输入与之相 同的字符,举例:
scanf("%d,%d",&a, &b);
从键盘录入数据的时候,使用格式控制中的逗号分隔:3,4
scanf("%d-%d-%d",&year,&month,&day);
从键盘录入数据的时候,使用格式控制中的短横线分隔: 2025-5-7
scanf("%d年%d月%d日",&year,&month,&day);
从键盘录入数据的时候,使用格式控制中的年月日分隔:2025年05月07日
-
用%c格式输入字符时,空格和转义符(如:\n,
\:转义符
)都作为有效字符输入,应注意:scanf("%c%c%c",&a,&b,&c);
注意:其实Tab键、空格键、回车键的响应都是当做字符处理。
- 在输入数值数据(整型+浮点型)时,遇到空格、回车、Tab键或遇到非法输入(A,?…)则认为该输入结束;
scanf("%d%d%d",&a, &b, &c);
- 对于unsigned型变量所需的数据,可以%u(unsigned int)或者%d(signed int)格式输入。
复杂的输入输出
按指定格式输出数据宽度、小数位数、上下行数据按小数点对齐、用八进制、十六进制等。
输出数据格式控制
整型格式说明符
-
十进制形式(0~9)
说明符 说明 数据类型 %d和%md 用于基本整型 int %ld和%mld 用于长整型 long %u和%mu 用于无符号基本整型 unsigned int %lu和%mlu 用于无符号长整型 unsigned long -
八进制形式(0~7)
说明符 说明 数据类型 %o和%mo 用于基本整型 int %lo和% mlo 用于长整型 long -
十六进制形式(0~F)
说明符 | 说明 | 数据类型 |
---|---|---|
%x和%mx | 用于基本整型 | int |
%lx和%mlx | 用于长整型 | long |
说明:
m
表示输出整型数据所占总宽度(列数,1个字符占1列),其中:
①当实际数据的位数不到m位,数据前面将用空格填满,举例:原数据(“12”
)
②若实际数据的位数大于等于m位时,则以数据的实际位数为标准进行输出,列宽无效,举例:
原数据("12345"
)列宽m(4)的数据("12345"
)
总结:
如果实际数据列宽<m,使用空格补全m。
如果实际数据列宽>=m,输出实际数据,m失效。
一个int型整型也可以使用%u输出,反之一个unsigned型整数也可以用%d、%o、%x格式输出。 按相互赋值的规则处理。
/*************************************************************************> File Name: demo03.c> Author: 小刘> Description: > Created Time: 2025年05月07日 星期三 15时37分11秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[])
{printf("%d\n",12); // "12" 输出后,实际列宽位2printf("%6d\n",12); // " 12" 输出后,实际列宽为6,右对齐printf("%-6d\n",12); // "12 " 输出后,实际列宽为6,左对齐printf("%6d\n",1234567); // "1234567" 输出后,实际列宽为7,m失效printf("%06d\n",12); // "000012" 输出后,实际列宽为6,不足补0printf("%+d,%+d\n",12,-12); // "+12,-12" 显示正负号printf("%#06x,%#06o\n",12,12); // "0x000c,000014" 输出后,实际列宽6return 0;
}
运行效果:
字符型格式说明符
-
字符型
说明符 说明 举例 %c或者%mc 输出的字符占m列 printf(“%3c\n”,‘a’);
用法和整型的用法一致。
-
字符串型
在C语言中,支持字符串常量,不支持字符串变量
说明符 说明 %ms 输出的字符串占m列。若串长>=m,全部输出;反之在串前补空格(m为正往前补空格) 举例: printf("%6s\n","hello")
;" hello"
%-ms 输出的字符串占m列。若串长>=m,全部输出;反之在串前补空格(m为负往后补空格) 举例: printf("%-6s\n","hello")
;" hello"
%m.ns 输出的字符串占m列,只取字符串前n个字符,不足部分串前补空格。 举例: pri ntf("%6.2s\n","hello")
;" he"
%-m.ns 输出的字符串占m列。只取字符串前n个字符,不足部分串后补空格。 举例: p ri ntf("%-6.2s\n","hello")
;"he "
案例:
-
需求:字符串输出
-
代码:
/*************************************************************************> File Name: demo04.c> Author: 小刘> Description: > Created Time: 2025年05月07日 星期三 16时02分40秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[]) {printf("%3s,%7.2s,%-5.3s,%.4s\n","CHIAN","CHIAN","CHINA","CHINA");return 0; }
-
运行结果
浮点格式说明符
浮点型格式分为三种形式:
序号 | 名称 | 说明 |
---|---|---|
1 | 十进制形式 | %m.nf或者%-m.nf,m是列宽,n是保留的小数位 |
2 | 指数形式 | %m.ne或者%-m.ne,m是列宽,n是保留的尾数位 |
3 | %g或者%G形 式 | 根据数值的大小,自动选择%f或者%e中宽度较短的一种格式,不输出 无意义的0 |
解释:
在输出浮点型数据时,格式说明符中的m表示整个数据所占的列宽,n表示小数点后面所占的位数 (保留的小数位)
如果在小数点后取n位后,所规定的数据宽度m不够输出数据前面的整数部分(包括小数点),则 按实际的位数进行输出。
案例:
-
需求:输出浮点型数时,指定小数位。
-
案例:
/*************************************************************************> File Name: demo05.c> Author: 小刘> Description: > Created Time: 2025年05月07日 星期三 16时09分47秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[])
{float f = 123.155f;//f是标识符,不计入列宽,实际列宽是7printf("%8.2f,%-8.2f,%8.6f,%8.2e.%g,%G\n",f,f,f,f,f,f);//指定小数位,采用四舍五入//结果为“ 123.15”,“123.15 ”,“123.155000”,“1.23e+02”,“123.155”,“123.155”return 0;
}
-
运行结果:
这里的小数保留采用四舍五入
案例:
- 要求:求3个圆的周长,输出结果时要求按小数点对齐,取两位小数。
- 代码:
/*************************************************************************> File Name: demo06.c> Author: 小刘> Description: > Created Time: 2025年05月07日 星期三 16时26分57秒************************************************************************/#include <stdio.h>
#define PI 3.14int main(int argc,char *argv[])
{double r1 = 1.53, r2 = 21.83,r3 = 123.71,s1,s2,s3;s1 = 2.0 * PI * r1;s2 = 2.0 * PI * r2;s3 = 2.0 * PI * r3;printf("%f\n%f\n%f\n",s1,s2,s3);return 0;
}
案例:
-
要求:设有如下C程序
-
代码
#include "stdio.h"main(){ double x=34.567;printf("x=%f\n",x);// 34.567000printf("x=%d\n",x);// 27263,这种写法错误,自动类型转换异常printf("x=%d\n",(int)x);// 34}
-
这个程序的实际运行结果为
x=34.567000
x=27263
x=34
-
说明
显然,这个程序中的第二个格式输出语句输出的结果是错误的,这是因为在第二个格式输出语 句中,格式说明符%d是基本整型格式说明符,而输出项目是双精度型的数据,它们是不匹配 的。
输出数据格式控制
整型格式说明符
-
十进制形式(0~9)
说明符 说明 数据类型 %d和%md 用于基本整型 int %ld和%mld 用于长整型 long %u和%mu 用于无符号基本整型 unsigned int %lu和%mlu 用于无符号长整型 unsigned long -
八进制形式(0~7)
说明符 说明 数据类型 %o和%mo 用于基本整型 int %lo和% mlo 用于长整型 long -
十六进制形式(0~F)
说明符 | 说明 | 数据类型 |
---|---|---|
%x | 用于基本整型 | int |
%lx | 用于长整型 | long |
总结:
① 用于输入与输出整型数据的格式说明符是完全一致的。
② 与输出情形一样,对于八进制与十六进制的输入格式,主要用于输入无符号的整型数据。
浮点型格式说明符
说明符 | 说明 | 数据类型 |
---|---|---|
%f或者%e | 用于单精度浮点型 | float |
%lf | 用于双精度浮点型 | double |
总结:
①与输出不同,输入时无论单精度还是双精度,都不能用m.n来指定输出的宽度和小数点后保留的位数。
②可以指定输入数据所占的列数,系统自动按它截取所需的数据,如;
scanf("%3d%3d",&a,&b);
输入测试数据:1234567
结果:a=123,b=456
explain:从输入的数据1234567中,截取前三列(123),赋值给a,再截取3列(456),赋值给b,剩余的7舍去
③若在%后有一个==*
和一个数字==,表示跳过指定的列数,如:
scanf("%2d%*3%3d",&a,&b);
输入测试数据:12345678
结果:a=12,b=678
explain:从输入的数据12345678中,截取前2列(12),赋值给a,再跳转3列,然后截取3列(678),赋值给b
案例:
/*************************************************************************> File Name: demo06.c> Author: 小刘> Description: > Created Time: 2025年05月07日 星期三 16时26分57秒************************************************************************/#include <stdio.h>
#define PI 3.14int main(int argc,char *argv[])
{int a,b;scanf("%3d%3d",&a, &b);// 输入:1234567printf("a=%d,b=%d\n"); // 输出:a=123,b=456return 0;
}
说明:
当用于输入整型数据的格式说明符中没有宽度说明时,则在具体输入数据时分为以下两种情况:
① 如果各格式说明符之间没有其它字符,则在输入数据时,两个数据 之间用"空格"、或"Tab"、 或"回车"来分隔。
② 如果各格式说明符之间包含其它字符,则在输入数据时,应输入与 这些字符相同的字符作为间 隔。
例如,设有如下说明
int a,b;
float c,d;
现要利用格式输入函数输入a=12,b=78,c=12.5,d=7.6
。 采用不同的格式说明,其输入数据的形 式也是不同的。分别说明如下:
-
若输入语句为
scanf("%d%d%f%f",&a,&b,&c,&D. ;
(即格式说明符中没有宽度说明, 各格式说明符之间也没有其他字符。)则输入数据的形式应为
12 78 12.5 7.6
↲ (两个数据之间用空格来分隔,当然也可用“Tab”或“回车”来分隔。) -
若输入语句为
scanf("%d,%d,%f,%f",&a,&b,&c,&D.
; (格式说明符中没有宽度说明,但各 格式说明符之间有其它字符,即逗号)则输入数据的形式应为
12,78,12.5,7.6
↲ (即在输入的两个数据之间同时要输入逗号。) -
若输入语句 s
canf("a=%d,b=%d,c=%f,d=%f",&a,&b,&c,&D. ;
(即格式说明符中没有宽度 说明,但各格式说明符之间有其它字符。)输入数据的形式应为
a=12,b=78,c=12.5,d=7.6
↲ (即在输入的两个数据之间同时要输入这 些非格式说明符的字符。) -
在用于输入的实型格式说明符中不能用m.n来指定输入的宽度和小数点后的位数(这是与输出的 不同之处)。
例如:
scanf("%7.2f",&a); × 此用法是错误的
-
为了便于程序执行过程中从键盘输入数据,在一个C程序开始执行时,系统就在计算机内存中开 辟了一个输入缓冲区,用于暂存从键盘输入的数据。开始时该输入缓冲区是空的。当执行到一 个输入函数时,就检查输入缓冲区中是否有数据;
扩展:
说明符 | 含义 | 示例 |
---|---|---|
%n | 已读取的字符数 | scanf(“%d%n”, &num, &count); |
代码:
/*************************************************************************> File Name: demo07.c> Author: 刘孟丹> Description: > Created Time: 2025年05月07日 星期三 16时57分15秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[])
{int num,count;printf("请输入一个数据:\n");scanf("%d%n",&num,&count);printf("num=%d,count=%d\n",num,count);return 0;
}
运行结果;
7.2f",&a); × 此用法是错误的`
- 为了便于程序执行过程中从键盘输入数据,在一个C程序开始执行时,系统就在计算机内存中开 辟了一个输入缓冲区,用于暂存从键盘输入的数据。开始时该输入缓冲区是空的。当执行到一 个输入函数时,就检查输入缓冲区中是否有数据;
扩展:
说明符 | 含义 | 示例 |
---|---|---|
%n | 已读取的字符数 | scanf(“%d%n”, &num, &count); |
代码:
/*************************************************************************> File Name: demo07.c> Author: 刘孟丹> Description: > Created Time: 2025年05月07日 星期三 16时57分15秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[])
{int num,count;printf("请输入一个数据:\n");scanf("%d%n",&num,&count);printf("num=%d,count=%d\n",num,count);return 0;
}
运行结果;