任务1登月纸桥
本关任务:编写一个函数,计算需要把纸折叠多少次(假设纸张足够大,可以无限次折叠),其厚度才能搭建一座登月纸桥,考虑到将来需要到更远的星球,所以函数需要根据具体距离计算纸张折叠的次数并返回。
相关知识
函数是一个命名的程序代码块,是程序完成其操作的一种功能单位。在程序设计中,有许多算法是通用的,例如求一个数的平方根,求一个三角函数等,经常会将这些算法定义为一个函数,这样在程序中需要这些算法的地方就可以直接使用(通过函数调用)它们了。
虽然 C 和 C++ 的库函数已经提供了丰富的功能,但很多时候程序员还是需要根据具体问题的需求定义自己的函数。函数需要先定义再使用(函数调用)。
函数的定义
函数定义的一般格式为:
<返回值类型> <函数名>(<参数列表>)
<函数体>
-
<返回值类型>
、<函数名>
及<参数列表>
构成了函数头。 -
<返回值类型>
说明函数返回值的数据类型,也称为函数的返回类型。
它可以是任一基本数据类型或用户自定义的数据类型;如果无返回值,则用关键字 void 说明。默认的返回类型是 int,即若未指定返回类型,则返回类型是 int 。
-
<函数名>
是程序员为该函数指定的名字,函数名需要遵守标识符命名规定。 -
<参数列表>
指明函数的参数的个数、名称和类型,函数定义中的参数称为形式参数,简称形参。
当有多个形式参数时,用逗号分隔。如果函数没有形参,参数列表为空,函数名后面的圆括号不能少,这时也可在括号中加上关键字 void,表示这是一个无参函数。
<函数体>
描述函数的功能,即函数所完成的具体操作,它由一系列说明语句和执行语句组成。
函数体实际上是一个复合语句,花括号不能少,它指明函数体的开始和结束。函数执行时,如同执行一个复合语句一样,顺序执行函数体,直到遇到 return 语句或者遇到表示函数体结束的那个右花括号为止,函数执行完毕后,返回调用程序继续执行。
例如,下面的程序定义了一个名为 max 的函数,该函数有3个形式参数 a、 b 和 c,类型均为 int,返回值也是 int 类型。函数体由一个变量说明语句、一个赋值语句和一个返回语句构成。函数的功能是求 a 、b 和 c 中最大的值:
int max(int a, int b, int c)
{
int m;
m = (a > b) ? a : b;
return (m > c) ? m : c;
}
注意:
-
所有函数的定义是并列的、平行的,在一个函数定义内部不允许定义另外一个函数。但可以对别的函数进行调用或作引用说明;
-
函数定义中声明的所有变量都是局部变量,只在声明语句所处的程序块中有效。大多数函数都有一组参数,函数定义时指明了每个形式参数的名字、类型,函数调用时提供的实在参数要与形式参数一一对应(函数调用时需要进行参数传递,将实参的值一个个传给对应的形参)。函数的形式参数也可视为局部变量,在函数体范围内有效;
-
函数可以有返回值,也可以没有返回值;
-
函数没有返回值时,返回类型必须用关键字 void 说明。这类函数的函数体内没有 return 语句或 return 语句中无表达式。当函数执行到 return 语句时,程序返回到调用该函数的地方;如果函数体没有 return 语句,当执行完函数体最后的语句后,程序再返回到调用该函数的地方;
-
函数有返回值时,必须指明返回值类型(不能是 void ),此时函数体必须包含带表达式的 return 语句。表达式的值将返回给调用程序,该值的数据类型必须与返回值类型一致;
-
函数体可有多个 return 语句。调用函数执行时,只要遇到一个 return 语句,就马上忽略剩余代码,立刻返回到调用程序;
-
函数需要先定义后使用,但函数的相互调用关系很难保证所有函数都是先定义后使用的。这时就需要先声明函数原型了。
声明函数
为了让 main 程序(或者其他准备调用函数的程序)知道函数的相关信息,需要在调用程序的前面,以“函数原型”的方式对被调用的函数进行声明。
在 C 和 C++ 中,函数原型的一般形式为:
<返回值类型> <函数名>(<参数列表>);
函数原型形式上只是比函数头多了一个分号,函数原型参数列表中形参的名称可以省略。函数原型告诉编译器后面将会定义这么一个函数,其函数名、返回值类型、参数列表分别是什么,便于编译器检查函数调用的正确性。
例如,前面函数 max 的函数原型可以为:
int max(int , int , int);
建议:如果一个文件中定义多个函数时,把主函数之外的其它函数的函数原型都放在文件中所有函数定义的前面,这样所有函数调用就都符合“先声明,后使用”的原则了。
编程要求
在右侧编辑器中的Begin-End
之间补充代码,编写一个函数,给定一个距离和纸张的厚度(数据由平台提供,你需要获取后使用),计算需要把这张纸折叠多少次,其厚度才能搭建一座登月纸桥。考虑到将来需要到更远的星球,函数需要根据具体距离计算纸张折叠的次数并返回。
已知:月球离地球最近距离(近地点)为363300千米,最远距离(远地点)为405500千米,一张纸的厚度一般为0.088到0.114毫米。
测试说明
平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:
测试输入:363300 0.088
预期输出:需要折叠42次
测试输入:405500 0.088
预期输出:需要折叠43次
#include <iostream>
using namespace std;
// foldTimes-计算建纸桥的折叠次数
// 参数:dis-星际距离(千米),thick-纸的厚度(毫米)
// 返回值:建桥需要折叠的次数
int foldTimes(double dis, double thick);
int main()
{
double dis, thick;
cin >> dis >> thick;
cout << "需要折叠" << foldTimes(dis,thick) << "次" << endl;
return 0;
}
int foldTimes(double dis, double thick)
{
// 请在这里补充代码,实现函数foldTimes
/********** Begin *********/
int sum=0;
dis=1000000*dis;
while(thick<dis)
{
thick*=2;
sum++;
}
return sum;
/********** End **********/
}
任务2几点几分了
本关任务:编写一个函数 whatTime,该函数通过秒表显示的秒数,计算当前的时间(几点几分几秒),计算得到的时间通过参数返回。
相关知识
本关内容涉及传引用,是 C++ 对 C 语言的扩充部分, C 语言不包括这部分内容。
由于 C++ 的函数只能返回一个值,而本题要求返回当前时间(几点几分几秒),需要返回三个值,所以没法通过 return 语句一次返回(如果学了结构也可以把三个数据一起打包返回),但 C++ 函数参数提供了另一种返回值的方式:引用参数。
函数调用时,需要进行参数传递,即把实参的值逐个传给对应的形参。对 C++ 而言参数的传递方式有两种:传值和传引用。
参数传值
传值是指值的复制,即把实参的值传递给形参,实参和形参是不同的变量,有各自独立的存储空间,因此函数被调用执行时,只能访问形式参数对应的内存单元,不能访问或修改实在参数的值。
下面的 swap 函数试图交换两参数的值。
#include <iostream>
using namespace std;
// 交换 x 和 y 的值
void swap(int x,int y)
{
int temp = x;
x = y;
y = temp;
}
int main()
{
int a = 10, b = 20;
// 调用函数,试图交换
swap(a, b);
cout << "a:" << a << " b:" << b << endl;
return 0;
}
程序的运行结果显示 a 和 b 的值并没被交换。
这是因为 main 函数在执行swap(a,b)
时,是把实在参数 a 和 b 的值传给了 swap 中形式参数 x 和 y,swap 函数中交换的也只是形参 x 和 y 这两个局部变量的值,跟 a 、b 两个变量无关,当 swap 函数执行完后,就返回到 main 函数的调用语句处,继续向下执行,输出 a 、b 的值,因此依然是原来的10和20。
参数传引用
传引用是指调用函数时,把对实参变量的引用传给形式参数,即将实参变量的地址存放到对应的形参的形式单元中。
当程序转入到被调用函数后,在执行函数体过程中,对形式参数的任何引用或赋值都被处理成对相应形式单元的间接访问,即根据形式单元中存放的实参变量的地址找到实参单元,对形式参数的引用或赋值被处理成对该实参单元的访问。
也就是说,对形参的访问实际上就是对实参的访问,可以通过修改实参的方式把数据传出被调用函数。
提示:传引用要求实在参数必须是变量。
修改上面的 swap 函数,实现真正的交换。
#include <iostream>
using namespace std;
// 交换x和y的值
void swap(int &x, int &y)
{
int temp = x;
x = y;
y = temp;
}
int main()
{
int a = 10, b = 20;
// 调用函数,试图交换
swap(a, b);
cout << "a:" << a << " b:" << b << endl;
return 0;
}
输出结果为:a:20 b:10
程序中函数 swap 的参数定义为int &x,int &y
,其中 x 、y 前面的 & 为引用符号,表示 x 、y 是引用变量,调用该函数时,对应参数的传递采用传引用方式。这时函数 swap 中修改 x 和 y 的值,实际上就是在修改实参 a 和 b 的值。
编程要求
在右侧编辑器中的Begin-End
之间补充代码,编写函数 whatTime,该函数通过秒表显示的秒数(该数据由平台提供,你需要获取后使用),计算当前的时间(几点几分几秒),计算得到的时间通过参数返回。
测试说明
平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:
测试输入:23456
预期输出:6:30:56
测试输入:34567
预期输出:9:36:7
#include <iostream>
using namespace std;
void whatTime(int secs, int &h, int &m, int &s)
{
// 请在这里补充代码,设计并实现函数whatTime,使main函数中的函数调用正确
/********** Begin *********/
h=secs/3600;
m=(secs/60)-(h*60);
s=secs%60;
/********** End **********/
}
int main()
{
int secs; // secs秒表上的秒数
int h, m, s; // 当前时间:h-小时,m-分,s-秒
cin >> secs; // 输入秒表上的秒数
whatTime(secs,h,m,s); // 计算当前时间
cout << h << ":" << m << ":" << s << endl; // 输出当前时间
return 0;
}
任务3这天星期几?
本关任务:编写函数 whatDay,计算某年某月的1号是星期几并返回。
相关知识
要知道某一天是星期几,可以用已知的某一天进行推导。
例如已知公元1年1月1日是星期一,公元2年1月1日则是在星期一基础上加上一整年的天数(要考虑闰年,闰年一年366天,非闰年365天),如果不是1月,例如7月,则要加上1到6月的所有天数,这里也要考虑闰年,因为闰年的二月是29天,非闰年是28天。
这样就可以算出从公元1年1月1日到该年月过了多少天,而过了7天星期不变,所以可以用这种方法推导出公元元年之后的任何一天是星期几。
下面的程序可以计算星期一过了 n 天后是星期几:
w = 1; // 从星期一开始
w = w + n; // n天后
w = w % 7; // 得到0-6,其中0为星期天
// 调整星期天
if(w == 0) w = 7;
编程要求
在右侧编辑器中的Begin-End
之间补充代码,计算输入数据年月份的1号是星期几并返回(周一到周日分别返回整数1到7)。
其中该函数的参数为输入的两个整数数据:年和月。
提示:由于判断是否闰年有多个地方需要用到,故已把该功能单独实现为一个函数。
测试说明
平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:
测试输入:2016 1
预期输出:2016年1月1日是星期5
测试输入:2017 7
预期输出:2017年7月1日是星期6
#include <iostream>
using namespace std;
// 函数leapYear
int leapYear(int y)
{
if(y % 4 == 0 && y % 100 != 0 || y % 400 == 0)
return 1;
return 0;
}
// 函数whatDay:计算某年某月某日是星期几
// 参数:year-年,month-月
// 返回值:--7分别表示星期一到星期日
int whatDay(int year, int month)
{
// 请在这里补充代码,实现函数whatDay
/********** Begin *********/
int a,sum=0;
int b[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
//计算从公元1月1日到现在有多少天
for(int j=1;j<year;j++)
{
for(int i=1;i<=12;i++){sum=sum+b[i];}
if(leapYear(j)==1){sum=sum+1;} //闰年+1
}
for(int i=0;i<month;i++){sum=sum+b[i];}
if(month>2&&leapYear(year)==1){sum=sum+1;}
a=(sum+1)%7;
return a;
/********** End **********/
}
int main()
{
int y, m, xq; // 年、月、星期几
cin >> y >> m; // 输入年月
xq = whatDay(y,m); // 计算星期几
cout << y << "年" << m << "月1日是星期"; // 输出星期
if(xq == 7)
cout << "日" << endl;
else
cout << xq << endl;
return 0;
}
任务4打印日历
本关任务:根据输入的年份和月份来输出该年月的日历。
相关知识
日历的格式如下图所示:
上图中每个汉字(一、二...日)占四个字节,右对齐,由于汉字显示本身就占2个字节,所以只需要在汉字前面多输出两个空格就好了。每个日期数字占4个字节,也是右对齐,这样能使输出的日历上下对齐。
剩下的事情就是循环输出这个月的所有日期了,这个月有多少天可以专门用一个函数实现,注意闰年二月(判闰年的函数这里又可以用一次了)是29天。
注意:在 1 号前应该留多少空位(如果 1 号是星期 n ,则留 n-1 个空位,每个空位是一个日期的宽度),注意什么时候换行(日期加 1 号前空位数量是 7 的倍数则换行)。
编程要求
在右侧编辑器中的Begin-End
之间补充代码,根据输入的年月(函数printMonth 的两个参数)打印该年该月的日历。日历输出格式要求如下:
-
每个汉字(一、二、...、日)占四个字节,右对齐,由于汉字显示本身就占2个字节,所以只需要在汉字前面多输出两个空格就好了;
-
每个日期数字占4个字节,也是右对齐,这样能使输出的日历上下对齐。
测试说明
平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:
测试输入:2017
6
预期输出:
一 二 三 四 五 六 日
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
测试输入:2000
2
预期输出:
一 二 三 四 五 六 日
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29
// 包含两种I/O库,可以使用任一种输入输出方式
#include <stdio.h>
#include <iostream>
using namespace std;
// 函数printMonth:按要求的格式打印某年某月的日历
// 参数:year-年,month-月
// 返回值:无
void printMonth(int year, int month);
// leapYear:判断闰年
// 参数:y-年
// 返回值:1-是闰年,0-不是闰年
int leapYear(int y)
{
if(y % 4 == 0 && y % 100 != 0 || y % 400 == 0)
return 1;
return 0;
}
// 函数whatDay:计算某年某月的1号是星期几
// 参数:year-年,month-月
// 返回值:1到7--星期1到星期日
int whatDay(int year, int month)
{
// 1年月日是星期一
int w = 1;
int i;
// 1到year-1都是全年
for(i = 1; i < year; i++)
{
if(leapYear(i))
w += 366;
else
w += 365;
}
switch(month)
{
case 12: // 加月的
w += 30;
case 11: // 加月的
w += 31;
case 10: // 加月的
w += 30;
case 9: // 加月的
w += 31;
case 8: // 加月的
w += 31;
case 7: // 加月的
w += 30;
case 6: // 加月的
w += 31;
case 5: // 加月的
w += 30;
case 4: // 加月的
w += 31;
case 3: // 加月的
if(leapYear(year))
w += 29;
else
w += 28;
case 2: // 加月的天
w += 31;
case 1: // 1月不加了
;
}
// 得到-6,其中为星期天
w = w % 7;
// 调整星期天
if(w == 0)
w = 7;
return w;
}
// 请在下面补充代码,实现函数printMonth
/*************** Begin **************/
void printMonth(int year, int month)
{
int d = whatDay(year, month);
int n = 0;
cout << " " << "一 " << "二 " << "三 "
<< "四 " << "五 " << "六 " << "日" << endl;
for (int i = 0; i < d - 1; i++)
{
cout << " ";
}
switch (month)
{
case 1:
n = 31;
break;
case 2:
if (leapYear(year))
n = 29;
else
n = 28;
break;
case 3:
n = 31;
break;
case 4:
n = 30;
break;
case 5:
n = 31;
break;
case 6:
n = 30;
break;
case 7:
n = 31;
break;
case 8:
n = 31;
break;
case 9:
n = 30;
break;
case 10:
n = 31;
break;
case 11:
n = 30;
break;
case 12:
n = 31;
break;
}
for (int i = 1; i < n + 1; i++)
{
if (i < 10)
cout << " " << i;
else
cout << " " << i;
switch (d)
{
case 1:
if ((i % 7) == 0)
cout << endl;
break;
case 2:
if ((i % 7) == 6)
cout << endl;
break;
case 3:
if ((i % 7) == 5)
cout << endl;
break;
case 4:
if ((i % 7) == 4)
cout << endl;
break;
case 5:
if ((i % 7) == 3)
cout << endl;
break;
case 6:
if ((i % 7) == 2)
cout << endl;
break;
case 7:
if ((i % 7) == 1)
cout << endl;
break;
}
}
cout << endl;
}
/*************** End **************/
int main()
{
// 年、月
int y, m;
// 输入年月
cin >> y >> m;
// 输出该年月的日历
printMonth(y,m);
return 0;
}