c++学习笔记(3)
函数:
需要理解的东西: 函数调用,在每一个函数被调用时,系统都将会创建一个活动记录(也称为活动结构)来存储其参数和变量并且将活动记录存放到一个叫做调用栈的内存区域,调用栈也称为执行栈,运行栈,机器栈,简称为栈。当一个函数调用另一个函数时,调用者的活动的活动记录被完好无损的保留,系统会创建新的活动记录来处理新的函数调用,当一个函数完成其工作并返回调用者时,它关联的活动记录将从调用栈中释放。
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
const int number_of_prime = 50;
const int number_of_prime_per_line = 10;
void printGrade(double grade)
{
if(grade>=90)
{
cout << "Level is A" << endl;
}
else if(grade>=80)
{
cout << "Level is B" << endl;
}
else if(grade>=70)
{
cout << "Level is C" << endl;
}
else
{
cout << "Level is D" << endl;
}
}
int main(int argc, char *argv[])
{
double grade;
cout << "Enter your grade: ";;
cin >> grade;
printGrade(grade);
return 0;
}
# void 函数一般不需要return语句, 但是也可以加入return 语句,用来表示函数的结束。(spark里面的写法)
如将上面的printGrade()函数进行一下修改,加入处理异常分数的代码:如果分数异常的话,使用return直接结束函数后面的部分
<cstdlib>中的exit()函数也有相似的功能。
void printGrade(double grade)
{
if(grade<0 || grade>100) // 异常分数处理, 使用了return语句
{
cout << "Invalid grade." << endl;
return;
// exit(0);
}
if(grade>=90)
{
cout << "Level is A" << endl;
}
else if(grade>=80)
{
cout << "Level is B" << endl;
}
else if(grade>=70)
{
cout << "Level is C" << endl;
}
else
{
cout << "Level is D" << endl;
}
}
参数顺序关联:parameter order association, 调用函数时的参数顺序必须与函数签名中的顺序一样。
求最大公约数: 函数版本
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
const int number_of_prime = 50;
const int number_of_prime_per_line = 10;
int Gcd(int n1, int n2) // 常规方法求GCD
{
int gcd = 1;
int k = 2;
while(k<=n1 && k<=n2)
{
if(n1%k==0 && n2%k==0)
{
gcd = k;
}
k++;
}
return gcd; // return gcd
}
int Gcd_o(int n1, int n2) // 欧几里得方法求最大公约数
{
int gcd;
if(n1 < n2)
{
int tmp;
tmp = n1;
n1 = n2;
n2 = tmp;
}
//int tt;
while(n1 % n2 != 0)
{
int b = n1 % n2;
n1 = n2;
n2 = b;
}
gcd = n2;
return gcd;
}
int main(int argc, char *argv[])
{
cout << "Enter a number: ";
int num1;
cin >> num1;
cout << "Enter a another number: ";
int num2;
cin >> num2;
int gcd_value = Gcd_o(num1, num2);
cout << "The GCD of " << num1 << " and " << num2 << " is " << gcd_value << endl;
return 0;
}
判断素数:代码模块化
定义两个函数isPrime(), printPrime()
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
bool isPrime(int num)
{
// 判断一个属是否为素数
bool flag = true;
for(int devisor=2; devisor<num; devisor++)
{
if(num%devisor==0)
{
flag = false;
}
}
return flag;
}
void printPrime(int num_of_prime)
{
int k = 0;
int prime = 2;
cout << "Print " << num_of_prime << " primes: " << endl;
while(k<=num_of_prime)
{
if(isPrime(prime))
{
k++;
if(k%10==0)
{
cout << setw(4) << prime << endl;
}
else
{
cout << setw(4) << prime;
}
}
prime++;
}
}
int main(int argc, char *argv[])
{
// 判断素数
cout << "Enter number of Prime: ";
int num1;
cin >> num1;
printPrime(num1);
return 0;
}
运行结果:
函数的重载: function overload
可以用同样的名字命名函数,只是函数的签名不同,两个函数的函数名相同,但是有不同的参数列表,并且在同一个文件夹中,C++编译器根据标签决定哪一个哪一个函数应该被使用。
例如两个max()函数
int max(int n1, int n2)
{
cout << "int max is called" << endl;
if(n1 > n2)
{
return n1;
}
else
{
return n2;
}
}
double max(double n1, double n2)
{
cout << "float max is called" << endl;
if(n1 > n2)
{
return n1;
}
else
{
return n2;
}
}
C++编译器会根据输入的参数决定调用哪个max()函数, 例如定义找三个数的最大值函数
double max(double n1, double n2, double n3)
{
return max(max(n1, n2), n3);
}
函数重载可以让代码更加清晰,执行相同的任务,但拥有不同类型参数的函数应该具有相同的名字, 函数重载必须有不同的参数列表,不能依据不同的返回类型重载函数
函数原型: 声明一个函数,但是没有实现它。
在调用一个函数之前,必须在程序中声明它,(1)可以将所有的函数声明放在调用之前.(2)在函数调用之前声明一个函数原型
function prototype, 也可以去掉函数原型中的参数名称,C++编译器会忽略他们。
int max(int n1, int n2); // function prototype
double max(double n1, double n2);
int main(int argc, char *argv[])
{
return 0;
}
缺省参数: 函数参数的默认值(python中),如果一个函数调用中没有传入参数,函数将使用缺省值
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
void print_area(float radius=1.0)
{
cout << "Area is " << 3.14*radius*radius << endl;
}
int main(int argc, char *argv[])
{
print_area();
print_area(5);
return 0;
}
函数参数中,有的参数有缺省值,有的参数没有缺省值,则所有带有缺省值的参数应该放在参数列表的末尾,否则是不合法的。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
内联函数:C++提供内联函数来提升短函数的性能 (inline function)
函数调用有额外的运行开销(将参数和CPU寄存器压入调用栈,以及在函数间切换控制所花费的时间)。内联函数可避免函数调用的开销。
内联函数是不会被调用的,实际上编译器将其代码复制到了每个调用节点上。
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
inline void f(int month, int year)
{
cout << "Month is " << month << endl;
cout << "Year is " << year << endl;
}
int main(int argc, char *argv[])
{
int month=10;
int year = 1994;
f(month, year);
f(9, 2018);
return 0;
}
在实际运行中,C++编译器会扩展每个对内联函数的调用,将代码复制过来替换调用,相当于:
int main(int argc, char *argv[])
{
int month=10;
int year = 1994;
cout << "Month is " << month << endl;
cout << "Year is " << year << endl;
return 0;
}
将函数直接复制到调用点。
内联函数对于短函数是值得使用的,但不适合程序中多次被调用的长函数, 在这种情况下会急剧增加可执行代码的长度,因为函数会被复制到多个位置,因此,c++编译器对过长的函数会忽略inline关键字,inline只是对编译器提出一个请求,至于接受还是忽略由编译器决定。
3. 局部,全局和静态局部变量
global variable: 定义在全部函数之外,缺省值是0
局部变量: 没有缺省值
静态局部变量: 当一个函数结束执行后,其所有的局部变量都会被销毁,这些变量也称为局部变量。如果需要保留局部变量的值,以便下次调用时使用,c++提供了静态局部变量来实现这一功能。
static int i;
4. 以引用方式传递参数:
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
void swap(int n1, int n2)
{
cout << "\tInside the function: " << endl;
cout << "\tn1 is " << n1 << " and n2 is " << n2 << endl;
int tmp = n1;
n1 = n2;
n2 = n1;
cout << "\tAfter swap n1 is " << n1 << " and n2 is " << n2 << endl;
}
int main(int argc, char *argv[])
{
int num1 = 10;
int num2 = 40;
cout << "Before call swap num1 is " << num1 << " and num2 is " << num2 << endl;
swap(num1, num2);
cout << "After call swap num1 is " << num1 << " and num2 is " << num2 << endl;
return 0;
}
运行结果:
变量num1和num2在执行完函数swap()之后并没有实现交换,只是在函数内部实现了交换
为此,c++提供了引用变量可以解决这一问题:引用变量可以用在函数参数中来引用原变量。
一个引用变量 实际上是另一个变量的别名,对引用变量的改变都会作用在原变量上
声明:
int count;
int& r=count;
引用变量 r 引用原变量count.
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
void swap(int& n1, int& n2)
{
cout << "\tInside the function: " << endl;
cout << "\tn1 is " << n1 << " and n2 is " << n2 << endl;
int tmp = n1;
n1 = n2;
n2 = n1;
cout << "\tAfter swap n1 is " << n1 << " and n2 is " << n2 << endl;
}
int main(int argc, char *argv[])
{
int num1 = 10;
int num2 = 40;
cout << "Before call swap num1 is " << num1 << " and num2 is " << num2 << endl;
swap(num1, num2);
cout << "After call swap num1 is " << num1 << " and num2 is " << num2 << endl;
return 0;
}
运行结果:
十六进制转化为十进制
#include <iostream>
#include <string>
#include <iomanip>
#include <cctype> // for char process;
#include <cmath> // for math function;
using namespace std;
int hex2Dec(string hex);
int hexchar2Dec(char hexchar);
int main(int argc, char *argv[])
{
string hexstring;
cout << "Enter a hex: ";
getline(cin, hexstring);
int Decimal = hex2Dec(hexstring);
cout << "The hex " << hexstring << " is equilent to decimal " << Decimal << endl;
//cout << hexchar2Dec('A') << endl;
return 0;
}
int hexchar2Dec(char hexchar) // 单个字符转化为数字
{
int num;
hexchar = toupper(hexchar);
if(hexchar>='0' && hexchar<='9')
{
num = hexchar - '0';
}
else if(hexchar>='A' && hexchar<='F')
{
num = 10 + hexchar - 'A';
}
else
{
num = 16; // the eeror value
}
return num;
}
int hex2Dec(const string& hex) // 字符串转化为数字
{
int len = hex.length(); // 字符串的长度
int decimal = 0;
for(int i=0; i<=len; i++)
{
int digit = hexchar2Dec(hex[i]);
if(digit == 16)
{
cout << "Enter a wrong hex" << endl;
break;
}
decimal += digit * pow(16.0, len-i-1); // pow()函数和sqrt()函数使用的陷阱: 参数应该转化为double类型
}
return decimal;
}
输入月份和年份,打印出日历:
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
bool isLeapYear(int year);
void printCalendarHead(int year, int month);
int getNumbersOfDaysInMonth(int year, int month);
int getTotalNumberOfDays(int year, int month);
int getStartDay(int year, int month);
void printCalendarBody(int year, int month);
int main(int argc, char *argv[])
{
cout << "Enter Year and Month: ";
int year, month;
cin >> year >> month;
printCalendarHead(year, month);
//cout << "Get number of days in a month: " << getNumbersOfDaysInMonth(year, month) << endl;
//cout << "Get total number of days: " << getTotalNumberOfDays(year, month) << endl;
printCalendarBody(year, month);
return 0;
}
//判断闰年
bool isLeapYear(int year)
{
bool LeapYear = false;
if(year%400==0 || (year%4==0 && year%100!=0))
{
LeapYear = true;
}
return LeapYear;
}
void printCalendarHead(int year, int month)
{
string month_name;
switch (month)
{
case 1: month_name = "January"; break;
case 2: month_name = "Febuary"; break;
case 3: month_name = "March"; break;
case 4: month_name = "April"; break;
case 5: month_name = "May"; break;
case 6: month_name = "June"; break;
case 7: month_name = "July"; break;
case 8: month_name = "August"; break;
case 9: month_name = "September"; break;
case 10: month_name = "October"; break;
case 11: month_name = "November"; break;
case 12: month_name = "December"; break;
}
cout << " " << month_name << " " << year << endl;
cout << "------------------------------------" << endl;
cout << " Sun " << "Mon " << "Tue " << "Wed " << "Thu " << "Fri " << "Sat " << endl;
}
int getTotalNumberOfDays(int year, int month) // from the start of 1800/1/1
{
int total = 0;
for(int i=1800; i<year; i++)
{
if(isLeapYear(year))
{
total += 366;
}
else
{
total += 365;
}
}
//total += getNumbersOfDaysInMonth(int year, int month);
for(int i=1; i<month; i++)
{
total += getNumbersOfDaysInMonth(year, i);
}
return total;
}
int getNumbersOfDaysInMonth(int year, int month) // 获取year年份,第month个月份的天数
{
if(month==1 || month==3 || month==5 || month==7 ||
month==8 || month==10 || month==12)
{
return 31;
}
if(month==4 || month==6 || month==9 || month==11)
{
return 30;
}
if(month==2)
{
return isLeapYear(year)? 29: 28;
}
return 0; // error month return
}
int getStartDay(int year, int month) //1800/1/1 星期三
{
int total_days = getTotalNumberOfDays(year, month) + 3; // +3三天后为星期日, 日历的头部 -4也行
return total_days % 7;
}
void printCalendarBody(int year, int month)
{
int start_day = getStartDay(year, month);
cout << "start day is " << start_day << endl;
int days_current_month = getNumbersOfDaysInMonth(year, month);
int i;
for(i=0; i<start_day; i++)
{
cout << " ";
}
for(i=1; i<=days_current_month; i++)
{
cout << setw(5) << i;
if((start_day + i)%7==0)
{
cout << endl;
}
}
cout << endl;
}
运行结果: