c++:-1


C++第一部分介绍基础:c++:-0,本节介绍C++中函数使用。

函数

函数调用

调用函数需要先声明函数原型
嵌套调用:
c++:-1

参数传递

  • 在函数被调用时才分配形参的存储单元
  • 实参可以是常量、变量或表达式
  • 实参类型必须与形参相符
  • 值传递是传递参数值,即单向传递
  • 引用传递可以实现双向传递
  • 常引用作参数可以保障实参数据的安全

传引用比传对象计算消耗小

可变参数

C++中提供了两种方法:

  • 如果所有的实参类型相同,可以传递一个名为initializer_list的标准库类型;
  • 如果实参的类型不同,我们可以编写可变参数的模板。

initializer_list

initializer_list是一种标准库类型,用于表示某种特定类型的值的数组,该类型定义在同名的头文件中
c++:-1

使用:

  • 使用模板时,我们需要在模板名字后面跟一对尖括号,括号内给出类型参数。例如:
    initializer_list ls; // initializer_list的元素类型是string
    initializer_list li; // initializer_list的元素类型是int
  • initializer_list比较特殊的一点是,其对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值。
  • 含有initializer_list形参的函数也可以同时拥有其他形参

应用:

  • 在编写代码输出程序产生的错误信息时,最好统一用一个函数实现该功能,使得对所有错误的处理能够整齐划一。然而错误信息的种类不同,调用错误信息输出函数时传递的参数也会各不相同。
  • 使用initializer_list编写一个错误信息输出函数,使其可以作用于可变数量的形参。

内联函数

作用:编译时在调用处用函数体进行替换,节省了参数传递、控制转移等开销。

声明时使用关键字: inline
注意:

  • 内联函数体内不能有循环语句和switch语句;
  • 内联函数的定义必须出现在内联函数第一次被调用之前;
  • 对内联函数不能进行异常接口声明。

常量表达式

C++-11中新增,用于初始化常量表达式
(1)constexpr函数语法规定

  • constexpr修饰的函数在其所有参数都是constexpr时,一定返回constexpr;
  • 函数体中必须有且仅有一条return语句。

(2)constexpr函数举例

constexpr int get_size() { return 20; } constexpr int foo = get_size();  //正确:foo是一个常量表达式 

函数默认参数值

(1)默认参数值的说明次序

  • 有默认参数的形参必须列在形参列表的最右,即默认参数值的右面不能有无默认值的参数;
  • 调用时实参与形参的结合次序是从左向右。
    例:
    int add(int x, int y = 5, int z = 6);//正确
    int add(int x = 1, int y = 5, int z);//错误
    int add(int x = 1, int y, int z = 6);//错误
    (2)默认参数值与函数的调用位置
  • 如果一个函数有原型声明,且原型声明在定义之前,则默认参数值应在函数原型声明中给出;如果只有函数的定义,或函数定义在前,则默认参数值可以函数定义中给出。
    例:

c++:-1

重载函数

函数名相同,参数类型和返回类型不同;参数个数不同

c++:-1
c++:-1
c++:-1

系统函数

C++的系统库中提供了几百个函数可供程序员使用,例如:

  • 求平方根函数(sqrt)
  • 求绝对值函数(abs)
  • 正弦值、余弦值和正切值的函数:sin()、cos()、tan()
    使用系统函数时要包含相应的头文件,例如:cmath

举例

计算n次方

计算x的n次方

#include <iostream>  using namespace std; //计算x的n次方 double power(double x, int n) {     double val = 1.0;     while (n--) val *= x;     return val; } int main() {     cout << "5 to the power 2 is "          << power(5, 2) << endl;     return 0; } 

进制转换(8-10)

输入一个8位二进制数,将其转换为十进制数输出。

#include <iostream> using namespace std;  double power (double x, int n); //计算x的n次方  int main() {     int  value = 0;     cout << "Enter an 8 bit binary number  ";     //cin:只有在输入完数据再按回车键后,该行数据才被送入键盘缓冲区,形成输入流,提取运算符“>>”才能从中提取数据。需要注意保证从流中读取数据能正常进行。     for (int i = 7; i >= 0; i--) {         char ch;         cin >> ch;         if (ch == '1')             value += static_cast<int>(power(2, i));//tatic_cast()强制类型转换     }     cout << "Decimal value is  " << value << endl;     return 0; } double power (double x, int n) {     double val = 1.0;     while (n--)         val *= x;     return val; } 

