前言
这一节简单地介绍C中两个基础语法
1. if…else…的替代方法:switch…case…
2. 函数function的定义及声明
参考及引用:笨办法学C
一、switch用法
swich是一种选择语法,使用场景是针对不同情况需要做出不同操作。因此它可以代替某些if…else…语句。当不同情况数较多时,就会体现出switch的优势。
1.使用语法
我们直接看一段代码来掌握其语法
#include <stdio.h>
int main(int argc, char *argv[])
{
if(argc != 2) {
printf("ERROR: You need one argument.\n");
// this is how you abort a program
return 1;
}
int i = 0;
for(i = 0; argv[1][i] != '\0'; i++) {
char letter = argv[1][i];
switch(letter) {
case 'a':
case 'A':
// letter=='a'或'A'时,执行的程序段
printf("%d: 'A'\n", i);
break;
case 'e':
case 'E':
// letter=='e'或'E'时,执行的程序段
printf("%d: 'E'\n", i);
break;
case 'i':
case 'I':
// letter=='e'或'E'时,执行的程序段
printf("%d: 'I'\n", i);
break;
case 'o':
case 'O':
// letter=='o'或'O'时,执行的程序段
printf("%d: 'O'\n", i);
break;
case 'u':
case 'U':
// letter=='u'或'U'时,执行的程序段
printf("%d: 'U'\n", i);
break;
case 'y':
case 'Y':
// letter=='y'或'Y'时,执行的程序段
if(i > 2) {
// it's only sometimes Y
printf("%d: 'Y'\n", i);
}
break;
default:
// letter取其他值时,执行的程序段
printf("%d: %c is not a vowel\n", i, letter);
}
}
return 0;
}
输入及运行结果如下:
$ ./ex13 aAeOk
0: 'A'
1: 'A'
2: 'E'
3: 'O'
4: k is not a vowel
从程序中我们提取出关键信息:
switch()的括号中是可以取不同值的变量(必须是广义上的整数,比如字母或者结果为整数的布尔表达式),紧跟在case后面的值是该变量可能的不同取值,case下面的程序段是变量正好等于该值时,要执行的程序;
default指无论何值都可以执行的程序。看到这里可能有个疑问,从程序运行结果看default后面的程序,是在letter不取前面case中的任何值时才会执行,怎么能说无论何值都会执行呢?
这个问题的答案关键在break。我们通过switch的执行原理可以获得答案。
2.执行原理:跳转表
switch实际上是一个跳转表,switch(a)这句话就定义了一个跳转表的起始位置,并且要将这个起始地址加上a的值(因此a必须是整数),得到要跳转到的目的地址。
如果这个目的地址等于下面某个case代表的目的地址,就跳转到这个case处继续向下运行程序。
这时注意:如果没有break,这个程序就从当前case处,一直向下运行。会把下面的case带的代码也执行一遍。因此要使用break来进行隔断! 这样才能保证只执行某个case处的代码。
以上面代码为例具体解释跳转表的跳转过程:
编译器会标记swicth语句的顶端,我们先把它记为地址Y。Y是整张表的起始地址(类似于数组的其起始地址)。
接着对switch中的表达式求值,产生一个数字。在上面的例子中,数字为argv[1]中字母的原始的ASCLL码。
编译器也会把每个类似case 'A’的case代码块翻译成这个程序中距离语句顶端的地址,所以case ‘A’就在*(Y + ‘A’)处。
接着计算是否Y+letter位于switch语句中,如果距离太远则会将其调整为Y+Default。
一旦计算出了地址,程序就会“跳”到代码的那个位置并继续执行。这就是一些case代码块中有break而另外一些没有的原因。
如果输出了’a’,那它就会跳到case ‘a’,它里面没有break语句,所以它会贯穿执行底下带有代码和break的case ‘A’。
最后它执行这段代码,执行break完全跳出switch语句块。
这时再看上面那个关于default的问题。
如果default前面的case都不带break,那么default后面的代码一定会被执行,它之所以不会被执行,就是因为前面的break!所以说default是变量无论取何值都可以执行的程序。
二、函数定义及声明
1.定义语法
定义时按照下面的格式:
返回值数据类型 函数名(数据类型 参数1, 数据类型 参数2,...)
{
函数体
return 返回值
}
函数体包含声明部分和语句部分,声明部分用于声明及初始化函数中用到的变量,语句部分就是执行运算等其他操作的语句。
2.提前声明
举一个函数定义的栗子(虽然定义了这么多函数,最终执行的仍是主函数main哦~)
#include <stdio.h>
#include <ctype.h>
// forward declarations
int can_print_it(char ch);
void print_letters(char arg[]);
void print_arguments(int argc, char *argv[])
{
int i = 0;
for(i = 0; i < argc; i++) {
print_letters(argv[i]);
}
}
void print_letters(char arg[])
{
int i = 0;
for(i = 0; arg[i] != '\0'; i++) {
char ch = arg[i];
if(can_print_it(ch)) {
printf("'%c' == %d ", ch, ch);
}
}
printf("\n");
}
int can_print_it(char ch)
{
return isalpha(ch) || isblank(ch);
}
int main(int argc, char *argv[])
{
print_arguments(argc, argv);
return 0;
}
其中下面这两行是提前声明函数的语句。就是把要声明的函数,其定义参数的那行复制一遍就行。
int can_print_it(char ch);
void print_letters(char arg[]);
为什么需要提前声明?
可以看到,在下面的程序中,函数print_arguments先被定义,函数体中用到了函数can_print_it和print_letters;然而这两个函数在后面才被定义,因此为了不报错,我们需要在定义print_arguments前,声明这两个函数。
如果我们最后定义函数print_arguments,就不需要提前声明了。
总结
1、switch的本质是跳转表;
2、switch用法中的break很重要,可以用它来实现 if(xxx || xxx)的逻辑,或者其他更复杂的操作;
3、使用自定义函数时,注意是否需要提前声明。