第四章 常量、宏、枚举、函数
本章任务:1.const关键字 2.宏定义 3.枚举常量 4.自定义函数 5.函数递归
1.1 const关键字
我们可以使用const关键字来将一个变量变为常量,形式为:const int i = 20; 为什么称为常量呢?因为i中的值一旦被设定,后期就无法更改。这样的常量称为符号常量(命名常量)。
1.2 宏
#define开头的语句称为宏定义语句,结尾不需要分号。例如:
#define PI 3.14
int main(int argc, const char * argv[])
{
float r = 5.0f,s;
s = PI * r * r;
printf("圆的面积是:%.2f",s);
return 0;
}
注意:这里#define所定义的常量没有数据类型,其实本质上就是字符。预编译就是在程序编译以前进行的,而#define所定义的常量都会在预编译之前进行替换,所以代码中的PI都会被3.14替换。
宏函数
列举一个简单的例子:
#define MAX(x,y) x>y?x:y
int main(int argc, const char * argv[])
{
int i , j;
scanf("%d %d" , &i,&j);
printf("%d和%d之间的最大值为:%d",i,j,MAX(i,j));
return 0;
}
宏定义的本质为替换全部的文本信息,上边定义的宏函数可以在mian中替换为:
printf("%d和%d之间的最大值为:%d",i,j,x>y?x:y);
PS:一般情况是这样的,当计算表达式相对简单时,宏是很好用的,如果计算表达式过于复杂,宏定义就会不适应。优点:使用宏能够加快执行速度。缺点:如果一个程序多出用到宏,在程序运行时,每个宏都被替换为一个副本,这样程序的内存开销加大。
1.3 枚举
枚举就是一组命名的常数,并且范围数量是有限的。而枚举所定义出来的变量可以在这个枚举的范围内取一个赋值给枚举变量。如果为变量赋值时,只使用枚举定义出来的命名常量的话,那么我们就不会再给其赋值一些不存在的情况。语法:enum spectrum {red,orange,yellow,green,blue};
上边就是一个枚举的语法,因为枚举就是一组正数常量,我们能够知道red=0,orange=1,yellow=2,green=3,blue=4。
枚举中也可以为各个值指定一个值: enum spectrum {red,orange=5,yellow,green=10,blue};我们能够知道red=0,orange=5,yellow=6,green=10,blue=11。
#include <stdio.h>
int getNum(int x);
int countSum(int x,int y,int z);
enum puKe {A,B=2,C=3,D=4,F=5,G=6,H=7,I=8,M=9,L=10,J,Q,K};
int main(int argc, const char * argv[])
{
enum puKe x1=A,x2=L,x3=K;
int i = countSum(x1, x2, x3);
printf("%d",i);
return 0;
}
//扑克21点问题
int countSum(int x,int y,int z)
{
int sum=0;
sum =getNum(x)+getNum(y)+getNum(z);
if (sum>21) {
if (x==0) {
x=1;
}
if (y==0) {
y=1;
}
if (z==0) {
z=1;
}
sum = getNum(x)+getNum(y)+getNum(z);
}
return sum;
}
int getNum(int x)
{
switch (x) {
case 1:
x=1;
break;
case A:
x=11;
break;
case B:
case C:
case D:
case F:
case G:
case H:
case I:
case M:
case L:
x = x;
break;
case J:
case Q:
case K:
x = 10;
break;
default:
break;
}
return x;
}
题目说明:在扑克牌的二十一点游戏中,纸牌2到10分别以他们的面值计分,JQK分别按10分计算。而纸牌A根据玩家最后手中的总分来计分,首先将A以11分来计算,如果玩家总分大于21分,则A按照1分来算,如果玩家没有超过21分,那么A就按照11分计算。尝试编写一个函数,有三个参数,每一个参数代表一张牌,然后根据传入的牌计算总分,并返回。
1.4 函数
从函数定义上分可以分为两类:系统库函数和自定义函数。从返回值上划分也可以分为两类:有返回值函数和无返回值函数。从参数个数上分也可以分为两大类:有参函数和无参函数。
自定义函数:return_type function_name(parameter_list)
{
//函数体
}
自定义函数声明:return_type function_name(parameter_list);
形参和实参:形式参数是在定义函数时使用,并且在整个函数内部可以使用,除了函数之后就不能使用。实际参数是在调用函数时使用,通过实际参数将数据传递给形式参数,然后再经形式参数传递给函数。
PS:如果在函数中对形参做出改变,那么在函数执行完毕后实参的值也不会发生变化,因为在函数内部所操作的都是实参副本。
1.5 变量的存储类型
局部变量的存储类型:局部变量的存储类型只能是 auto,static和register。
平时使用的变量声明并没有指定存储类型,那么这些变量都是auto类型的。 自动局部变量在每次声明之后会创建,当其所在函数完毕后,该自动局部变量就会被释放。
static int i;一个被static所修饰的局部变量能够在程序运行期间一直保持其最后一次被改变的数值。
register int i; 寄存器变量。寄存器变量和自动局部变量具有相同的持续时间,区别在于变量存储区域被定为的位置,所有变量除了寄存器变量外都保存在计算机内存中,而寄存器变量是存储在CPU高速存储区(寄存器)内。任何时候,应用程序从来不用寄存器变量。
1.6 全局变量的存储类型
全局变量可以被声明为static 和 extern。 extern修饰的全局变量称为外部变量,作用是将一个源代码中的全局变量扩展到其他源文件中。
1.7 函数递归
//汉诺克移动盘子(递归)开始
void move(int n,char fetch,char put)
{
count++;
printf("将第%d个盘子从%c---->%c\n",n,fetch,put);
}
void hanoi(int n,char a,char b,char c)
{
if (n==1) {
move(1, a, c); //如果一个盘子,第一个移动到第三个
}else{
//1.将n-1个盘子从a----->b
hanoi(n-1, a, c, b);
//2.讲第n个盘子从a----->c
move(n, a, c);
//3.将n-1个盘子从b----->c
hanoi(n-1, b, a, c);
}
}
//汉诺克移动盘子(递归)开始
很典型的递归实例,记得要好好研究一下。还有一个比较简单,容易理解:
//求一个数的阶乘,非递归算法
long factorial(int x)
{
int sum = 1;
for(int i=0;i<x;i++)
{
sum *= i;
}
return sum;
}
//求一个数的阶乘,递归算法
long factorial(int x)
{
if(x == 1) return 1;
return x * factorial(x-1);
}