计算π

π的计算公式如下:

c++:-1

#include <iostream> using namespace std; //求arctan double arctan(double x) {     double sqr = x * x;     double e = x;     double r = 0;     int i = 1;     while (e / i > 1e-15) {         double f = e / i;         r = (i % 4 == 1) ? r + f : r - f;         e = e * sqr;         i += 2;     }     return r; }  int main() {     double a = 16.0 * arctan(1/5.0);     double b = 4.0 * arctan(1/239.0);     //注意:因为整数相除结果取整,如果参数写1/5,1/239,结果就都是0     cout << "PI = " << a - b << endl;     return 0; } 

寻找回文数

寻找并输出11~999之间的数M,它满足(M、M^2和M^3)均为回文数。
回文:各位数字左右对称的整数。
例如:11满足上述条件:112=121,113=1331。

分析:
用除以10取余的方法,从最低位开始,依次取出该数的各位数字。按反序重新构成新的数,比较与原数是否相等,若相等,则原数为回文。

#include <iostream>  using namespace std; //判断n是否为回文数 bool symm(unsigned n) {     unsigned i = n;     unsigned m = 0;     while (i > 0) {         m = m * 10 + i % 10;         i /= 10;     }     return m == n; }  int main() {     for(unsigned m = 11; m < 1000; m++)         if (symm(m) && symm(m * m) && symm(m * m * m)) {             cout << "m = " << m;             cout << "  m * m = " << m * m;             cout << "  m * m * m = "                  << m * m * m << endl;         }     return 0; } 

分段函数

计算分段函数,并输出结果

c++:-1
分析:
计算(sin(x))的公式,精度为(10^{-10})
c++:-1

#include "iostream" #include "cmath"  using namespace std;  const double T=1e-10; //定义计算精度10^{-10}  //计算sin(x) double tsin(double x) {     double g=0;     double t=x;     int n=1;     do{         g+=t;         n++;         t=-t*x*x/(2*n-1)/(2*n-2);     }while(fabs(t)>=T);//fabs:绝对值     return g; }  int main() {     double k,r,s;     cout << "r=";     cin >>r;     cout << "s=";     cin >>s;     if(r*r<=s*s)     {         k= sqrt(tsin(r)*tsin(r)+tsin(s)*tsin(s));     }else         k=tsin(r*s)/2;     cout <<k <<endl;     return 0; } 

掷骰子

每个骰子有六面,点数分别为1、2、3、4、5、6。游戏者在程序开始时输入一个无符号整数,作为产生随机数的种子。每轮投两次骰子,第一轮如果和数为7或11则为胜,游戏结束;和数为2、3或12则为负,游戏结束;和数为其它值则将此值作为自己的点数,继续第二轮、第三轮...直到某轮的和数等于点数则取胜,若在此前出现和数为7则为负。

分析:
(1)rand函数
函数原型:int rand(void);
所需头文件:
功能和返回值:求出并返回一个伪随机数

(2)srand函数
void srand(unsigned int seed);
参数:seed产生随机数的种子
所需头文件:
功能:为使rand()产生一序列伪随机整数而设置起始点。使用1作为seed参数,可以重新初化rand()。

