目录
第一节:变量进阶
在第二章中,我们已经学习了变量的定义与使用,接下来我们将学习变量的更多特性。
1-1.局部变量与全局变量
1-1-1.局部变量
我们之前在代码中定义的变量全部都是 局部变量,它的特征是被{}包括在内,可以说,被{}包括在内的变量都是局部变量:
int main()
{ // 大括号
int a = 0; // 我是一个局部变量
char b = 'A'; // 我也是一个局部变量
return 0;
} // 反大括号
经过第二章的学习,我们可以注意到无论分支语句还是循环语句,它们都各自带有自己的{}。对于{}嵌套的情况,即使在嵌套中的{}定义的变量,也是局部变量,无论{}嵌套了多少层:
int main()
{ // 大括号
int a = 0; // 我是一个局部变量
char b = 'A'; // 我也是一个局部变量
if(1)
{
float f = 1.000001; // 我也是一个局部变量
}
while(1)
{
long c = 10; // 我还是一个局部变量
}
return 0;
} // 反大括号
1-1-2.全局变量
与局部变量相反,只要不被{}包括的变量就是 全局变量,可以说,一个变量不是局部变量就是全局变量:
int e = 100; // 我就是全局变量
int main()
{ // 大括号
int a = 0; // 我是一个局部变量
char b = 'A'; // 我也是一个局部变量
if(1)
{
float f = 1.000001; // 我也是一个局部变量
}
while(1)
{
long c = 10; // 我还是一个局部变量
}
return 0;
} // 反大括号
全局变量和局部变量在使用范围上有区别,这种区别就是变量的另一种特性:生命周期
1-2.变量生命周期
变量的生命周期就是它的“寿命”,一个变量的生命周期从它被定义开始,到它所在的这个{}结束(或者程序结束时结束)。在这个变量的生命周期之外,我们不能使用它。
1-2-1.局部变量的生命周期
请阅读以下代码:
int main()
{ // 大括号
int a = 0; // 我是一个局部变量a,我的生命周期从这一行开始
if(1)
{
float f = 1.000001;
}
while(1)
{
long c = 10;
}
return 0; // 程序结束,所有“存活”变量的生命周期都在这里结束
} // 反大括号,局部变量a的生命周期在这里结束
局部变量a的生命周期如上图所示,在它的生命周期中,可以对它进行使用。
下图帮助理解生命周期的长度:
ps:上面图片的 main 函数中并没有 return(忘记写了,正常应该补上),所以a的生命周期才在反大括号处结束
如何证明生命周期确实是这样呢?我们先看看局部变量a:
#include <stdio.h>
int main()
{ // 大括号
int a = 0; // 我是一个局部变量a,我的生命周期从这一行开始
printf("我是一个标记,标记号:1,现在我使用变量a:%d",a);
return 0; // 程序结束,局部变量a的生命周期在这里结束
}
我们在a的生命周期中使用a,程序可以正常执行。
看看生命周期之外使用a的情况:
#include <stdio.h>
int main()
{ // 大括号
printf("我是一个标记,标记号:0,现在我使用变量a:%d", a);
int a = 0; // 我是一个局部变量a,我的生命周期从这一行开始
return 0;
} // 程序结束,局部变量a的生命周期在这里结束
结果报错了,a是没有声明的标识符,意思是a没有定义,它还没有“出生”。
再来看看嵌套{}中的局部变量f:
#include <stdio.h>
int main()
{
if (1)
{
float f = 1.000001; // 局部变量f的生命周期在这里开始
printf("我是一个标记,标记号:1,现在我使用变量f:%f", f);
} // 反大括号,局部变量f的生命周期在这里结束
return 0;
}
我们在f的生命周期中使用f,程序可以正常执行。
看看生命周期之外使用f的情况:
#include <stdio.h>
int main()
{
if (1)
{
float f = 1.000001; // 局部变量f的生命周期在这里开始;
} // 反大括号,局部变量f的生命周期在这里结束
printf("我是一个标记,标记号:0,现在我使用变量f:%f", f);
return 0;
}
虽然我们在f定义之后的再使用f,但是这时候它已经“死了”,在生命周期之外所以也属于未定义的符号。
1-2-2.全局变量的生命周期
全局变量并没有被{}括起来,它的生命周期从定义开始,直到程序结束:
我们可以验证一下,请阅读以下代码和执行结果:
#include <stdio.h>
int e = 100; // 我是一个全局变量e,我的生命周期从这一行开始
int main()
{
printf("我是一个标记,标记号:0,现在我使用变量e:%d", e);
if(1)
{
printf("我是一个标记,标记号:1,现在我使用变量e:%d", e);
}
printf("我是一个标记,标记号:2,现在我使用变量e:%d", e);
return 0; // 程序结束,所有“存活”变量的生命周期都在这里结束,包括全局变量
}
结果表明,无论是在{}还是嵌套{}中,全局变量都可以使用,即它的生命周期覆盖整个程序。
第二节:函数
2-1.函数调用
我们之前已经使用过 printf 函数,函数的使用就叫调用,它的调用格式如下:
[函数名] ([参数]);
例如使用 printf 函数
printf("我是参数");
2-2.库函数
库函数是C语言库中自带的函数比如之前的 printf 函数和 scanf 函数,我们要使用这些函数需要引头文件:
#include <stdio.h> // stdio.h 就是头文件名,#include <头文件名>引进这个库,就可以使用里面的库函数
int main()
{
int a;
printf("使用我需要引用头文件stdio.h");
scanf("%d",&a); // 使用我也需要引用头文件stdio.h
}
ps:一个头文件可以包含多个库函数,一些库函数在不同头文件中
这类函数不需要我们自己写就可以用,因为它们是其他人写好了保存在头文件中,可以被我们之间使用。
除了可以直接使用的库函数,我们也可以自己写函数。
2-3.自定义函数
我们自己写的函数叫 自定义函数,它们写在 main 函数之前,所有函数(包括库函数)都具有以下格式:
[返回类型] [函数名]([参数])
{
使用函数时执行的代码
}
返回类型:包括之前讲的基本类型:int、short、char等,它是函数调用 return 时返回的一个数据类型,然后可以被外部接收到,不需要返回数据就填 void。
函数名:函数的名称,自定义函数可以自己取名字,像变量一样。
参数:使用函数时需要的参数,可以没有参数,也可以有多个参数。
我们可以编写函数来实现一些固定功能,例如一个简单的加法函数:
int Add(int x,int y) // 函数的声明
{
int sum = x + y; // {}是函数的定义,里面可以不写任何东西
return sum;
}
ps:不论 return 之后有没有代码,只要执行了 return 后面的代码都不执行了。
以上叫做函数的 声明 与 定义。
先声明,后调用 是函数的六字真言。
现在我们使用它来完成一些简单的加法:
#include <stdio.h>
int Add(int x, int y)
{
int sum = x + y;
return sum;
}
int main()
{
int result_1 = Add(4, 5); // 可以传具体数字
printf("结果为: % d\n",result_1);
int a = 4;
int b = 3;
int result_2 = Add(a, b); //也可以传变量
printf("结果为: % d\n",result_2);
return 0;
}
上述代码的执行顺序是:
还有一些细节需要注意:
1、函数调用:调用时,传入的参数4和5会被函数里的参数x和y按顺序接收到,等价于int x = 4,int y = 5;下面传入a、b时也一样会被接收,即int x = a、int y = b
2、函数返回:根据之前讲的生命周期,在函数外部sum已经被销毁了,所以函数返回时是把sum变量的值返回了,例如第一次调用 Add 函数时,返回的是sum的值 9,然后被result_1接收到,即int result_1 = 9
2-4.加餐-函数解耦
现在我们可以自己写函数了,我们可以用函数把我们之前写的猜数字小游戏这种比较长的代码,按照功能用不同的函数执行。
先来看看源代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h> // 包含函数 srand 和 rand 的文件
#include <time.h> // 包含函数 time 的文件
int main()
{
while(1)
{
//游戏菜单
printf("************************************\n");
printf("*************1.开始游戏*************\n");
printf("*************0.退 出*************\n");
printf("************************************\n");
int select;
printf("请选择:");
scanf("%d", &select);
switch (select)
{
case 1: // 开始游戏
srand(time(NULL)); // 用时间戳设置随机数种子
int random = rand(); // 获取一个随机数
random = random % 101; // 让随机数的值在0--100之间
int count = 0; // 记录猜测次数
while (1)
{
int guess;
printf("请输入你猜测的数字:");
scanf("%d", &guess);
if (guess > random) // 大于
{
printf("猜大了\n");
count++;
}
else if (guess < random) // 小于
{
printf("猜小了\n");
count++;
}
else // 等于
{
count++;
printf("恭喜你,猜中了\n");
printf("共猜了 %d 次\n", count);
break;
}
}
break;
case 0: // 退出
return 0;
default:// select既不是1也不是0,即一个非法的输入
printf("非法的输入,请重新选择\n");
}
}
return 0;
}
把他分成不同的功能分区:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h> // 包含函数 srand 和 rand 的文件
#include <time.h> // 包含函数 time 的文件
int main()
{
while(1)
{
//游戏菜单---------------------------------------------
printf("************************************\n");
printf("*************1.开始游戏*************\n");
printf("*************0.退 出*************\n");
printf("************************************\n");
//游戏菜单---------------------------------------------
//选择------------------------------------------------
int select;
printf("请选择:");
scanf("%d", &select);
switch (select)
{
case 1: //开始游戏-----------------------------------
srand(time(NULL)); // 用时间戳设置随机数种子
int random = rand(); // 获取一个随机数
random = random % 101; // 让随机数的值在0--100之间
int count = 0; // 记录猜测次数
while (1)
{
int guess;
printf("请输入你猜测的数字:");
scanf("%d", &guess);
if (guess > random) // 大于
{
printf("猜大了\n");
count++;
}
else if (guess < random) // 小于
{
printf("猜小了\n");
count++;
}
else // 等于
{
count++;
printf("恭喜你,猜中了\n");
printf("共猜了 %d 次\n", count);
break;
}
//开始游戏-----------------------------------
}
break;
case 0: // 退出
return 0;
default:// select既不是1也不是0,即一个非法的输入
printf("非法的输入,请重新选择\n");
}
//选择------------------------------------------------
}
return 0;
}
2-4-1.游戏菜单
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h> // 包含函数 srand 和 rand 的文件
#include <time.h> // 包含函数 time 的文件
void PrintMune() // 游戏菜单
{
printf("************************************\n");
printf("*************1.开始游戏*************\n");
printf("*************0.退 出*************\n");
printf("************************************\n");
}
int main()
{
while (1)
{
//游戏菜单---------------------------------------------
PrintMune();
//游戏菜单---------------------------------------------
//选择------------------------------------------------
int select;
printf("请选择:");
scanf("%d", &select);
switch (select)
{
case 1: //开始游戏-----------------------------------
srand(time(NULL)); // 用时间戳设置随机数种子
int random = rand(); // 获取一个随机数
random = random % 101; // 让随机数的值在0--100之间
int count = 0; // 记录猜测次数
while (1)
{
int guess;
printf("请输入你猜测的数字:");
scanf("%d", &guess);
if (guess > random) // 大于
{
printf("猜大了\n");
count++;
}
else if (guess < random) // 小于
{
printf("猜小了\n");
count++;
}
else // 等于
{
count++;
printf("恭喜你,猜中了\n");
printf("共猜了 %d 次\n", count);
break;
}
//开始游戏-----------------------------------
}
break;
case 0: // 退出
return 0;
default:// select既不是1也不是0,即一个非法的输入
printf("非法的输入,请重新选择\n");
}
//选择------------------------------------------------
}
return 0;
}
2-4-2.选择
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h> // 包含函数 srand 和 rand 的文件
#include <time.h> // 包含函数 time 的文件
void PrintMune()
{
printf("************************************\n");
printf("*************1.开始游戏*************\n");
printf("*************0.退 出*************\n");
printf("************************************\n");
}
void Select() // 选择
{
int select;
printf("请选择:");
scanf("%d", &select);
switch (select)
{
case 1: //开始游戏-----------------------------------
srand(time(NULL)); // 用时间戳设置随机数种子
int random = rand(); // 获取一个随机数
random = random % 101; // 让随机数的值在0--100之间
int count = 0; // 记录猜测次数
while (1)
{
int guess;
printf("请输入你猜测的数字:");
scanf("%d", &guess);
if (guess > random) // 大于
{
printf("猜大了\n");
count++;
}
else if (guess < random) // 小于
{
printf("猜小了\n");
count++;
}
else // 等于
{
count++;
printf("恭喜你,猜中了\n");
printf("共猜了 %d 次\n", count);
break;
}
//开始游戏-----------------------------------
}
break;
case 0: // 退出
return 0;
default:// select既不是1也不是0,即一个非法的输入
printf("非法的输入,请重新选择\n");
}
}
int main()
{
while (1)
{
//游戏菜单---------------------------------------------
PrintMune();
//游戏菜单---------------------------------------------
//选择------------------------------------------------
Select();
//选择------------------------------------------------
}
return 0;
}
2-4-3.开始游戏
前排提示:函数里面也可以调用函数,所以 开始游戏 的函数在 选择 函数里面调用。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h> // 包含函数 srand 和 rand 的文件
#include <time.h> // 包含函数 time 的文件
void PrintMune()
{
printf("************************************\n");
printf("*************1.开始游戏*************\n");
printf("*************0.退 出*************\n");
printf("************************************\n");
}
void StartGame() // 开始游戏
{
srand(time(NULL)); // 用时间戳设置随机数种子
int random = rand(); // 获取一个随机数
random = random % 101; // 让随机数的值在0--100之间
int count = 0; // 记录猜测次数
while (1)
{
int guess;
printf("请输入你猜测的数字:");
scanf("%d", &guess);
if (guess > random) // 大于
{
printf("猜大了\n");
count++;
}
else if (guess < random) // 小于
{
printf("猜小了\n");
count++;
}
else // 等于
{
count++;
printf("恭喜你,猜中了\n");
printf("共猜了 %d 次\n", count);
break;
}
}
}
void Select()
{
int select;
printf("请选择:");
scanf("%d", &select);
switch (select)
{
case 1: //开始游戏-----------------------------------
StartGame();
//开始游戏-----------------------------------
break;
case 0: // 退出
return 0;
default:// select既不是1也不是0,即一个非法的输入
printf("非法的输入,请重新选择\n");
}
}
int main()
{
while (1)
{
//游戏菜单---------------------------------------------
PrintMune();
//游戏菜单---------------------------------------------
//选择------------------------------------------------
Select();
//选择------------------------------------------------
}
return 0;
}
这样就完成了,但是还有一点BUG,那就是选择0退出时,由于 return 在 Select 函数中,就变成了 Select 函数退出,而不是程序结束,所以可以让 Select 函数把选择的值返回回来,如果是0就在 main 函数中退出:
1、修改 Select 函数的返回值为 int
2、在 main 函数中用变量 select 接收返回值
3、判断 selset 的值,是0就结束程序,否则什么都不做
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h> // 包含函数 srand 和 rand 的文件
#include <time.h> // 包含函数 time 的文件
void PrintMune()
{
printf("************************************\n");
printf("*************1.开始游戏*************\n");
printf("*************0.退 出*************\n");
printf("************************************\n");
}
void StartGame() // 开始游戏
{
srand(time(NULL)); // 用时间戳设置随机数种子
int random = rand(); // 获取一个随机数
random = random % 101; // 让随机数的值在0--100之间
int count = 0; // 记录猜测次数
while (1)
{
int guess;
printf("请输入你猜测的数字:");
scanf("%d", &guess);
if (guess > random) // 大于
{
printf("猜大了\n");
count++;
}
else if (guess < random) // 小于
{
printf("猜小了\n");
count++;
}
else // 等于
{
count++;
printf("恭喜你,猜中了\n");
printf("共猜了 %d 次\n", count);
break;
}
}
}
int Select()
{
int select;
printf("请选择:");
scanf("%d", &select);
switch (select)
{
case 1: //开始游戏-----------------------------------
StartGame();
//开始游戏-----------------------------------
break;
case 0: // 退出
return 0;
default:// select既不是1也不是0,即一个非法的输入
printf("非法的输入,请重新选择\n");
}
}
int main()
{
while (1)
{
//游戏菜单---------------------------------------------
PrintMune();
//游戏菜单---------------------------------------------
//选择------------------------------------------------
int select = Select();
if(select == 0)
{
return 0;
}
//选择------------------------------------------------
}
return 0;
}
这样代码就改好了。
像这样使用多个函数将代码分区的方式叫 解耦,解耦有几个好处:
1、每个函数各自执行其功能,互不影响
2、main 函数中可以清晰的知道代码执行的大体逻辑
3、如果代码出了问题,可以将问题定位到某个函数中,更容易发现和修改
下期预告:
下一次是加餐,主要内容如下:
1、作用域与 static 关键字
2、函数的链式访问 和 递归