1. 什么是语句?
我们把向计算机发出的一条条指令和数据称为语句。每一条语句末尾用
;
结束。
C语言程序语句示例:
int main()
{
int age; //定义变量语句
age = 0; //赋值语句
scanf("%d", &age);
if (age < 18)
{
printf("未成年\n"); //调用printf函数语句
}
return 0; //函数返回语句
}
简单的语句就是要求计算机做出的一些动作,可以是从键盘读入一个数或者在屏幕上显示数据,也可以是定义一个变量或者给某个变量赋值等。
当把多条语句组合在一起,用来完成某一项工作时,这些语句被称为块语句。块语句要用花括号{
和}
包围起来:
C语句可分为以下五类:
- 表达式语句
- 函数调用语句
- 控制语句
- 复合语句
- 空语句
控制语句用于控制程序的执行流程,以实现程序的各种结构方式,它们由特定的语句定义符组成,C语
言有九种控制语句,可分成以下三类:
- 条件判断语句也叫分支语句:if语句、switch语句;
- 循环执行语句:do while语句、while语句、for语句;
- 转向语句:break语句、goto语句、continue语句、return语句。
2. 分支语句(选择结构)
选择结构的程序设计是一种根据判断条件的成立与否来确定下一步所做操作的一种程序控制结构。其程序的执行流程不再像顺序结构那样,从上到下一条条依次执行所有语句,而是根据判断条件的成立与否而走向不同的分支,因而,选择结构也被称为分支结构。C语言对某一条件成立与否的判断处理是用关系运算和逻辑运算来解决的。
2.1 if语句
if语句的用法:
if(condition)
{
/* 如果condition为真将执行的语句 */
}
代码1:
int main()
{
int age = 0;
scanf("%d", &age);
if (age < 18)
{
printf("未成年\n");
}
return 0;
}
if…else 语句的用法:
if(condition)
{
/* 如果condition为真将执行的语句 */
}
else
{
/* 如果condition为假将执行的语句 */
}
代码2:
int main()
{
int age = 0;
scanf("%d", &age);
if (age < 18)
{
printf("未成年\n");
}
else
{
printf("成年\n");
}
}
多分支:
if(condition_1)
{
/* 如果condition_1为真将执行的语句 */
}
else if(condition_2)
{
/* 如果condition_2为真将执行的语句 */
}
else
{
/* 如果condition_1和condition_2为假将执行的语句 */
}
代码3:
int main()
{
int age = 0;
scanf("%d", &age);
if (age < 18)
{
printf("少年\n");
}
else if (age >= 18 && age < 30)
{
printf("青年\n");
}
else if (age >= 30 && age < 50)
{
printf("中年\n");
}
else if (age >= 50 && age < 80)
{
printf("老年\n");
}
else
{
printf("老寿星\n");
}
}
注意:
在C语言:0表示假,非0表示真。
2.1.1 悬空else
注意这个代码:
int main()
{
int a = 0;
int b = 2;
if(a == 1)
if(b == 2)
printf("hehe\n");
else
printf("haha\n");
return 0;
}
输出结果为空,并非haha。
改正:
int main()
{
int a = 0;
int b = 2;
if (a == 1)
{
if (b == 2)
{
printf("hehe\n");
}
}
else
{
printf("haha\n");
}
return 0;
}
原因:
else是和它离的最近的且没有与其他else匹配的if相匹配的。
注意:
适当增加{},提高代码可读性。
2.1.2 if书写形式的对比
代码1:
if (condition)
{
return x;
}
return y;
代码1的意思是:condition为真则返回x,否则返回y。但是,在阅读上并不直观,可改为:
代码2:
if (condition)
{
return x;
}
else
{
return y;
}
代码3:
int num = 1;
if (num == 5)
{
printf("hehe\n");
}
代码2并没有错,但是在写num == 5时很容易漏写一个等号,写成num = 5:
此时程序依然可以执行,不会报错,因为num被赋值为5,判断条件为真,可以执行,但实际上已经错了,所以代码2可以写成:
代码4:
int num = 1;
if (5 == num)
{
printf("hehe\n");
}
因为当漏写等号时,即5 = num,程序会报错:
总结:
代码2和代码4更好,逻辑更加清晰,不容易出错。
2.1.3 练习
- 判断一个数是否为奇数
int main()
{
int n = 0;
scanf("%d", &n);
if (n % 2 == 1)
{
printf("%d是奇数\n", n);
}
else
{
printf("%d不是奇数\n", n);
}
return 0;
}
- 输出1-100之间的奇数
int main()
{
for (int i = 0; i < 100; i++)
{
if (i % 2 == 1)
{
printf("%d是奇数\n", i);
}
}
return 0;
}
2.2 switch语句
一个 switch 语句允许测试一个变量等于多个值时的情况。每个值称为一个 case,且被测试的变量会对每个 switch case 进行检查。
C 语言中 switch 语句的语法:
switch(expression){
case constant-expression :
statement(s);
break; /* 可选的 */
case constant-expression :
statement(s);
break; /* 可选的 */
/* 您可以有任意数量的 case 语句 */
default : /* 可选的 */
statement(s);
}
switch 语句必须遵循下面的规则:
- switch 语句中的 expression 是一个**整型表达式**。
- 在一个 switch 中可以有任意数量的 case 语句。每个 case 后跟一个要比较的值和一个冒号。
- case 的 constant-expression 必须为**整形常量表达式**。
- 当被测试的变量等于 case 中的常量时,case 后跟的语句将被执行,直到遇到 break 语句为止。
- 当遇到 break 语句时,switch 终止,控制流将跳转到 switch 语句后的下一行。
- 不是每一个 case 都需要包含 break。如果 case 语句不包含 break,控制流将会继续执行后续的 case,直到遇到 break 为止。
- 一个 switch 语句可以有一个可选的 default case,出现在 switch 的结尾。default case 可用于在上面所有 case 都不为真时执行一个任务。default case 中的 break 语句不是必需的。
2.2.1 在switch语句中的 break
在switch语句中,我们没办法直接实现分支,搭配break使用才能实现真正的分支。
比如:
输入1,输出星期一
输入2,输出星期二
输入3,输出星期三
输入4,输出星期四
输入5,输出星期五
输入6,输出星期六
输入7,输出星期日
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)
{
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期天\n");
break;
}
return 0;
}
有时候我们的需求变了:
- 输入1-5,输出的是“weekday”;
- 输入6-7,输出“weekend”
所以我们的代码就应该这样实现了:
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("weekday\n");
break;
case 6:
case 7:
printf("weekend\n");
break;
}
return 0;
}
break语句的实际效果是把语句列表划分为不同的分支部分。
编程好习惯:
在最后一个 case 语句的后面加上一条 break语句。
(之所以这么写是可以避免出现在以前的最后一个 case 语句后面忘了添加 break语句)。
2.2.2 default子句
如果表达的值与所有的case标签的值都不匹配怎么办?
其实也没什么,结构就是所有的语句都被跳过而已。
程序并不会终止,也不会报错,因为这种情况在C中并不认为是个错误。但是,如果你并不想忽略不匹配所有标签的表达式的值时该怎么办呢?
你可以在语句列表中增加一条default子句,把下面的标签default:
写在任何一个 case
标签可以出现的位置。当 switch 表达式的值并不匹配所有 case 标签的值时,这个 default 子句后面的语句就会执行。
所以,每个switch语句中只能出现一条default子句。但是它可以出现在语句列表的任何位置,而且语句流会像执行一个case标签一样执行default子句。
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("weekday\n");
break;
case 6:
case 7:
printf("weekend\n");
break;
default:
printf("输入错误\n");
break;
}
return 0;
}
编程好习惯:
在每个 switch 语句中都放一条default子句是个好习惯,甚至可以在后边再加一个 break 。
2.2.3 练习
int main()
{
int n = 1;
int m = 2;
switch (n)
{
case 1:
m++; //m=3
case 2:
n++; //n=2
case 3:
switch (n)
{//switch允许嵌套使用
case 1: //跳过
n++;
case 2:
m++; //m=4
n++; //n=3
break;
}
case 4:
m++; //m=5
break;
default:
break;
}
printf("m = %d, n = %d\n", m, n); //m = 5, n = 3
return 0;
}
3. 循环语句
有的时候,我们可能需要多次执行同一块代码。一般情况下,语句是按顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。
编程语言提供了更为复杂执行路径的多种控制结构。
循环语句允许我们多次执行一个语句或语句组,下面是大多数编程语言中循环语句的流程图:
C 语言提供了以下几种循环类型:
- while
- for
- do while
3.1 while循环
只要给定的条件为真,C 语言中的 while 循环语句会重复执行一个目标语句。
C 语言中 while 循环的语法:
while(condition)
{
statement(s);
}
statement(s) 可以是一个单独的语句,也可以是几个语句组成的代码块。
condition 可以是任意的表达式,当为任意非零值时都为 true。当条件为 true 时执行循环。 当条件为 false 时,退出循环,程序流将继续执行紧接着循环的下一条语句。
while 循环的关键点是循环可能一次都不会执行。当条件为 false 时,会跳过循环主体,直接执行紧接着 while 循环的下一条语句。
例如:
在屏幕上打印1-10的数字。
int main()
{
int i = 1;
while (i <= 10)
{
printf("%d ", i);
i++;
}
return 0;
}
3.1.1 while语句中的break和continue
break
int main()
{
int i = 1;
while (i <= 10)
{
if (5 == i)
break;
printf("%d ", i);
i++;
}
return 0;
}
总结:
break在while循环中的作用:
在循环中只要遇到break,就停止后期的所有的循环,直接终止循环。
所以:while中的break是用于永久终止循环的。
continue
int main()
{
int i = 1;
while (i <= 10)
{
if (5 == i)
continue;
printf("%d ", i);
i++;
}
return 0;
}
这里当i=5时,会跳出循环到条件判断中,但i仍然是5,所以形成了死循环。
改正:
int main()
{
int i = 1;
while (i <= 10)
{
i++;
if (i == 5)
continue;
printf("%d ", i);
}
return 0;
}
总结:
continue在while循环中的作用就是:
continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,而是直接跳转到while语句的判断部分。进行下一次循环的入口判断。
再看几个代码:
int main()
{
int ch = 0;
//getchar读取输入缓冲区里的数据。
while ((ch = getchar()) != EOF)
putchar(ch);
return 0;
}
练习1:
int main()
{
char passwd[10] = { 0 };
printf("请输入密码:>");
scanf("%s", &passwd);
getchar(); //读取回车\n
printf("请确认密码(Y/N):>");
int ret = getchar();
if ('Y' == ret)
{
printf("Yes\n");
}
else
{
printf("No\n");
}
return 0;
}
练习2:
//这个代码的作用是:只打印数字字符,跳过其他字符的
int main()
{
char ch = '\0';
while ((ch = getchar()) != EOF)
{
if (ch < '0' || ch > '9')
continue;
putchar(ch);
printf("\n");
}
return 0;
}
3.2 for循环
3.2.1 语法
for 循环语句的一般形式为:
for (表达式1; 表达式2; 表达式3)
{
语句;
}
表达式1为初始化部分,用于初始化循环变量的。
表达式2为条件判断部分,用于判断循环时候终止。
表达式3为调整部分,用于循环条件的调整。
实例:
使用for循环 在屏幕上打印1-10的数字。
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
printf("%d ", i);
}
return 0;
}
现在我们对比一下for循环和while循环:
//实现相同的功能,使用while
int main()
{
int i = 0;
i = 1;//初始化部分
while (i <= 10)//判断部分
{
printf("hehe\n");
i = i + 1;//调整部分
}
return 0;
}
//实现相同的功能,使用for
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
printf("hehe\n");
}
return 0;
}
可以发现在while循环中依然存在循环的三个必须条件,但是由于风格的问题使得三个部分很可能偏离较
远,这样查找修改就不够集中和方便。所以,for循环的风格更胜一筹;for循环使用的频率也最高。
3.2.2 break和continue在for循环中
我们发现在for循环中也可以出现break和continue,他们的意义和在while循环中是一样的。
但是还是有些差异:
//代码1
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (i == 5)
break;
printf("%d ", i); //1 2 3 4
}
return 0;
}
//代码2
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (i == 5)
continue;
printf("%d ", i); //1 2 3 4 6 7 8 9 10
}
return 0;
}
3.2.3 for语句的循环控制变量
建议:
- 不可在for 循环体内修改循环变量,防止 for 循环失去控制。
- 建议for语句的循环控制变量的取值采用“前闭后开区间”写法。
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
//前闭后开的写法
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
//两边都是闭区间
for (i = 0; i <= 9; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
3.2.4 一些for循环的变种
int main()
{
//代码1
for (;;)
{
printf("hehe\n");
}
//for循环中的初始化部分,判断部分,调整部分是可以省略的,但是不建议初学时省略,容易导致问题。
//代码2
int i = 0;
int j = 0;
//这里打印多少个hehe?
int count = 0;
for (i = 0; i < 10; i++)
{
for (j = 0; j < 10; j++)
{
printf("hehe\n");
count++;
}
}
printf("%d", count); //100
//代码3
int i = 0;
int j = 0;
//如果省略掉初始化部分,这里打印多少个hehe?
int count = 0;
for (; i < 10; i++)
{
for (; j < 10; j++)
{
printf("hehe\n");
count++;
}
}
printf("%d", count); //10
//代码4-使用多余一个变量控制循环
int x, y;
for (x = 0, y = 0; x < 2 && y < 5; ++x, y++)
{
printf("hehe\n");
}
return 0;
}
3.2.5 一道笔试题:
//请问循环要循环多少次?
int main()
{
int i = 0;
int k = 0;
for (i = 0, k = 0; k = 0; i++, k++)
k++;
return 0;
}
循环0次。
因为判断语句k被赋值为0,为假,不执行语句。
3.3 do…while()循环
3.3.1 语法:
do
{
循环语句;
} while(表达式);
3.3.2 do语句的特点
循环至少执行一次,使用的场景有限,所以不是经常使用。
int main()
{
int i = 10;
do
{
printf("%d\n", i);
} while (i < 10);
return 0;
}
3.3.3 do while循环中的break和continue
int main()
{
int i = 1;
do
{
if (5 == i)
break;
printf("%d ", i);
i++;
} while (i <= 10);
return 0;
}
int main()
{
int i = 1;
do
{
i++;
if (5 == i)
break;
printf("%d ", i); //2 3 4
} while (i <= 10);
return 0;
}
int main()
{
int i = 1;
do
{
if (5 == i)
continue;
printf("%d ", i); //1 2 3 4 死循环
i++;
} while (i <= 10);
return 0;
}
int main()
{
int i = 1;
do
{
i++;
if (5 == i)
continue;
printf("%d ", i); //2 3 4 6 7 8 9 10 11
} while (i <= 10);
return 0;
}
3.4 练习
- 计算 n的阶乘。
int main()
{
int n = 0;
int ret = 1;
int i = 1;
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
ret *= i;
}
printf("%d的阶乘为%d\n", n, ret);
return 0;
}
- 计算 1!+2!+3!+……+10!
int main()
{
int n = 0;
int ret = 1;
int i = 1;
int sum = 0;
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
ret *= i;
sum += ret;
}
printf("%d的阶乘为%d\n", n, ret);
printf("阶乘和为%d\n", sum);
return 0;
}
- 在一个有序数组中查找具体的某个数字n。(讲解二分查找)
//一般方法
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 0; //要查找的数
scanf("%d", &k);
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < sz; i++)
{
if (arr[i] == k)
{
printf("%d找到了,下标是:%d\n", k, i);
break;
}
}
if (i == sz)
{
printf("%d没找到\n", k);
}
return 0;
}
//二分查找
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 0; //要查找的数
scanf("%d", &k);
int sz = sizeof(arr) / sizeof(arr[0]);
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (arr[mid] < k)
{
left = mid + 1;
}
else if (arr[mid] > k)
{
right = mid - 1;
}
else
{
printf("%d找到了,下标是:%d\n", k, mid);
break;
}
}
if (left > right)
printf("没找到\n");
return 0;
}
- 编写代码,演示多个字符从两端移动,向中间汇聚。
#include <Windows.h>
int main()
{
char arr1[] = "hello world";
char arr2[] = " ";
int left = 0;
int right = strlen(arr2) - 1;
while (left <= right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
left++;
right--;
Sleep(1000);
system("cls"); //清空屏幕
}
printf("%s\n", arr2);
return 0;
}
- 编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则提示登录成,如果三次均输入错误,则退出程序。
#include <string.h>
int main()
{
char password[20] = { 0 };
int i = 0;
for (i = 0; i < 3; i++)
{
printf("请输入密码:>");
scanf("%s", password); //数组名为数组地址
if (strcmp(password, "abcdef") == 0)
{
printf("登录成功\n");
break;
}
else
{
printf("密码错误\n");
}
}
if (i == 3)
{
printf("三次密码均输入错误,退出程序\n");
}
return 0;
}
- 猜数字游戏
#include <stdlib.h>
#include <time.h>
void menu()
{
printf("**********************************\n");
printf("*********** 1.play ************\n");
printf("*********** 0.exit ************\n");
printf("**********************************\n");
}
//(0~RAND_MAX) RAND_MAX--rand函数能返回随机数的最大值 0x7fff -- 32767
void game()
{
int guess = 0;
//1.生成随机数
int ret = rand() % 100 + 1;
//printf("%d\n", ret);
//2.猜数字
while (1)
{
printf("请输入要猜的数字:>");
scanf("%d", &guess);
if (guess < ret)
{
printf("猜小了\n");
}
else if (guess > ret)
{
printf("猜大了\n");
}
else
{
printf("恭喜你,猜对了\n");
break;
}
}
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
return 0;
}
4. goto语句
C语言中提供了可以随意滥用的 goto语句和标记跳转的标号。
从理论上 goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码。
但是某些场合下goto语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过
程。
例如:一次跳出两层或多层循环。
多层循环这种情况使用break是达不到目的的。它只能从最内层循环退出到上一层的循环。
goto语言真正适合的场景如下:
for (...)
for (...)
{
for (...)
{
if (disaster)
goto error;
}
}
…
error :
if (disaster)
// 处理错误情况
下面是使用goto语句的一个例子,然后使用循环的实现方式替换goto语句:
一个关机程序:
shutdown -a 取消关机
shutdown -s 关机
shutdown -f 强行关闭应用程序
shutdown -m \计算机名 控制远程计算机
shutdown -i 显示“远程关机”图形用户界面,但必须是Shutdown的第一个参数
shutdown -l 注销当前用户
shutdown -r 关机并重启
shutdown -s -t 时间 设置关机倒计时
shutdown -r -t 时间 设置重新启动倒计时
shutdown -h 休眠
#include <Windows.h>
#include <string.h>
int main()
{
char input[10] = { 0 };
system("shutdown -s -t 60");
again:
printf("电脑将在1分钟内关机,如果输入:我是猪,就取消关机!");
scanf("%s", input);
if (0 == strcmp(input, "我是猪"))
{
system("shutdown -a");
}
else
{
goto again;
}
return 0;
}
而如果不适用goto语句,则可以使用循环:
int main()
{
char input[10] = { 0 };
system("shutdown -s -t 60");
while (1)
{
printf("电脑将在1分钟内关机,如果输入:我是猪,就取消关机!\n请输入:>");
scanf("%s", input);
if (0 == strcmp(input, "我是猪"))
{
system("shutdown -a");
break;
}
}
return 0;
}