#include "iostream" #include "cmath"  using namespace std; enum GameStatus{Win,Lose,Playing};//枚举存储状态  //掷骰子,计算和数,输出和数 int rollDice() {     int die1=1+rand()%6;     int die2=1+rand()%6;     int sum=die1+die2;     cout << die1<<"+"<<die2<<"="<<sum<<endl;     return sum; }   int main() {     int sum,myPoint;     GameStatus status;     unsigned seed;     int rollDice();     cout <<"请输入种子:";     cin >> seed;     srand(seed);//将种子传给rand()     sum=rollDice();//第一轮掷骰子     switch (sum) {         case 7:         case 11:             status=Win;             break;         case 2:         case 3:         case 12:             status=Lose;             break;         default:             status=Playing;             myPoint=sum;             cout << "点数为"<<myPoint<<endl;             break;     }     //第二轮以后     while (status==Playing)     {         sum=rollDice();         if(sum==myPoint) //某轮和数等于点数取胜         {             status=Win;         }else if(sum ==7)//出现和数为7则负         {             status=Lose;         }     }     //输出     if(status==Win)     {         cout << "Win"<<endl;     } else         cout << "Lose" <<endl;     return 0; } 

疑问:为什么die1和die2是一样的?
种子一样!

    srand(2);     int die1=1+rand()%6;     srand(1);     int die2=1+rand()%6;     cout<<die1<<","<<die2<<endl; 

求组合数

c++:-1

#include <iostream>  using namespace std; int commit(int n,int k) {     if(k>n)         return 0;     else if(n==k || k==0)         return 1;     else         return commit(n-1,k)+ commit(n-1,k-1); }  int main() {     int n,k;     cout << "请输入n和k:";     cin >> n>>k;     cout <<"C("<<n<<","<<k<<")="<< commit(n,k)<<endl;     return 0; } 

汉诺塔问题

有三根针A、B、C。A针上有N个盘子,大的在下,小的在上,要求把这N个盘子从A针移到C针,在移动过程中可以借助B针,每次只允许移动一个盘,且在移动过程中在三根针上都保持大盘在下,小盘在上。

分析:
将n 个盘子从A针移到C针可以分解为三个步骤:

  • 将A 上n-1个盘子移到 B针上(借助C针);
  • 把A针上剩下的一个盘子移到C针上;
  • 将n-1个盘子从B针移到C针上(借助A针)。
#include <iostream> using namespace std;  //将src针的最上面一个盘子移动到dest针上 void move(char src, char dest) {     cout << src << " --> " << dest << endl; }  //将n个盘子从src针移动到dest针,以medium针作为中转 void hanoi(int n, char src, char medium, char dest) {     if (n == 1)         move(src, dest);     else {         //将A 上n-1个盘子移到 B针上(借助C针);         hanoi(n - 1, src, dest, medium);         //把A针上剩下的一个盘子移到C针上;         move(src, dest);         //将n-1个盘子从B针移到C针上(借助A针)         hanoi(n - 1, medium, src, dest);     } } int main() {     int m;     cout << "Enter the number of diskes: ";     cin >> m;     cout << "the steps to moving " << m << " diskes:" << endl;     hanoi(m,'A','B','C');     return 0; } 

值交换

输入两个整数并交换
分析:
参数传递有两种:值传递和引用传递
引用就是别名,定义int &a=i;

值传递,并没有交换
c++:-1

#include<iostream> using namespace std; void swap(int a, int b) {     int t = a;     a = b;     b = t; }  int main() {     int x = 5, y = 10;     cout<<"x = "<<x<<"  y = "<<y<<endl;     swap(x, y);     cout<<"x = "<<x<<"  y = "<<y<<endl;     return 0; }  运行结果: x = 5      y = 10 x = 5      y = 10 

引用传递:
c++:-1

#include<iostream> using namespace std;  //a和b分别是x和y的引用 void swap(int& a, int& b) {     int t = a;     a = b;     b = t; }  int main() {     int x = 5, y = 10;     cout<<"x = "<<x<<"  y = "<<y<<endl;     swap(x, y);     cout<<"x = "<<x<<"  y = "<<y<< endl;     return 0; } x = 5  y = 10 x = 10  y = 5 

内联函数

#include <iostream> using namespace std;  const double PI = 3.14159265358979; inline double calArea(double radius) {     return PI * radius * radius; }  int main() {     double r = 3.0;     double area = calArea(r);     cout << area << endl;     return 0; } 

计算长方体的体积

有三个形参:length(长)、width(宽)、height(高),其中width和height带有默认值2和3。

