您现在的位置是:首页 >技术杂谈 >7. 函数的流程控制网站首页技术杂谈

7. 函数的流程控制

fo1_sky 2024-09-19 00:01:06
简介7. 函数的流程控制

在C++中,或是在任何编程语言中,都支持多种运行流程,一般分为下面三种:

  • 线性结构:程序代码按顺序执行一次,完毕后退出
  • 分支结构:程序代码按给定的条件选择运行路径,完毕后退出
  • 循环结构:程序代码按给定的条件一直循环,直到被迫停止或是主动停止。

线性结构

线性结构不必多说,在前面几节中都是这样的结构,随便再摘一段代码放过来:

int add( int a, int b )
{
	return a + b;
}

int main( int argc, char* argv[] )
{
	int c = add( 10, 20 ); // 第一次调用add函数
	int d = add( 16, 3 ); // 第二次调用add函数
	std::cout << c << std::endl;
	std::cout << d << std::endl;
	return 0;
}

这就是典型的线性结构代码。

分支结构

再复杂一点,可以给程序增加一个选择,在C++中,可以使用“if”这个语句,它的组成分为四部分:

  1. if开始的代码
  2. ()对代码进行封装
  3. ()内的判断条件
  4. 条件不成立时的“else”或是“else if”路径,此处可选。

说到判断,在C++中,if语句接受的是一个布尔(bool)型的变量,这个类型的变量只能取两个值:true和false,可以这样定义:

bool value = false;
bool value2;
value2 = true;

在这里,bool, false, true, 都是C++里面的保留字,不能用做变量名,在C++中,还有很多保留字,以后有机会再一个一个说明。

true:表示真
false:表示假

if的使用:

