c++教程3控制结构与函数

3控制结构与函数

  • if

  • switch

  • for

  • while

  • 声明函数

  • 传递参数

  • 重载函数

  • 优化函数

3.1 if

C++ if 关键字执行基本的条件测试,对给定表达式进行布尔值(true 或 false)求值,其语法如下:


if ( test-expression ) { statements-to-execute-when-true }

测试后面的大括号可以包含一个或多个语句,每个语句都以分号结束,但这些语句只有在发现表达式为真时才会被执行。如果测试结果为假,程序将继续执行下一个任务。

另外,if 语句还可以在测试失败时提供其他执行语句,方法是在 if 语句块后附加 else 语句块,如下所示:


if ( test-expression ) { statements-to-execute-when-true }

else { statements-to-execute-when-false }

如果测试成功时只需执行一条语句,则可以省略大括号,但保留大括号会使代码更清晰。

要测试两个条件,测试表达式可以使用 && 操作符,例如 if ( ( num > 5 ) && ( letter == 'A' ) )。另外,if 语句也可以 "嵌套 "在另一个 if 语句中,这样只有当两个测试都成功时,内部语句块中的语句才会被执行,但如果外部测试成功,外部语句块中的语句才会被执行。

ifelse.cpp


#include <iostream>
using namespace std;

int main()
{
  int num = 8;
  char letter = 'A';

  if( num > 5 )
  {
    cout << "Number exceeds five" << endl;

    if( letter == 'A' )
    {
      cout << "Letter is A" << endl;
    }

  }
  else { cout << "Number is five or less" << endl; }

  return 0;
}

执行

# ./ifelse
Number exceeds five
Letter is A

表达式 if ( flag == true ) 可以写成 if ( flag ),避免嵌套超过三层的 if 语句,以避免混淆和错误。

3.2 switch

使用较长的 if-else 语句进行条件分支时,通常使用 switch 语句会更有效,尤其是当测试表达式只对一个变量求值时。

switch 语句的工作方式与众不同。它获取给定的变量值,然后在多个 case 语句中寻找匹配值。然后执行与匹配的 case 语句值相关的语句。

如果没有找到匹配值,则不会执行任何 case 语句,但可以在最后的 case 语句后添加默认语句,指定在没有找到匹配值时要执行的语句。

重要的是,要在每个 case 语句后加上 break 关键字,以便在执行了与匹配的 case 值相关的所有语句后,停止程序继续执行开关块,除非这正是你所需要的。例如,每个包含三个值的代码块都需要一条语句,就像这样:

switch( variable-name )

{

case value1 ; case value2 ; case value3 ;

statements-to-be-executed ; break ;

case value4 ; case value5 ; case value6 ;

statements-to-be-executed ; break ;

}

通常情况下,每个 case 语句都有自己的执行语句集,并以 break 结束,如相反的程序。

switch.cpp

#include <iostream>
using namespace std;

int main()
{
  int num = 3;

  switch ( num )
  {
    case 1 : cout << num << " : Monday"; break;
    case 2 : cout << num << " : Tuesday"; break;
    case 3 : cout << num << " : Wednesday" ; break;
    case 4 : cout << num << " : Thursday"; break;
    case 5 : cout << num << " : Friday"; break;

    default : cout << num << " : Weekend day";
  }
  cout << endl;
  return 0;
}

执行

# ./switch
3 : Wednesday

请注意,默认语句后面不需要使用 break 关键字,因为默认语句总是出现在 switch 语句的最后。

3.3 for

循环是程序中自动重复的一段代码。循环的迭代次数由循环中的条件测试控制。当测试的表达式为真时,循环将继续进行,直到测试的表达式变为假时,循环结束。

C++ 编程中的三种循环结构是 for 循环、while 循环和 do-while 循环。最常用的循环可能是 for 循环,其语法如下

for ( initializer ; test-expression ; incrementer ) { statements }

初始化器为循环迭代次数的计数器设置起始值。为此使用了一个整数变量,传统上命名为 "i"。在循环的每次迭代中,都会对测试表达式进行评估,只有当该表达式为真时,迭代才会继续。当测试表达式为假时,循环立即结束,不再执行语句。每次迭代时,计数器都会递增,然后执行语句。

循环可以嵌套在其他循环中,这样内循环将在外层循环的每次迭代中完全执行其迭代。

#include <iostream>
using namespace std;

int main()
{
  int i , j;

  for ( i = 1; i < 4; i++ )
  {  
    cout <<  "Loop iteration: " << i << endl;

    // Uncomment the lines below to add the nested loop.
    // for ( j = 1; j < 4; j++ )
    // {
    //   cout << "    Inner loop iteration: " << j << endl;
    // }
  }

  return 0;
}

执行

# ./forloop
Loop iteration: 1
Loop iteration: 2
Loop iteration: 3

3.4 while

for循环的一种替代方法是使用while关键字,后面跟一个待求值表达式。当表达式为真时,将执行测试表达式后面大括号中的语句。然后将再次评估表达式,并继续执行 while 循环,直到发现表达式为假为止。

