C语言程序设计,知识一遍过
🔥温馨提示🔥:使用电脑端阅读,获取更好体验🚀
文章目录
- C语言程序设计,知识一遍过
- 顺序结构
- 表达式语句
- 字符的输入与输出
- 格式化输入/输出函数
- 选择结构
- 关系运算符和关系表达式
- 逻辑运算符和逻辑表达式
- if语句及其构成的选择结构
- 条件运算符构成的选择结构
- switch语句
- switch语句和 break 语句组成的选择结构
- 循环结构
- while 和 do…while 循环结构
- for 循环结构
- 循环结构的嵌套
- continue和break语句
- 预处理指令
- 宏定义
- 不带参数的宏定义
- 带参数的宏定义
- 条件编译
- 文件包含
- C头文件
- 引用头文件的语法
- 引用头文件的操作
- 只引用一次头文件
- 有条件引用
顺序结构
表达式语句
-
表达式语句:在表达式后面添加分号“ ;”,构成表达式语句。
执行完表达式语句后,表达式的值会被丢弃。因此,若表达式不修改操作数的值,表达式语句就没有什么实际意义。 -
复合语甸:把多个语句用花括号 “{ }” 括起来所构成的一个语句组。
一个复合语句在语法上视为一条语句。
比如:{i=1; i++; a=i;} -
空语句:只有一个分号 “;” 组成的语句。
比如:main() { ; } 其中的分号是一个空语句,但程序执行时不产生任何动作。
字符的输入与输出
-
字符输出函数 putchar()
格式:putchar(ch),其作用是向终端输出一个字符。 -
字符输入函数 getchar( )
功能:是从终端输人一个字符。
getchar() 函数没有参数,函数值就是从输入设备得到的字符。
格式化输入/输出函数
-
输出函数 printf()
printf() 函数是C语言提供的标准输出函数,它的作用是向终端(或系统隐含指定的输出设备)按指定格式输出的若干数据。
一般形式:printf(格式控制, 输出列表);
格式控制:是用双引号括起来的字符串。它包括两种信息:-
格式转换说明:由 “%” 和格式字符组成。
-
需要原样输出的字符也写在格式控制内。
输出列表:需要输出的一些数,可以是常量、变量或表达式。输出列表中的各输出项用逗号隔开。
-
格式控制符 | 描述 | 示例 |
---|---|---|
%d 或 %i | 输出十进制整数 | printf("%d", 123); 输出 123 |
%u | 输出无符号十进制整数 | printf("%u", 456U); 输出 456 |
%o | 输出八进制整数 | printf("%o", 0123); 输出 123 |
%x 或 %X | 输出十六进制整数(小写/大写) | printf("%x", 0xabc); 输出 abc |
%f | 输出浮点数(默认保留六位小数) | printf("%f", 123.456); 输出 123.456000 |
%e 或 %E | 以科学计数法输出浮点数 | printf("%e", 123.456); 输出 1.234560e+02 |
%g 或 %G | 根据值的大小自动选择 %f 或 %e | printf("%g", 123.456); 输出 123.456 |
%c | 输出一个字符 | printf("%c", 'A'); 输出 A |
%s | 输出一个字符串 | printf("%s", "Hello"); 输出 Hello |
%% | 输出一个 % 符号 | printf("%%"); 输出 % |
%p | 输出指针的值以十六进制表示 | int a=3,*p=&a,printf("%p", p); 输出 变量a的地址 |
%zu | 输出 sizeof 的结果 | printf("%zu",sizeof(int)); 输出 4 |
此外,printf
还允许通过额外的格式说明符来指定字段宽度、精度、对齐方式等,例如:
%10d
:输出宽度为10的十进制整数,不足部分用空格填充。%-10d
:输出宽度为10的十进制整数,不足部分用空格填充,且左对齐。%.2f
:输出浮点数,保留两位小数。%10.2f
:输出宽度为10的浮点数,包括小数点和小数部分,总共保留两位小数,不足部分用空格填充。
这些格式控制符可以组合使用,以满足不同的输出需求。
格式说明与输出项在类型上与个数上保持一致。
-
输入函数 scanf()
一般形式:scanf(格式控制,地址表列);
格式控制:同 printf() 函数。
地址表列:由若干个变量地址组成,既可以是变量的地址,也可以是字符串的首地址。
比如:scanf(“%d%f%If”,&a,&b,&e),输入整型变量a、单精度型变量b、双精度型变量c
说明:-
scanf() 函数中的格式说明也是以%开始,以一个格式字符结束,中间可以加入附加的字符。
-
对 unsigned 型变量的数据,可以用%d、%o、%x格式输入。
-
在 scanf() 函数中格式字符前可以用一个整数指定输入数据所占宽度,但对于输入实型数则不能指定其小数位的宽度。
-
在格式控制串中,格式说明的个数应该与输入项的个数相等且要类型匹配。
-
scanf() 函数中的输入项只能是地址表达式,而不能是变量名或其他内容。
如果在 "格式控制” 字符串中除了格式说明以外还有其他字符,则在输入数据时应输入与这些宇符相同的字符。
在用 “%c” 格式输入字符时,空格字符和转义字符都可作为有效字符输入。
在输入数据时,若实际输入数据少于输入项个数,scanf()函数会等待输入,直到满足条件或遇到非法字符才结束;若实际输入数据多于输入项个数,多余的数据将留在缓冲区备用,作为下一次输入操作的数据。
在输入数据时,遇到以下情况将认为输人结束:“空格” “回车” 或 “跳格” ( “Tab” )键,上述字符统一可称为“间隔符”。
选择结构
关系运算符和关系表达式
-
关系运算符
C语言提供了6种关系运算符:小于(<)、小于等于(<=)、大于等于( >=)、大于(>)、等于(==)、不等于(!=)。
结合性:自左向右。
优先级:前4种关系运算符的优先级别相同,后2种优先级相同。前4种优先级高于后种。关系运算符的优先级低于算术运算符,高于赋值运算符。 -
关系表达式
由关系运算符构成的表达式,称为关系表达式。关系运算符两边的运算对象可以是C语言语句中任意合法的表达式。比如:(a>b)!=c,a>=b,(a1)<(b2)都是合法的关系表达式。
- 关系运算符的值为 “逻辑值”,只有两种可能值:整数0或者整数1。
- 关系运算符两边值的类型不一致时,系统将自动转换。
逻辑运算符和逻辑表达式
-
逻辑运算符
C语言提供了3种逻辑运算符:逻辑与(&&)、逻辑或(|)、逻辑非(!)。其中,“&&” 和 “||” 是双目运算符,而 “!” 是单目运算符,要求必须出现在运算对象的左边。
结合性:自左至右。
优先级:“!” > “&&” > “||” (非与或)。
“!” > 算术运算符 > 关系运算符 > “&&” > “||” > 赋值运算符。 -
逻辑表达式
逻辑表达式由逻辑运算符和运算对象组成。
参与逻辑运算的对象可以是一个具体的值,还可以是C语言中任意合法的表达式。
逻辑表达式的运算结果为1(真)或者为0(假)。
A&&B 运算中,只有 A、B 同为真时才为真。同真为真,剩余为假(一假则假
)。
A||B 运算中,只有 A、B 同为假时才为假。同假为假,剩余为真(一真则真
)。
关系运算符不能连用。比如:1<x<5。在数学中可以使用,但在这里不能使用,可改写成1<x && x<5。
&&和||的短路原则,以&&为例,要使(表达式1)&&(表达式2)结果为真则要求表达式1和表达式2都为真,如果表达式1为假,则不计算表达式2
if语句及其构成的选择结构
-
if(表达式) 语句
if是C语言的关键字。表达式两侧的括号不可少,并且只能是圆括号。
紧跟着的语句,称为 “子句“,如果在 “子句” 中需要多个语句,则应该使用大括号把一组语句括起来构成复合语句。 -
if(表达式) 语句1
else 语句2
上述语句含义是:如果表达式成立,执行语句1;否则执行语句2。 -
if(表达式1) 语句1
else if(表达式2) 语句2
else if(表达式3) 语句3
…
else 语句n
上述语句含义是:满足表达式1时,执行语句1,不满足表达式1,但满足表达式2时,执行语句2,表达式1和2都不满足执行语句3,如果所有表达式都不成立,则执行语句n。
else 必须与 if 配对,共同组成 if…else 语句,这在程序题中需要注意。例如:
#include<stdio.h> int main() { int a,b; printf("please input a,b: "); scanf("%d,%d",&a,&b); if(a == b) printf("a=b\n"); else if(a>b)printf("a>b\n"); else printf("a<b"); }
为了避免嵌套的if-else语句二义性,C语言规定else总是与在其前尚未配对的最近的if组成配对关系。
条件运算符构成的选择结构
条件运算符:?:
条件表达式的一般形式:表达式1 ? 表达式2 : 表达式3
求解过程:先求表达式1 的值,当表达式1 的值是非 0 时,以表达式2 的值作为整个条件表达式的值;当表达式1的值是0时,以表达式3 的值作为整个条件表达式的值。
优先级:条件运算符高于赋值运算符,但低于逻辑运算符、关系运算符和算术运算符。
比如:a=t > 1? 2 : 3,如果判定 t>1为真,即非0时,条件表达式的值为2,并把2赋值给变a;如果判定t>1为假,即为0时,条件表达式的值为3,并把3赋值给变量a。
switch语句
switch 语句是C语言提供的多分支选择语句,用来实现多分支选择结构。
基本格式:
switch(表达式)
{
case 常量表达式: 语句1
case 常量表达式: 语句1
...
case 常量表达式: 语句1
default: 语句n+1
}
说明:
-
switch 后面用花括号括起来的部分是 switch 语句体
-
switch 后面括号内的 ”表达式”,可以是C语言中任意合法表达式,但表达式两侧的括号不能省略。
-
case 与其后面的常量表达式合称 case语句标号,常量表达式的类型必须与 switch 后面的表达式的类型相匹配,且各 case语句标号的值各不相同,
不能重复
。 -
default 也是关键字,起标号的作用,代表除了以上所有 case 标号之外的那些标号,default标号可以出现在语句体中任何标号位置上,当然也可以没有。
-
case 语句标号后的语句1、语句2等,可以是一条语句,也可以是若干条;在必要时,case语句标号后的语句可以省略不写。
switch语句的执行过程:
-
首先计算紧跟在 switch 后面的一对圆括号中的表达式的值,当表达式的值与某一个 case 后面的常量表达式的值相等时,就
执行此 case 后面的语句体并将流程转移到下一个 case 继续执行,直至switch 语句的结束
。 -
若所有的 case中的常量表达式的值都没有与表达式值匹配,又存在default,则执行default后面的语句,直至switch 语句结束。
-
如果不存在 default ,则跳过 switch 语句体,什么也不做。
switch语句非常有用,但在使用时必须谨慎。
只能针对基本数据类型中的整型类型
使用switch,这些类型包括int、char等。对于其他类型,则必须使用if语句。
switch()的参数类型不能为实型
switch语句和 break 语句组成的选择结构
在 switch语句中 case 之后的语句加上 break语句,程序执行到 break 时会立即跳出 switch 语句体
。其中,break是C语言的关键词。switch语和break语句的联合使用,使得switch语句真正起到分支的作用。下面用一段程序说明:
#include<stdio.h>
int main() {
int x=1,a=0,b=0;
switch(x) {
case 0: a++;
case 1:b++;
case 2:a++;b++;break; // 试试加不加break的情况下,结果是否一致
case 3: b++;
default:x=99;
}
printf("a=%d,b=%d,x=%d",a,b,x);
}
执行上述程序后,输出结果为:a=1,b=2。
程序分析:switch后的表达式x的值为1。然后寻找与1相同的case1分支,执行其后的语句,此时a=0,b=1。接着开始执行case2后的语句,执行a++;b++后,a=1,b=2。执行到break语句,跳出switch 语句体,开始执行 printf函数。
循环结构
while 和 do…while 循环结构
-
while 语句
一般形式:while(表达式) 循环体
while 是C语言中的关键字,表达式是循环条件,由它来控制循环体是否执行。
执行过程(先对表达式进行条件判断,后执行语句):-
计算while 后括号中表达式的值。当表达式的值非0时,执行while语句中的循环体内嵌语句;当表达式值为0时,跳过该while语句,执行该while 结构后的其他语句。
-
执行循环体内嵌语句。
-
返回去继续执行while语句,直到条件不满足,退出循环。
-
-
do…while 语句
基本格式:do { 循环体语句 } while(表达式);
-
do 是C语言的关键字,必须和 while 联合使用,不能单独出现。
-
do…while 循环由 do 开始,用 while 结束。
-
while 后面的圆括号中的表达式,可以是C语言中任意合法的表达式,由它控制循环是否执行,且圆括号不可丢。
-
在语法上,在 do 和 while 之间只能是一条语句,如需要执行多条语句时,可使用复合语句。
-
while(表达式)后的分号不可丢。
执行过程(
先执行循环体一次,然后判断循环条件是否成立
):-
先执行一次指定的循环体语句。
-
执行完后,判别 while 后面的表达式的值,当表达式的值为非零(真)时,程序流程返回去重新执行循环体语句。
-
如此反复,直到表达式的值等于零为止,此时循环结束,
-
for 循环结构
基本格式:for(表达式1; 表达式2; 表达式3) 循环体
比如:for(i=0; i<10; i+ +) { }
圆括号中通常是3个表达式,用于for循环的控制。for 语句中的表达式可以部分或者全部省略,但两个分号是不可省略的。
各个表达式之间用分号隔开,且圆括号不可省略。
按照语法规则,循环体只能是一条语句,如需要完成多项操作,须使用复合语句。
执行过程:
-
求表达式1的值。
-
求表达式2的值,若其值为真(非0),则执行for语句中指定的循环内嵌语句,后执行求表达式3的值,若其值为假(0),则退出循环,执行 for 语句以下的其他语句。
-
重复步骤2。
循环结构的嵌套
循环嵌套的定义:在一个循环体内又完整地包含另一个循环结构。内循环还可以嵌套循环,形成多层循环结构。外循环成立的条件下,才可以执行内循环。
while 循环、do…while 循环和for循环都可以嵌套,但每一层循环在逻辑上必须完整。
continue和break语句
-
continue 语句
作用:结束本次循环,即跳过本次循环体中continue语句后面的语句,立刻进行下一次的循环条件判断,然后决定循环是否继续进行。例如:#include<stdio.h> int main() { int i, sum = 0; for (i = 1; i <= 5; i++) { if (i == 3) continue; sum = sum + i; } printf("i=%d,sum=%d\n", i, sum); return 0; }
程序输出结果:i=6,sum=12。
程序分析:当程序进行到第三次循环,即当i=3时,由 if语句和 continue 语句可知本次循环结束,i的值不加到 sum 中,然后接着开始第四次循环,即从i=4 开始,直到循环结束。 -
break 语句
作用:可以使程序流程跳出 switch 语句体,也可用 break语句在循环结构中终止本层循环体,从而提前结束本层循环。
当 break 出现在循环体中的 switch 语句体内时,其作用只是跳出该 switch 语句体,并不能中止循环体的执行。若想强行中止循环体的执行,可以在循环体中,但并不在switch语句中设break语句,满足某种条件则跳出本层循环体。例如:#include<stdio.h> int main() { int i, sum = 0; for (i = 1; i <= 5; i++) { if (i == 3) break; // 如果i的值为3,跳出for循环,然后执行printf函数 sum = sum + i; // 把i的值加到sum中 } printf("i=%d,sum=%d\n", i, sum); return 0; }
程序输出结果:i=3,sum=3。
程序分析:当程序进行到第三次循环,即当i=3时,由 i 语句和 break 语句可知此时应跳出依次存 for 循环,执行 for 循环后的语句,即执行 printf 函数输出语句。
预处理指令
C语言的预处理是编译过程的一个早期阶段,它发生在实际的编译之前。预处理阶段主要处理源代码中的预处理指令,这些指令以#
开头。预处理指令告诉预处理器在实际编译之前如何修改源代码。
C语言提供了三种预处理功能,这三种预处理包括宏定义
、文件包含
、条件编译
。
指令 | 描述 |
---|---|
#define | 定义宏 |
#include | 包含一个源代码文件 |
#undef | 取消已定义的宏 |
#ifdef | 如果宏已经定义,则返回真 |
#ifndef | 如果宏没有定义,则返回真 |
#if | 如果给定条件为真,则编译下面代码 |
#else | #if 的替代方案 |
#elif | 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码 |
#endif | 结束一个 #if……#else 条件编译块 |
#error | 当遇到标准错误时,输出错误消息 |
#pragma | 使用标准化方法,向编译器发布特殊的命令到编译器中 |
宏定义
宏定义又称宏代换、宏替换,简称宏。
它指的是用一个宏名(名字)来代表一个字符串,或者根据一系列预定义的规则替换一定的文本模式。
宏定义最常见的用法是定义代表某个值的全局符号,例如,#define PI 3.14159
。另一种用法是定义带参数的宏,这样的宏可以像函数一样被调用,并在调用语句处展开宏,用调用时的实际参数来代替定义中的形式参数。
不带参数的宏定义
定义形式:#define 宏名 替换文本
或 #define 宏名
说明:
-
再define宏名和宏替换文本之间用空格隔开。
-
同一个宏名不能重复定义。
-
可以用#undef命令终止宏定义作用域。
-
在进行宏定义时,可以引用已定义的宏名。
-
宏可以在源代码中多次使用,并在预处理阶段被其定义的值替换。
例如:
#define PI 3.14159 // 定义一个宏
#undef PI // 取消一个先前定义的宏。
带参数的宏定义
-
带参数的宏定义
定义形式:#define 宏名(参数表) 字符串
宏定义不止进行简单的字符串替换,话可以进行参数替换。 -
带参数的宏调用
一般形式:宏名(实参列表)
例如:#define total(x) x*x+x+1 // 宏定义 a=total(2); // 宏调用
解析:在宏调用过程,用实参2取代替形参x,经预处理宏展开后的语句为 a=2+2+2+1。
在带参宏定义中,不会为形式参数分配内存,因此不必指明数据类型。而在宏调用中,实参包含了具体的数据,要用它们去代换形参,因此必须指明数据类型。同时要特别注意实参是表达式时的调用。
比如:#define total(x) xx // 宏定义
a=total(x+y); // 宏调用
解析:经预处理宏展开后的语句为x+yx+y。
比如:#define total(x) (x)(x) // 宏定义
a = total(x +y); // 宏调用
解析:经预处理宏展开后的语句为(x+y)(x+y)。
条件编译
条件编译指令允许程序员根据特定的条件包含或排除代码块。这些条件可以是编译时定义的宏、编译器的特定标志,或者其他任何可以在预处理阶段确定的条件( 条件编译指令,用于根据条件包含或排除代码块。
)。
以下是常用的条件编译指令:
-
#ifdef 和 #ifndef:
这两个指令用于检查是否定义了一个特定的宏。如果定义了宏(对于#ifdef
),或者没有定义宏(对于#ifndef
),则编译器会包含随后的代码块,直到遇到#endif
为止。#ifdef DEBUG // 这部分代码仅在定义了DEBUG宏时编译 #endif #ifndef NDEBUG // 这部分代码在NDEBUG宏`未定义`时编译 #endif
-
#if、#elif、#else 和 #endif:
这些指令提供了更复杂的条件编译功能,类似于C语言中的if-else
语句。#if
后面可以跟随一个常量表达式,如果表达式的值为非零,则编译器会包含随后的代码块
。#elif
(相当于else if
)和#else
提供了额外的条件分支。#if defined(OS_WINDOWS) // Windows平台特有的代码 #elif defined(OS_LINUX) // Linux平台特有的代码 #else // 其他平台共有的代码 #endif
-
#defined:
#if
和#ifdef
指令后面都可以使用defined
操作符来检查宏是否定义。defined
操作符返回一个整数值,如果宏定义了,则为非零值;否则为零。#if defined(FEATURE_A) && !defined(FEATURE_B) // 当FEATURE_A定义了,而FEATURE_B未定义时编译 #endif
-
#undef:
这个指令用于取消之前定义的宏。这可以用来在特定条件下移除之前定义的宏。#define MY_MACRO 1 // ... 一些代码 ... #undef MY_MACRO // 取消MY_MACRO的定义
-
#error:
当条件编译的某个条件不满足时,可以使用#error
指令生成一个编译时错误。这有助于在编译阶段捕获配置错误或不支持的平台。#if !defined(OS_SUPPORTED) #error "Unsupported operating system!" #endif
-
#line:
这个指令可以改变预处理器生成的行号和文件名,通常用于复杂的宏定义或代码生成器。#line 100 "newfile.c"
-
#pragma:
这是一个编译器特定的预处理指令,用于向编译器传递额外的信息或指令。不同的编译器可能支持不同的#pragma
指令。#pragma once // 防止头文件被多次包含,某些编译器支持这个指令
文件包含
C头文件
头文件是扩展名为 .h 的文件,包含了 C 函数声明和宏定义,被多个源文件中引用共享。有两种类型的头文件:程序员编写的头文件和编译器自带的头文件。
在程序中要使用头文件,需要使用 C 预处理指令 #include 来引用它。前面我们已经看过 stdio.h 头文件,它是编译器自带的头文件。
引用头文件相当于复制头文件的内容,但是我们不会直接在源文件中复制头文件的内容,因为这么做很容易出错,特别在程序是由多个源文件组成的时候。
建议把所有的常量、宏、系统全局变量和函数原型写在头文件中,在需要的时候随时引用这些头文件。
引用头文件的语法
使用预处理指令 #include 可以引用用户和系统头文件。它的形式有以下两种:
#include <file.h>
这种形式用于引用系统头文件。它在系统目录的标准列表中搜索名为 file 的文件。
#include "file.h"
#include "file.c"
这种形式用于引用用户头文件。它在包含当前文件的目录中搜索名为 file 的文件。
引用头文件的操作
#include 指令会指示 C 预处理器浏览指定的文件作为输入。预处理器的输出包含了已经生成的输出,被引用文件生成的输出以及 #include 指令之后的文本输出。例如,如果您有一个头文件 header.h,如下:
char *test (void);
和一个使用了头文件的主程序 program.c,如下:
int x;
#include "header.h"
int main (void)
{
puts (test ());
}
编译器会看到如下的代码信息:
int x;
char *test (void);
int main (void)
{
puts (test ());
}
只引用一次头文件
如果一个头文件被引用两次,编译器会处理两次头文件的内容,这将产生错误。为了防止这种情况,标准的做法是把文件的整个内容放在条件编译语句中,如下:
#ifndef HEADER_FILE
#define HEADER_FILE
the entire header file file
#endif
这种结构就是通常所说的包装器 #ifndef。当再次引用头文件时,条件为假,因为 HEADER_FILE 已定义。此时,预处理器会跳过文件的整个内容,编译器会忽略它。
有条件引用
有时需要从多个不同的头文件中选择一个引用到程序中。例如,需要指定在不同的操作系统上使用的配置参数。您可以通过一系列条件来实现这点,如下:
#if SYSTEM_1
# include "system_1.h"
#elif SYSTEM_2
# include "system_2.h"
#elif SYSTEM_3
...
#endif
但是如果头文件比较多的时候,这么做是很不妥当的,预处理器使用宏来定义头文件的名称。这就是所谓的有条件引用。它不是用头文件的名称作为 #include 的直接参数,您只需要使用宏名称代替即可:
#define SYSTEM_H "system_1.h"
...
#include SYSTEM_H
SYSTEM_H 会扩展,预处理器会查找 system_1.h,就像 #include 最初编写的那样。SYSTEM_H 可通过 -D 选项被您的 Makefile 定义。