#include <stdlib.h>
#include <stdio.h>
int main ( int argc, char *argv[] )
{
	bool b = false; // 一般在定义变量时就要给它一个初始值,否则此变量的值不可预料。
	if ( b )
	{
		printf("b is true!
");
	}else          // 有else执行路径的代码
	{
		printf("b is false!
");
	}
}

编译输出:

b is false!

这样,这段代码就根据b的值到了不同的执行路径,此时另一段代码并没有被执行。
这里的判断条件是写死到代码中的变量,因为此值没有变化,那么程序的执行结果并没有发生变化,修改此段代码:

#include <stdlib.h>
#include <stdio.h>

int main( int argc, char *argv[] )
{
    if ( argc < 2 )   // 在这里判断输入参数的数量是否大于两个,如果小于两个,就执行这一行的输出
    {

        printf("use this program like : %s param
", argv[0] );
    }else
    {
        // 如果参数大于等于两个,就按顺序把参数打印出来
        for ( int i = 1; i < argc; i ++ )
        {
            printf("param %d : %s
", i, argv[i] );
        }
    }
    return 0;
}

编译后运行:

fosky@fosky-CW65S:~/workspace/cppbook$ ./testif
use this program like : ./testif param
fosky@fosky-CW65S:~/workspace/cppbook$ ./testif param1 param2
param 1 : param1
param 2 : param2

可以看到,此程序根据参数的个数选择了不同的执行路径,而且里面还多了个循环。

这里的判断条件是一个比较过程。那么可以做为判断条件的语句,只要是C++认为能计算出真假的内容都可以。
可以是一个变量,也可以是一个表达式,或是一些逻辑操作语句。

表达式

由运算符组成的语句,比如下面:

int a = 1;
int b = a + 1; // 表达式
if ( b )       // 没有else路径的判断
{
}

if ( a )
{
}

if ( a > b )     // 表达式
{
}

if ( a == b )    // 表达式
{
}

if ( ( b / 2 ) == 1 )   // 表达式
{
}

这段代码编译没问题,说明C++认为这些计算结果都可以转换为bool数据。

操作符

说到这里,就要补充一下C++的计算符,大概分为三类:

  1. 数学计算类型:加(+),减(-),乘(*),除(/),取余(%),自加(++),自减(–)
  2. 逻辑操作符:逻辑与(&&),逻辑或(||),取反( !),,大于(>),小于(<),大于等于(>=),小于等于(<=),等于(==),不等于(!=)
  3. 比特位操作符:按位与(&),按位或(|),异或(^),向左移位(<<),向右移位(>>)

数学计算操作符可以按数学中的含义理解,而计算顺序也同样和数学计算式相同。如下计算过程:

  int c = 10 + 2 * 3;
  print("%d
", c );  // 这里输出16,即先计算乘法,后计算加法

如果想调整计算顺序,需要使用括号把想优先计算的内容包起来:

  int c = (10 + 2 ) * 3;
  printf("%d
", c );   // 这里输出36

能参与表达式计算的内容有多种,不光是明显的变量,函数同样也可以放到表达式中,如下代码:

int fun( int a, int b )
{
	return a + b;
}

int a = fun(10, 6 ) * 3;
int b = 6 + fun( 3, 6 ) * 2;

float f = fun( 3, 34 ) * 0.5;  // 不同类型的变量也可以放到一起计算,C++自动把低精度的数据转换为更高精度的数值

这里函数和变量处于同样的地位,都被看做一个单一的变量,说到底,有返回值的函数在函数最后把计算结果返回了,这时候其实是临时产生了一个变量放在了表达式的对应位置。

同样,变量也可以放到表达式中,在以前的代码中已经多次使用。

C++数据类型的表达数值范围,一般顺序如下:
char < short < int < long < long long
int < float < double

如果有多种类型的数据在同一个表达式中参与计算,则在计算过程中自动进行数据类型的转换,当然这个转换并不影响原数据的值和类型,如下代码中:

  int c = 10;
  float f = 2.56;
  float f2 = c + f;    // 在这里,变量c先提升为float,然后再和f计算结果得到结果

布尔类型可以看做一个特殊的类型,所有的其它数据,只要不是0,一般都可以转换为bool类型的真,为0的值,或是表达式计算为0的值,都会转换为bool类型的假。

另一种数据类型,指针,同样可以做为条件判断的依据,同样的原则,指针的值为0, 结果为假,指针的值不为0,结果为真,一般指针的0值用NULL来表示。

如下代码中:

  bool b = 10;  // 此时b为真(true)
  bool b1 = 0;  // 此时b为假(false)
  bool b2 = (3 > 5);  // 逻辑判断为假,此时b为假
  float *pf = NULL; // 定义一个指针变量,初始地址为NULL,空。
  bool b3 = pf;    //  这时b3的值为假
  bool *pb = &b2;  // pb这个指针指向了变量b2的地址
  bool b4 = pb;    // bp有值,则b4为真。

那么,做为“if”的判断条件,也可以是以上任意表达式:

int fun( int a, int b )
{
	return a % b;
}

if ( fun ( 30, 4 ) )
{
	printf("fun result is not zero!
");
}else
{
	printf("fun result is zero!
");
}

以上代码通过判断fun函数的返回值来执行不同的代码。

循环结构

一般循环结构在C++中for或while这两个方式构建。
先看代码:

int c = 10;
for ( int i = 0; i < c; i ++ )  // 当i的值小于c的值时,输出i,在下一次循环时,i的值加1
{
	printf("%d ", i);
}

此段代码输出:

0 1 2 3 4 5 6 7 8 9

可以看到,首先出现的是for关键字,后面跟着括号,而括号里面用”;”分成了三部分。
其中第一部分叫做循环的初始值,第二部分叫做循环的终止条件,第三部分则是变化部分。这三部分中,只有第二部分一定不能少,第一和第三部分都可缺失:

int c = 10;
int i = 0;   // 初始值在循环外定义
for ( ; i < 10; )
{
	printf("%d ", i);
	i ++;      // 变化在循环内产生,每次循环i 加 1.
}

for ( int k = 0; k < 5; )
{
	printf("%d 
", k );
	k ++;
}

i = 0; // 这段代码上面部分已经定义了变量 i , 在这里只需要重新初始化
for ( ; i < 3; i ++ )
{
	printf("%d ", i );
}

同样和if一样,条件判断部分也可以由各种各样的表达式代替,在这里就不一一举例了。

使用while构建的循环结果,分两种,while在前,while在后。
while在前,是先判断执行条件,符合条件后才会进入循环内。

int c = 10;
while( c > 0 )  // 只要c大于0,就执行while内部的代码,即输出c的值,然后c自减1.
{
	printf("%d ", c );
	c --;
}

while在后,是先执行循环内的代码,然后才判断执行条件,这样就会至少执行一次内部代码,如下:

int c = -1;
do
{
	printf("%d
", c );
	c --;
}while( c > 0 )

可以看到,此代码至少被执行一次,而不管变量c是什么值。

另外一个被广大程序员嫌弃的关键字"goto",其实也可以构建循环逻辑,只是不太长用,而且也容易把代码搞乱,因此不建议,不过这里也给出一个例子:

#include <stdlib.h>
#include <stdio.h>

int main( int argc, char *argv[] )
{
	int c = 0;
	
	LOOP:          // 这里定义一个标签,goto语句的转向,需要这样的标签
	 c ++;
	 printf("%d ", c );
	 if ( c < 10 )
	 {
		 goto LOOP;
	 }
	
	printf("
");
	
	return 0;
}


编译运行:

fosky@fosky-CW65S:~/workspace/cppbook$ ./goto
1 2 3 4 5 6 7 8 9 10 

关于代码的结构就说这么多,下一节主要再聊聊函数的调用。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。