循环的语句块必须包含会影响测试表达式的代码,以便将评估结果更改为假,否则将创建一个会锁定系统的无限循环!当被测试表达式在第一次求值时被发现为假时,while 循环的语句块将永远不会被执行。

while循环的一个微妙变化是在循环的语句块前加上do关键字,并在其后加上 while 测试,语法如下:

do { statements-to-be-executed } while ( test-expression );

在 do-while 循环中,语句块总是至少被执行一次,因为表达式在循环第一次迭代后才会被求值。如果不小心开始运行一个无限循环,请按 Ctrl + C 键终止进程。

可以在任何类型的循环中加入 break 语句,以便在满足测试条件时立即终止循环。break 语句可确保不再执行该循环的迭代。同样,可以在任何一种循环中加入 continue 语句,以便在满足测试条件时立即终止循环的特定迭代。continue 语句允许循环进行下一次迭代。

while.cpp

{
  int i = 0; 
  vector <int> vec(10);

  while ( i < vec.size() )
  {
    i++; // 1 - 10

    // Uncomment the next line to skip at three.
     if ( i== 3 ) { cout << " | Skipped"; continue; }

    // Uncomment the next line to break on eight.
     if ( i == 8 ) { cout << endl << "Done"; break; }

    vec[ i-1 ] = i;	// vec[0] = 1, vect[1] = 2, etc.

    cout << " | " << vec.at( i-1 );
  }
  cout << endl;

  return 0;
}

执行

# ./forloop
Loop iteration: 1
Loop iteration: 2
Loop iteration: 3

参考资料

3.5声明函数

函数包含一段为程序提供特定功能的代码。从主程序调用函数时,将执行其语句,完成后可选择将值返回给主程序。使用函数有三大好处:

  • 函数使程序代码更易于理解和维护。
  • 其他程序可以重复使用经过测试的函数。
  • 在大型项目中,多个程序员可以通过处理程序中的不同函数来分担工作量。

每个函数都在程序代码的早期以"原型"的形式声明,原型由函数返回值的数据类型和函数名组成,函数名后有括号,括号中可选择包含函数可能使用的"参数"数据类型列表。函数原型声明的语法如下

return-data-type function-name ( arguments-data-type-list );
// 示例
float computeArea( float, float );

严格来说,函数原型中的参数被称为 "形式参数"。
函数的定义出现在程序代码的后面,包括原型的重复和实际的函数体。函数体是每次调用函数时要执行的语句,包含在一对大括号中。如果函数不会向调用者返回任何值,请使用 void 关键字。

编译器会根据原型检查函数定义,因此实际返回的数据类型必须与原型中指定的数据类型一致,而且提供的任何参数在数量和数据类型上都必须一致。如果定义与原型不匹配,编译就会失败。一个简单的 computeArea 定义可能如下所示:

float computeArea( float width, float height )

{
  return ( width * height ) ;
}

在函数中声明的变量只能在该函数的本地使用,不能在其他函数中全局访问。这种限制被称为 "变量作用域"。

#include <iostream>
using namespace std;

float bodyTempC();
float bodyTempF();

int main()
{
  cout << "Centigrade: " << bodyTempC() << endl;
  cout << "Fahrenheit: " << bodyTempF() << endl;
  return 0;
}

float bodyTempC()
{
  float temperature = 37.0;
  return temperature;
}

float bodyTempF()
{
  float temperature = 98.6;
  return temperature;
}

执行

# ./scope
Centigrade: 37
Fahrenheit: 98.6

3.6传递参数

函数调用经常向函数提供参数值。这些参数可以是任何数量和数据类型,但必须与函数原型声明中指定的参数一致。

传递给函数的参数仅提供原始值的副本,这种过程称为"按值传递"。

传递给参数的值可以是程序代码中指定的"静态"值,也可以是用户输入的"动态"值。在命令提示符下,C++ 的 cin 函数可以与 >> 输入流操作符一起使用。

在定义函数之前,必须先声明函数原型。通常,函数原型出现在主函数之前,而函数定义则出现在主函数之后。

函数原型可以为参数指定默认值,当调用没有传递参数值时,将使用默认值。可以在原型中为多个参数分配缺省值,但这些参数必须始终出现在参数列表的末尾,排在其他参数之后。

与主函数可以调用函数一样,函数也可以调用其他函数并传递参数。

#include <iostream>
using namespace std;

float fToC( float degreesF = 32.0 );

int main()
{
  float fahrenheit, centigrade;

  cout << "Enter a Fahrenheit temperature: ";
  cin >> fahrenheit;

  centigrade = fToC( fahrenheit );

  cout << fahrenheit << "F is " << centigrade << "C";
  cout << endl << "Freezing point: " << fToC() << "C";

  cout << endl;
  return 0;
}

float fToC( float degreesF )
{
  float degreesC = ( (5.0 / 9.0 ) * ( degreesF - 32.0 ) );
  return degreesC;
}

执行

# ./args
Enter a Fahrenheit temperature: 89
89F is 31.6667C
Freezing point: 0C

3.7函数重载