#include <iostream> #include <iomanip> using namespace std;  int getVolume(int length, int width = 2, int height = 3);//声明在前,定义默认参数值 int main() {     const int X = 10, Y = 12, Z = 15;     cout << "Some box data is " ;     cout << getVolume(X, Y, Z) << endl;     cout << "Some box data is " ;     cout << getVolume(X, Y) << endl;     cout << "Some box data is " ;     cout << getVolume(X) << endl;     return 0; } //定义中不定义默认参数值 int getVolume(int length, int width, int height) {     cout << setw(5) << length << setw(5) << width << setw(5)          << height << 't';//setw用于设置字段的宽度     return length * width * height; } 

重载函数

编写两个名为sumOfSquare的重载函数,分别求两整数的平方和及两实数的平方和。

#include <iostream> using namespace std; //类型不同 int sumOfSquare(int a, int b) {     return a * a + b * b; } double sumOfSquare(double a, double b) {     return a * a + b * b; } int main() {     int m, n;     cout << "Enter two integer: ";     cin >> m >> n;     cout<<"Their sum of square: "<<sumOfSquare(m, n)<<endl;     double x, y;     cout << "Enter two real number: ";     cin >> x >> y;     cout<<"Their sum of square: "<<sumOfSquare(x, y)<<endl;     return 0; } 

求正弦值、余弦值和正切值

从键盘输入一个角度值,求出该角度的正弦值、余弦值和正切值。

#include <iostream> #include <cmath> using namespace std; const double PI = 3.14159265358979;  int main() {     double angle;     cout << "Please enter an angle: ";     cin >> angle;  //输入角度值     double radian = angle * PI / 180; //转为弧度     cout << "sin(" << angle << ") = " << sin(radian) <<endl;     cout << "cos(" << angle << ") = " << cos(radian) <<endl;     cout << "tan(" << angle << ") = " << tan(radian) <<endl;     return 0; } 

习题

(1)已知函数FA调用FB,若要把这两个函数定义在同一个文件中,则

  • FA必须定义在FB之前
  • FB必须定义在FA之前
  • 若FA定义在FB之后,则FA的原型必须出现在FB的定义之前
  • 若FB定义在FA之后,则FB的原型必须出现在FA的定义之前(对)

函数原型,就是函数的声明

(2)在()时为形参分配存储空间。

  • 函数声明
  • 函数定义
  • 函数调用(对)

(3)可以定义指向引用的指针.
错。因为引用不是对象,引用并没有在程序中占据内存空间,故没有地址的说法.
(4)类内实现好的成员函数是内联函数,在类体外实现的函数不能是内联函数
错。因为内联函数主要的作用是在某些情况(某个函数被调用多次)下可以提高程序的运行效率。定义内联函数,可以显式用inline声明,也可以直接在类内定义好实现. 扩展阅读
(5)已知程序中有以下声明:

  • int nonconst_var = 100;
  • const int const_var1 = 2;
  • const int const_var2 = nonconst_var;
    则下述代码中正确的是:
  • constexpr int constexpr_var1 = 3 + const_var1 * 4; (对)
  • constexpr int constexpr_var2 = 3 + nonconst_var * 4;
  • constexpr int constexpr_var3 = 3 + const_var2 * 4;

分析:
constexpr的变量的值必须是编译器在编译的时候就可以确定的。上例中因为nonconst_var的值在语法上来讲,运行期间可能被更改,所以编译期间无法确定,不属于常数表达式。因为const_var2是由非常数表达式来初始化的,所以const_var2也不是常数表达式。但const_var2本身的声明,定义及初始化是合法的。constexpr比const更严格,用来初始化constexpr_var2和constexpr_var3的也都不是常数表达式,所以他们的定义都是错误的。
(6)例3-15中的getVolume函数,如果直接调用int a=getVolume();后,会有什么样的结果?

  • 编译运行正确,a的值为0
  • 编译运行正确,a的值为6
  • 编译报错(对)
  • 运行出错
    分析:
    函数有一个形参没有默认值,所以至少要提供一个实参。注意,默认不是0

(7)判断两个浮点数是否相等

abs(a-b)<1e-10  //abs求绝对值 
发表评论

相关文章