1.1 简单的C++程序
// PRogram 1_1.cpp
#include<iostream.h>
void main()
{
cout<<"Let's learn to write a C++ Program.";
cin.get();
}
此程序由6行组成,
1)第1行为注释,程序的每行如出现符号" // ",则其右的所有符号为注释。注释是帮助阅读程序的说明,与该程序运行没有关系。在程序被编译时,注释被当作空格处理。此行指出本程序以文件"program 1_1.cpp"存储。
2)第二行#include是一条编译预处理指令,它告诉编译系统在编译本程序时把系统提供的头文件iostream.h的内容插入到第二行的位置,它在程序中的作用与第5行的输出语句有关。
3)第3~6行是程序的主体,由一个主函数组成。其中main是主函数名,第一个void指出该函数无返回值。括号()表示函数,括号内为函数的参数表,但此函数无参数,故用void表示,它与空括号的效果相同。
第4~6行称为函数体,用{ }括起来。函数体内可以包含任意多行语句。
第5行是本程序中唯一要执行的任务:向屏幕输出(显示)一字符串。
cout是一个标准输出文件名,这里表示屏幕。符号“<<”是运算符,它指示计算机把其右端用双引括起来的字符串输送到cout文件即屏幕。由于cout和<<的说明都在系统提供的头文件iostream.h中,因此,凡是程序中需要使用cout、<<等标准输入输出功能时,第二行包含的指令#include必须列出。
4) cin.get()表示按任意键继续。
5)此程序的执行结果为在屏幕上显示:
Let's learn to write a C++ Program.
---------------------------------------
1.1.2 求两个数的和
程序如下:
// program 1_2.cpp
#include<iostream.h>
void main()
{
int a,b,sum; // 定义了三个整型变量
a=43; // 把整数常量赋给变量a
b=37;
sum=a+b; // a与b相加的和赋给变量sum
cout<<"The sum is"<<sum;
cout<<endl; // endl 是回车换行的作用
}
程序运行结果如下:
This sum is 80
----------------------------
1.1.3 计算圆面积
程序代码如下:
// program 1_3.cpp
#include<iostream.h>
void main()
{
const float pai=3.14; // 用const 说明了一个float型常量pai,m由于pai是常量,所以在
// 程序中这个值不会被改变
float radius; // 说明了一个float型变量radius,用于存放半径值
cout<<"Enter radius: ";
cin>>radius; // cin 为iosream.h中说明的文件,作用为接收键盘输入。如此
// 时从键盘输入一个数2.5,就把输入的这个值送入变量radius.
float area=pai*radius*radius; // 说明了一个float型常量area,用于存放计算后的面积
cout<<"\n The area of circle is : ";
cout<<area<<endl; // 输出面积
}
程序运行结果如下:
Enter radius: 2.5
The area of circle is 19.625
说明:这个程序相对于progam 1_2有一个明显的优点,就是它的输入数据不必在编程时确定,而是在运行时由操作者直接从键盘输入。
--------------------------------------------------------------------------------------
上面的程序称为简单程序。直观上看,它们都很短,进一步分析发现它们有两个特点:
1)程序要做的事情是由程序员在编程时就安排好了的,是固定不变的。
2)程序要做几个动作也是由程序员在编程时安排好的,程序实际上是一个简单的命令序列。
下面的程序有了“判定”的能力。此程序用于温度值(攝氏与华氏温度)的换算,输入温度值并指出该值是攝氏(C)还是华氏(F)温度,然后程序根据不同的输入(攝氏或华氏)进行不同的换算。
2.1 按条件进行判定
// program 2_1.cpp
#include<iostream.h>
void main()
{
float t,tc,tf;
char corf;
const float fac=1.8,inc=32;
cout<<"Enter temperature: ";
cin>>t; // 输入温度值
cin>>corf; // 指出是攝氏(C)或华氏(F)
if(corf=='c'corf=='C')
{
tc=t;
tf=t*fac+inc;
}
else
if(corf=='F'corf=='f')
{
tf=t;
tc=(t-inc)/fac;
}
else tc=tf=0.0;
cout<<endl<<"The temperature is: ";
cout<<tc<<"C="<<tf<<"F\n";
}
程序运行结果如下:
Enter temperature: 40.2C
The temperature is 40.2C=104.36F
-----------------------------------
下面是使用switch(开关)语句的程序。switch提供了多分支选择的方法。
5.2 简单的计算器程序
这个程序完成的功能是:输入两个数,然后输入运算符,在屏幕上显示运算结果。
// program 2_2.cpp
程序运行结果如下:
#include<iostream.h>
void main()
{
int x,y;
char op;
cout<<"first integer: ";
cin>>x;
cout<<endl<<"Second integer: ";
cin>>y;
cout<<endl<<"Operator(+,-,*,/,%): ";
cin>>op;
switch(op) // switch 开关分支语句从这里开始,根据表达式的值分支
{
case '+': // 假如 op=='+'
cout<<endl<<x<<"+"<<y<<"="<<x+y;
break; // 执行完毕,退出switch分支。下面的break语句的作用相同
case '-': // 假如op=='-'
cout<<endl<<x<<"-"<<y<<"="<<x-y;
break;
case '*': // 假如op=='*'
cout<<endl<<x<<"*"<<y<<"="<<x*y;
break;
case '/': // 假如op=='/'
cout<<endl<<x<<"/"<<y<<"="<<x/y;
break;
case '%': // 假如op=='%'
cout<<endl<<x<<"%"<<y<<"="<<x%y;
break;
default:
cout<<endl<<"Wrong!";
}
}
程序运行结果如下:
first integer: 12
second integer: 7
operator(+,-,*,/,%): *
12*7 = 84
---------------------------------------
上面的这个程序可以由用户任意输入两个整数及运算符,完成指定的运算,但是它的不便之处是当有多次运算要进行时,每算完一次必须重新启动程序。下面的程序使用循环控制语句,使得程序可以进行多次运算。
// program 2_5.cpp
#include<iostream.h>
void main()
{
int x,y,quit=1;
char op,cont;
while(quit) // whiel 循环语句,括号里面是一个表达式,称为控制表达式。当这个表
{ // 达式的值为1时继续循环体内的代码。从这个大括号开始,进入循环体。
cin>>x;
cin>>y;
cin>>op;
switch(op)
{
case '+':
cout<<endl<<x<<"+"<<y<<"="<<x+y;
break;
case '-':
cout<<endl<<x<<"-"<<y<<"="<<x-y;
break;
case '*':
cout<<endl<<x<<"*"<<y<<"="<<x*y;
break;
case '/':
if(y==0) cout<<endl<<"Overflow!"; // 判定y是否为0,避免除0错误。
else
cout<<endl<<x<<"/"<<y<<"="<<x/y;
break;
case '%':
if(y==0) cout<<endl<<"Overflow!"; // 判定y是否为0,避免除0错误。
else cout<<endl<<x<<"%"<<y<<"="<<x%y;
break;
default:
cout<<endl<<"Wrong!";
break;
}
cout<<endl<<"Do you want to continue?(y or n)";
cin>>cont;
if(cont=='n') quit=0; // 当操作者输入字符'n'时,由于quit被赋值0。
// 当再次检测while表达式时,由于quit为0,所以退出while循环。
}
}
这个程序所做出的改进是很明显的,只要在每次完成一次计算后,用户回答是否继续时按下'y'键(或除'n'之外的任何字符),就 可以连续运行,完成用户指定的四则运算,直到接收到字母'n'为止。在循环体内必须有改变while表达式值的语句,否则一旦进入死循环就无法跳出,这种情形称为“死循环”,编程中应注重避免。
2.3 计算e的值和求素数
e是自然对数的底,它和π一样是数学中最常用的无理数常量。其近似值的计算公式为:
e=1+1/1!+1/2!+1/3!+...+1/(n-1)!+r
当n充分大时,这个公式可以计算任意精度e的近似值。为了保证误差r<ε,只需1/(n-1)! ( > r) <ε
程序代码如下:
// program 2_4.cpp
#include<iostream.h>
void main()
{
const double eps=0.1e-10;
int n=1;
float e=1.0,r=1.0;
do // 开始do循环。循环条件由后面的while中的表达式值确定。
{
e+=r;
n++;
r/=n;
}
while(r>eps);
cout<<"The approximate Value of natural logarithm base is: ";
cout<<e<<endl;
}
程序运行结果如下:
The approximate Value of natural logarithm base is: 2.71828
说明:
上面这个程序中使用了do循环,它是循环控制中的一种。循环控制条件放在后面的while表达式中。
-----------------------------------------
下面的程序用来求出1000以内的全部素数。
// program 2_5.cpp
#include<iostream.h>
void main()
{
const int m=1000;
int i,j,isprime;
for(i=2;i<=m;i++) // 这里用到了for循环控制语句。for循环语句有三个表达式
{ // 第一个表达式赋给变量初值,第二个表达式为循环控制条件,
// 第三个表达式为循环变量改变其值
isprime=1;
for(j=i-1;j>1;j--)
if(i%j==0)isprime=0;
if(isprime)cout<<i<<',';
if(i%30==0)cout<<endl;
}
}
说明:
1)当m值比较大时,这个程序计算量是比较大的,事实上这是用来求出小于m的所有素数的最简单也是最笨的算法。
一种办法是作下面的改进:
把 for(j=i-1;j>1;j--)改为:
int i1=int(sqrt(i));
for(j=i1;j>1;j--)
为了确定整数i是不是素数,不需要用2,3,。。。,i-1来除它,只需用2,3,。。。,sqrt(i)试除就可以了。
这里sqrt(i)是标准函数,功能是计算平方根,而int()则是把浮点值围 化为整型值。
另一种算法是埃拉脱散(Eratosthenes)筛法,将在以后介绍。
--------------------------------------------------------------------------------------
3.1 统计学生成绩
已知n个学生的注册号和成绩,计算他们的平均成绩,并列出成绩最好的前t名学生的注册号和分数。
程序代码如下:
// program 3_1.cpp
#include<iostream.h>
void main()
{
const int n=3;
const int t=n;
int index[n]; // 存放注册号
float score[n]; // 存放成绩
for(int i=0;i<n;i++)
cin>>index[i]>>score[i]; // 从键盘输入数据
float sum=0;
for(i=0;i<n;i++)
sum+=score[i]; // 计算分数总和
cout.precision(2); // 设置输出精度
cout<<endl<<"Average score:"<<sum/n;
cout.width(28); // 输出精度
cout<<endl<<" register number score";
for(i=0;i<t;i++) // 选取前t名分数最高的学生,输出其注册号和成绩
{
float s=score[i];
int j1=i;
for(int j=i+1;j<n;j++)
if(s<score[j])
{
s=score[j];
j1=j;
}
if(j1>i)
{
score[j1]=score[i];
score[i]=s;
j=index[j1];
index[j1]=index[i];
index[i]=j;
}
cout.width(4); // 输出序号,注册号和分数
cout<<endl<<i+1;
cout.width(11); // 设置宽度
cout<<index[i];
cout.width(16);
cout.precision(2); // 设置输出精度
cout<<score[i];
}
cout<<endl;
}
程序运行结果如下:
说明 :
cout.precision(2)是一个函数调用语句,函数名是precision。cout说明此函数是一个标准类的一个标准对象cout的函数。所“标准”,即它们所涉及的流(stream)类和对象cout都是由系统已经义好了的。其说明可以在头文件iostream.h中找到。函数precision要求一个int型参数,该参数指出在后面输出的浮点数的精度取到小数点后的多少位。
cout.width()是另一个函数的用法。width决定了其后数据输出的宽度。
-----------------------------------------------------------------------------------
3.2 输出三角函数表
输出人0~90度之间每隔15度的正弦、余弦、正切函数值。
程序代码如下:
// program 3_2.cpp
#include<iostream.h>
#include<math.h>
void main()
{
const float pai=3.1416f;
const int interval=15;
cout.width(10);
cout<<"Angle x";
cout.width(10);
cout<<"sin(x)";
cout.width(10);
cout<<"cos(x)";
cout.width(10);
cout<<"tan(x)";
float arc;
cout.precision(4);
for(int doa=0;doa<=90;doa+=interval)
{
arc=pai*doa/180;
cout<<endl;
cout.width(10);
cout<<doa;
cout.width(10);
cout<<sin(arc);
cout.width(10);
cout<<cos(arc);
cout.width(10);
if(doa==90) cout<<"-";
else cout<<tan(arc);
}
cout<<endl;
}
程序运行结果如下:
说明:
1)本程序中使用了系统提供的标准库函数sin(),cos(),tan(),它们都在头文件math.h中说明。因此在3行包含了此文件。
2)由于当doa=90时,其正切函数趋于无穷大,故对它有非凡的处理,否则如按正弦、余弦一样输出,当doa变到90时,运行将可能溢出或打印一个超界的大数。
--------------------------------------------------------------------
3.3 Eratosthenes 筛法求素数
在2_3节给出了一个求在一个范围内的全部素数的程序。下面给出的程序其算法比原来的好。
program3_5是对2~1000的所有素数进行检测,用小于该数的数试验,假如都不能除尽,就找到了一个素数。本节的程序也是用来求1000以内的所有素数,但其思想简单而巧妙。它是把2~1000所有的整数放到一起,首先把2保留,把2的所有素数从中去掉,再保留3,同时删去所有3的倍数,下一个数是5,7...。好象是一把筛子,把不需要的数逐步筛去,留下的正是所求的数。
程序代码如下:
// program 3_4.cpp
#include<iostream.h>
#include<iomanip.h>
void main()
{
const int n=1000;
int i=1,j,count=0,sieve[n+1];
for(j=1;j<n;j++)sieve[j]=j; // 把1~n存入sieve[n]
while(i<n+1) // 从while循环开始完成全部素数的产生和输出。
{
while(sieve[i]==1) i++; // 跳过值为1的分量,表示下一个分量必为一素数
cout<<setw(4)<<sieve[i]; // setw(4)设置以宽度为4的格式输出数据。setw(4)
// 等价于cout.width(4)
count++;
if(count%15==0)cout<<endl;
for(j=i;j<n+1;j+=i)
sieve[j]=1;
}
}
程序运行结果如下:
在本节以前给出的程序都是由一个函数组成的,实际上,一个真正的C++程序几乎都包含若干个由用户自定义的函数。
在下面的几个程序实例中,都调用了由用户定义的函数。
4.1 三次方程求根
按照Cardan公式,计算三次方程x3+px+q=0的一个实根的公式为:
在计算实根xr的程序中,把计算一个浮点数的立方根的程序作为一个用户定义的函数,而在主程序中两次调用这个函数。
程序代码如下:
// program 4_1
#include<iostream.h>
#include<math.h>
float cuberoot(float); // 自定义函数的原型
void main()
{
float p,q,xr;
cout<<"Input paramerters p,q:";
cin>>p>>q;
float a=sqrt(q/2*q/2+p/3*p/3*p/3);
xr=cuberoot(-q/2+a)+cuberoot(-q/2-a); // 调用cuberoot函数
cout<<endl<<"The real root of the equation is "<<xr;
}
float cuberoot(float x) // 自定义函数代码从这里开始
{
float root,croot;
const float eps=1e-6;
croot=x;
do
{
root=croot;
croot=(2*root+x/(root*root))/3;
}
while(abs(croot-root)>eps);
return croot;
}
------------------------------------------------------------------------------------
4.2 四元排序程序
对于任意的四个整数,经过处理,令其按从大到小的顺序排序。
// program 4_2.cpp
#include<iostream.h>
void swap(int &,int &); // 自定义函数原型
void main()
{
int a,b,c,d;
cout<<"Input 4 integers: ";
cin>>a>>b>>c>>d;
if(a<b)swap(a,b); // 调用自定义函数 swap()
if(b<c)swap(b,c);
if(c<d) swap(c,d);
if(a<b) swap(a,b);
if(b<c) swap(b,c);
if(a<b) swap(a,b);
cout<<endl<<a<<" "<<b<<" "<<c<<" "<<d;
}
void swap(int &x,int &y) // 自定义函数代码从这里开始
{
int temp;
temp=x;
x=y;
y=temp;
}
程序运行结果如下:
Input 4 integers: 17 25 8 41
41 25 17 8
说明:
这是一个简单的程序。函数swap()是void类型,这表明它没有返回值,所有用户定义函数都必须指明返回类型,无返回值的函数必须说明为void类型。
函数swpa()有两个整型参数,在参数表中的符号'&'表明参数x和y是“引用参数”。这与上节中的float x不同,后者称为赋值参数。引用参数和赋值参数在使用中主要有下面几点区别:
1)在形参表中增加符号'&'
2)在调用该函数时,引用参数对应的实参必须是指定类型的变量。如调用swap(a,b)中的实参a,b都是整型变量。而在上节中的赋值参数却是一个表达式,表达式的念是包含了常量、变量和一般的表达式。比如在本例中,写成swap(a+b,a-c)或swap(41,a*a+d*d)等都是错误的。
引用参数和引用调用是C++语言的一种重要功能,假如swap()的两个参数说明为赋值形参,会出现什么情况?
本例中的排序并不是一个好的方法,实际上,关于排序有很多好的算法可用。
注重:在编程中函数的作用是太重要了,不过,函数有利于编程、用利于程序结构,却不能提高程序的效率。相反,在函数调用时由于要保留“现场”以备在函数计算完成返回时继续运行原来的程序。因此,函数的每次调用,系统都要额外在时间和空间上付出一些代价。
为了解决这个问题,C++语言设置了内联函数“inline",其具体规定是:
1)在函数定义和说明之前增加要害字inline:
inline void swap(int &x,int &y){......}
2)调用内联函数与调用非内联函数的调用效果完全相同,不同的是,在对源程序进行编译时,对于非内联函数,其程序代码只有一份,在每个调用语句处,通过转移控制语句来运行,然后返回。
对于内联函数处理则不同,它是把函数体的程序代码放到每个调用该函数的语句处。例如可以把swap()函数说明为内联的:
inline void swap(int &x,int &y)
{
int temp;
temp=x;
x=y;
y=temp;
}
经过这一改动,主函数仍不变,通过编译生成的目标程序将使swap()的函数体程序代码在其中出现6次。
由此,我们可以看出,采用内联函数一方面在源程序的编写中仍可因使用函数而使程序简短清楚,另一方面在执行中又不必付出调用过程的时间和空间代价,唯一的代价是目标程序加长了。
一般说,调用次数不多的函数或函数体规模较大的函数,其调用代价相对可以忽略,不宜采内联函数。而函数体积短小且又频繁被调用的函数可以说明为内联函数。
例如对于更多个整数的排序算法,假如需要调用swap()的话,那么swap()函数应该说明为内联的。
-----------------------------------------------------------------------------------------
4.3 ”三色冰激淋“程序
这是一个由冰激淋商提出来的问题,有28种颜色的原料,可以组合成多少种三色冰激淋。一种答案是有 19656=28*27*26种,称为(28,3)的排列数A 3 28 ,它是把同样三种颜色的不同排列数也计算进来了,另一答案是3276=19656/3!种,称为(28,4)的组合数。其中28为元素数,3为选择数。下面的程序对输入的元素数和选择数计算相对应的排列数和组合数。
// program 4_3.cpp
#include<iostream.h>
long factorial(int number); // 函数原型,功能计算C(m,n)=A(m,n)/n!
void main()
{
int i,selections,elements;
cout<<"Number of selections: ";
cin>>selections;
cout<<"Out of how many elements: ";
cin>>elements;
double answer=elements;
for(i=1;i<selections;i++) // 计算排列数A(m,n)=m*(m-1)*...*(m-n+1)
answer*=--elements;
cout<<"A("<<elements+selections-1<<","<<selections<<")=";
cout<<answer<<endl;
answer/=factorial(selections); // 调用factorial函数,以完成计算C(m,n)=A(m,n)/n!
cout<<"C("<<elements+selections-1<<","<<selections<<")=";
cout<<answer<<endl;
}
long factorial(int number) // 计算C(m,n)=A(m,n)/n!
{
long value=1;
while(number>1)value*=number--;
return value;
}
程序运行结果如下:
说明:这个程序中很多地方使用了复合运算符,如*=、--、++等,它们的使用有利于使程序简短、书写方便,不过应准确把握其应用方法。
*=是赋值符与=与乘法运算符*复合而成,它是一种双止运算符,a*=b等价于a=a*b,也可以把它看作为a=a*b的简化写法。
--和++称为减量和增量运算符,属于单目运算符。a--等价于a=a-1,--a也等价于a=a-1,a--与--a的区别只有当与其它运算共同组成表达式时才能显现出来。例如answer*=--elements是一个”运算“与”乘赋值“组合成的表达式,其运算顺序是:
首先完成--elements,即elements=elements-1
然后完成answer*=elements,即answer=answer*elements
设执行之前answer=20,elements=10,则执行answer*=--elements之后,首先令elements=9,然后answer=20*9=180.
再来看一下value*=number--,运算顺序为:
首先完成 value*=number即value=value*number,
然后完成 number--.
设执行前value=20,number=10,则执行value*=number--后,首先value=20*10=200,然后number=10-1=9.
由此可以看出--a与a--;++a与a++之间的区别了。
factorial()函数也可以采用递归函数的方式设计,即在函数体内又(直接或间接地)调用该函数本身,函数定义如下:
long factorial(int number)
{
if(number<=1)return 1;
else return number*factorial(number-1);
}
这个程序与n!的数学定义式
1 n<=1
n! =
n+(n-1)! n>1
相吻合。
递归程序往往使程序思路清楚,易编易读。