函数"重载"允许同名函数在同一程序中并存,前提是它们的参数在数量、数据类型或数量和数据类型上有所不同。编译器通过识别函数参数的数量和数据类型,将函数调用与正确版本的函数相匹配,这一过程被称为"函数解析"。

当要执行的任务相似但又有细微差别时,创建重载函数非常有用。

#include <iostream>
using namespace std;

float computeArea( float );
float computeArea( float, float );
float computeArea( char, float, float );

int main()
{
  float num, area;

  cout << "Enter dimension in feet: ";
  cin >> num;

  area = computeArea( num );
  cout << "Circle: Area = " << area << " sq.ft." << endl;

  area = computeArea( num, num );
  cout << "Square: Area = " << area << " sq.ft." << endl;

  area = computeArea( 'T', num, num );
  cout << "Triangle: Area = " << area << "sq.ft." << endl;

  return 0;
}

float computeArea( float diameter )
{
  float radius = ( diameter / 2 );
  return ( 3.141593 * (radius * radius) );
}

float computeArea( float width, float height )
{
  return ( width * height);
}

float computeArea( char letter, float width , float height )
{
  return ( width / 2 ) * height;
}

执行

# ./overload
Enter dimension in feet: 89
Circle: Area = 6221.14 sq.ft.
Square: Area = 7921 sq.ft.
Triangle: Area = 3960.5sq.ft.

# ./overload
Enter dimension in feet: 89.9
Circle: Area = 6347.6 sq.ft.
Square: Area = 8082.01 sq.ft.
Triangle: Area = 4041.01sq.ft.

3.8递归

函数可以递归调用自己,以重复执行其函数体中包含的语句,这与循环非常相似。与循环一样,递归函数必须包含递增器和条件测试,以便再次调用自身,或在满足条件时停止重复。递归函数的语法如下:

递增器将改变传递参数的值,因此后续调用将把调整后的值传递回函数。

return-data-type function-name ( argument-list )

{

  statements-to-be-executed ;
  incrementer ;
  conditional-test-to-recall-or-exit ;

}
int main()
{
  computeFactorials( 1, 8 );
  return 0;
}

int computeFactorials( int num, int max )
{
  cout << "Factorial of " << num << ": ";
  cout << factorial( num ) << endl;
  num++;
  if( num > max ) return 0;
  else return computeFactorials( num , max );
}

int factorial( int n )
{
  int result;
  if( n == 1 ) result = 1;
  else
  result = ( factorial( n - 1 ) * n );
  return result;
}

执行

# ./optimize
Factorial of 1: 1
Factorial of 2: 2
Factorial of 3: 6
Factorial of 4: 24
Factorial of 5: 120
Factorial of 6: 720
Factorial of 7: 5040
Factorial of 8: 40320

输出会列出阶乘值(阶乘 3 是 3x2x1=6,等等),但可以通过优化阶乘()函数来改进程序。如果使用三元运算符编写该函数,就不需要变量。它只包含一条语句,因此其定义可以取代原型声明,成为 "内联 "声明。这意味着程序无需在声明和定义之间进行检查,从而提高了效率。

在C++中,"内联"(inline)是一个关键字,用于提示编译器在编译时将函数调用处的代码替换为函数体,以避免函数调用的开销。内联函数的目的是提高程序的执行效率。

使用内联函数可以减少函数调用的开销,因为函数调用涉及保存现场、压栈、跳转等操作,而内联函数的代码会直接插入到调用处,避免了这些开销。内联函数适用于函数体较短、频繁调用的情况。

内联函数的使用是一种建议,而非强制性要求。编译器会根据一些因素(如函数体大小、调用频率等)来决定是否将函数进行内联展开。此外,内联函数的定义通常应该放在头文件中,以便在多个源文件中进行内联展开。

需要注意的是,过度使用内联函数可能导致代码膨胀,增加可执行文件的大小。因此,在使用内联函数时,需要权衡代码大小和执行效率之间的平衡,合理选择使用内联的函数。

3.9小结

  • if语句将给定的测试表达式评估为布尔值的真或假。
  • 在if语句后面的大括号中包含的语句只有在评估结果为真时才会被执行。
  • if和else关键字用于根据测试表达式的结果执行条件性分支。
  • witch语句是条件性分支的另一种形式,它将case语句与给定值相匹配。
  • for 循环结构有参数声明初始化器、测试表达式和增量器或减量器。
  • while循环和do-while循环必须在其循环体中始终有一个递增器或递减器。
  • 任何类型的循环都可以通过在循环体中加入break语句来立即终止。
  • 任何类型的循环的单次迭代都可以通过在循环体中加入continue语句而被跳过。
  • 函数通常在程序开始时被声明为原型,并在主函数之后定义。
  • 在一个函数中声明的变量只能从该函数中访问,因为它们只有局部范围。
  • 如果在函数原型和定义中声明了参数,那么值可以被传入函数。
  • 重载函数具有相同的名称,但声明的参数数量或类型不同。
  • 递归函数重复调用自己,直到满足测试条件。
  • 只有一个或两个语句的简短的函数定义可以用inline关键字代替原型来声明。
posted @ 2023-12-29 20:21  磁石空杯  阅读(47)  评论(0编辑  收藏  举报