形参:用于接收传递值得变量实参:传递给函数的值
在循环中采用cin >> ch来读取字符而不是cin.get(),因为后者可读取所有输入的字符包括空格与换行符,而前者跳过空格与换行符。
交替进行乘除运算可防止中间值结果超过最大浮点数。
long double probability(unsigned numbers, unsigned picks) {
long double result = 1.0;
long double n;
unsigned p;
for (n = numbers, p = picks; p > 0; n--,p--)
result = result * n / p;
return result;
}
(为何不直接用两个形参来进行运算,而要定义两个局部变量?)
7.3函数和数组
将数组名和数组长度同时作为参数传递给函数。数组名传递的是数组的地址。
int * arr等价于int arr []
arr [i] = *(arr + i)
&arr [i] = arr + i
在数组函数中,实参cookies,形参arr,两者指向同一个地址。但是sizeof cookies的值为整个数组的长度(8个int值即为32),而sizeof arr的值为指针变量的长度。(用int定义,则为4)
sum_arr(cookies + 4,4),计算的是从cookies [4]到cookies [7]之间4个数字的和
填充数组:
int fill_array(double ar[], int limit) {
using namespace std;
double temp;
int i;
for (i = 0; i < limit; i++) {
cout << "Enter value #" << i + 1 << ": ";
cin >> temp;
if (!cin) {
cin.clear();
while (cin.get() != '\n')
continue;
cout << "Bad input; input process terminated.\n";
break;
}
else if (temp < 0)
break;
ar[i] = temp;
}
return i;
}
在不确定数组要输入的确切长度情况下输入,并返回实际长度。
正确输入(非负值)的前提下,循环结束后 i 的值比最后一个数组索引大1,即为数组长度。
显示数组及用const保护数组:
void show_array(const double arr[], int n) {
using namespace std;
for (int i = 0; i < n; i++) {
cout << "Property #" << i + 1 << ": $";
cout << arr[i] << endl;
}
}
const意味着函数中将不可使用arr []修改数组的值。
函数缺少一些原始数组的知识,比如它不能通过sizeof获得原始数组的长度,必须依赖于手动输入。
使用数组区间的函数:
sum_arr(cookies, cookies + Arsize);
sum_arr(cookies + 3, cookies + 8);
int sum_arr(const int* begin, const int* end) {
const int* pt;
int total = 0;
for (pt = begin; pt != end; pt++)
total += *pt;
return total;
}
制定元素区间, cookies 和 cookies + Arsize 定义了区间, cookies + Arsize 指向数组区间中最后一个元素后面的一个位置。
表达式 end - begin 是一个整数值,等于元素的元素数目(务必按顺序传递指针)。
7.3.5 指针和 const
1.让指针指向一个常量对象:防止使用指针改变所指向的值
2.将指针本身声明为常量,防止改变指针指向的位置
声明一个指向常量(const)的指针:
int age =39;
const int* pt = &age;
只是对 pt 而言它指向的是个常量:不能使用 pt 来修改 age 的值,但是可以使用 age 来修改。
并且 const 只能防止修改 pt 指向的值,不能防止修改 pt 的值,可以将新地址赋给 pt 。
可将 const 变量的地址赋给指向 const 的指针,两种方式都不能改变值。
不可将 const 变量的地址赋给常规指针。
const int months[3] = {1,2,3};
int sum(int arr[], int n);
......
int j = sum(months, 3); //错误!
指针指向指针:
一级间关系:可将非 const 指针赋给 const 指针
int age = 39;
int* pt = &age;
const int*pd = pt;
两级间关系:
const int **ppt;
int* p1;
const int n = 13;
ppt = &p1;// not allowed , but suppose it were
*ppt = &n;// valid, both const, but sets pl to point at n
*p1 = 10;// valid, but changes const n
(没搞明白) P222
又一种使用 const 的方式使得无法修改指针的值:
int sloth = 3;
const int* ps = &sloth;
int* const finger = &sloth;
使 finger 只能指向 sloth,但允许使用 finger 改变 sloth 的值。
不允许使用 ps 改变 sloth 的值,但 ps 可以指向另一个变量。
即 finger 和 *ps 是 const ,而 *finger 和 ps 不是 const。
int trouble = 3.2;
const double* const stick = &trouble;
指向 const 对象的 const 指针:stick 和 *stick 都是 const ,都不可改变。
通常,将指针作为函数参数传递时,可以使用指向 const 的指针来保护数据:
void array(const double ar[], int n);
array 不能修改传递给它的数组中的值。要求这里的数组元素是基本类型(一级间接关系),若它们是指针或指向指针的指针(二级),则不可使用 const 。 (???)
7.4 函数和二维数组
int data[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };
int total = sum(data, 3);
int sum(int(*ar2)[4], int size) {};// (*ar2)[4]括号必不可少: 指向由4个int组成的数组的指针
int sum2(int*ar2[4], int size) {};// 错误!:由4个指向int的指针组成的数组 (定义了一个指针数组),并且函数参数不能是数组
int sum(int ar2[][4], int size) {}; // 正确,且可读性更强
data 的类型是指向由 4 个 int 组成的数组的指针。
指针类型指定了列数,故只需将行数作为函数参数传递。
int a[100][4];
int total1 = sum(a, 100);// sum all of a
int total1 = sum(a, 10); // sum first 10 rows of a
int total1 = sum(a+10, 20);// sum next 20 rows of a
在函数中采用二维数组名称的方式访问数组元素 ar2 [ r ] [ c ] ,表达式 ar2 + r 指向编号为 r 的元素,是一个由 4 个 int 值组成的数组,故需要两次解除引用。
ar2 [ r ] [ c ] = *(*( ar2 + r ) + c ) // 指向指针的指针
因 ar2 是指向指针的指针,故 sum 函数中不能使用 const 。
7.5 函数和 C- 风格字符串
C- 风格字符串由一系列字符组成,以空值字符结尾(区别于常规 char 数组)。要将字符串作为参数传递给函数,表示其的方法有三种:char 数组、用引号括起的字符串常量、被设置为字符串地址的 char 指针。
上述三种选择的类型都是 char 指针 ( char* ),可将其作为字符串处理函数的参数
char ghost[15] = "galloping";
char * str = "galumpging"; // 报错! "const char *" 类型的值不能用于初始化 "char *" 类型的实体
int n1 = strlen(ghost);
int n2 = strlen(str);
int n3 = strlen("galumpging");
实际传递的是字符串第一个字符的地址。故形参应声明为 *char 类型。
计算特定字符在字符串中出现次数:
unsigned int c_in_str(const char * str, char ch) {
unsigned int count = 0;
while (*str) // quit when *str is '\0'
{
if (*str == ch)
count++;
str++;
}
return count;
}
可以用数组表示法,但指针表示法提示参数不一定是数组名,可以是其他形式的指针。
unsigned int c_in_str(const char str[], char ch)
空值字符 ' \0 '
返回字符串的函数:
#include<iostream>
char * buildstr(char c, int n);
int main() {
using namespace std;
int times;
char ch;
cin >> ch >> times;
char* ps = buildstr(ch, times);
cout << ps << endl;/*cout 对象认为 char 的地址是字符串的地址
打印该地址处的字符,然后继续打印后面的字符直到遇到空字符'\0'为止
可以将字符串或数组“赋值给一个 char 指针元素”*/
delete[]ps;
ps = buildstr('+', 20);
cout << ps << "-DONE-" << ps << endl;
delete[]ps;
return 0;
}
char * buildstr(char c, int n) {
char * pstr = new char[n + 1];
pstr[n] = '\0';
while (n-- > 0) {
pstr[n] = c;
}
return pstr;
}
函数无法返回一个字符串,但是可以返回字符串的地址。
7.6 函数和结构
传递和返回结构:
#include<iostream>
using namespace std;
struct travel_time {
int hours;
int mins;
};
const int Mins_per_hours = 60;
travel_time sum(travel_time t1, travel_time t2);
void show_time(travel_time t);
int main(){
travel_time day1 = { 5,45 };
travel_time day2 = { 4,55 };
travel_time trip = sum(day1, day2);
cout << "Two days total: ";
show_time(trip);
return 0;
}
travel_time sum(travel_time t1, travel_time t2) {
travel_time total;
total.mins = (t1.mins + t2.mins) % Mins_per_hours;
total.hours = t1.hours + t2.hours + (t1.mins + t2.mins) / Mins_per_hours;
return total;
}
void show_time(travel_time t) {
cout << t.hours << " hours " << t.mins << " minutes\n";
}
函数的返回值类型为结构类型。
直角坐标转化为极坐标:
#include<iostream>
#include<cmath>
using namespace std;
struct polar {
double distance;
double angle;
};
struct rect {
double x;
double y;
};
polar rect_to_polar(rect xypos);
void show_polar(polar dapos);
int main() {
rect rplace;
polar pplace;
cout << "Enter the x and y values: ";
while (cin >> rplace.x >> rplace.y) {
pplace = rect_to_polar(rplace);
show_polar(pplace);
cout << "Next two numbers (q to quit): ";
}
cout << "Done.\n";
return 0;
}
polar rect_to_polar(rect xypos) {
polar answer;
answer.distance = sqrt(xypos.x*xypos.x + xypos.y*xypos.y);
answer.angle = atan2(xypos.y, xypos.x);
return answer;
}
void show_polar(polar dapos) {
const double Rad_to_deg = 57.29577951;
cout << "distance = " << dapos.distance;
cout << ", angle = " << dapos.angle*Rad_to_deg;
cout << " degress\n";
}
定义返回值为结构类型的函数时,直接用结构名去定义,不必再用定义结构时的 struct。
atan() 函数不能区分 180 度以内和以外的角度,没有 atan2() 实用。
cin 期望用户输入数字,并接受任何形式的和数字输入,在输入字母 q 后(非数字输入)cin 将 q 留在输入队列中,并返回一个 fasle 的值导致循环结束。
若出现错误输入后还要继续运行,则需要 cin.clear() 来重置输入,并通过读取不合法输入来丢弃它们。
传递结构的地址:
为节省时间和空间,应使用指向结构的指针。修改原来的函数:
1.调用函数时将结构的地址 &pplace 而不是结构本身传递给它。
2.形参声明为指向 polar 的指针,即 polar* 类型。因原 rect_to_polar 函数需要返回一个结构,为利用指针的效率,需要传给函数两个指针,而不设置返回值。第一个指针指向要转换的结构(参数为 const 指针),第二个指针指向储存转换结果的结构。函数不返回一个新的结构而是修改已有的机构。
3.形参是指针不是结构,故应该用间接成员运算符 (->) 而不是成员运算符 ( . )。
#include<iostream>
#include<cmath>
using namespace std;
struct polar {
double distance;
double angle;
};
struct rect {
double x;
double y;
};
void rect_to_polar(const rect* pxy,polar*pda);
void show_polar(polar* pda);
int main() {
rect rplace;
polar pplace;
cout << "Enter the x and y values: ";
while (cin >> rplace.x >> rplace.y) {
rect_to_polar(&rplace,&pplace);
show_polar(&pplace);
cout << "Next two numbers (q to quit): ";
}
cout << "Done.\n";
return 0;
}
void rect_to_polar(const rect* pxy, polar*pda) {
pda->distance = sqrt(pxy->x*pxy->x + pxy->y*pxy->y);
pda->angle = atan2(pxy->y, pxy->x);
}
void show_polar(polar* pda){
const double Rad_to_deg = 57.29577951;
cout << "distance = " << pda->distance;
cout << ", angle = " << pda->angle*Rad_to_deg;
cout << " degress\n";
}
7.7 函数和 string 对象
#include<iostream>
#include<string>
using namespace std;
const int SIZE = 5;
void display(const string sa[], int n);
int main() {
string list[SIZE];
cout << "Enter your " << SIZE << " favorite astronomical signts:\n";
for (int i = 0; i < SIZE; i++) {
cout << i + 1 << ": ";
getline(cin,list[i]);
}
cout << "Your list:\n";
display(list, SIZE);
return 0;
}
void display(const string sa[], int n) {
for (int i = 0; i < n; i++)
cout << i + 1 << ": " << sa[i] << endl;
}
形参 sa 是一个指向 string 对象的指针,因此 sa [ i ] 是一个 string 对象。
7.8 函数与 array 对象
类对象基于结构,两者在函数处理方面很相近。模板 array 不仅可以储存基本数据类型还可以储存对象。
#include<iostream>
#include<array>
#include<string>
using namespace std;
const int Seasons = 4;
const array<string, Seasons>Snames = { "Spring","Summer","Fall","Winter" };
void fill(array<double, Seasons>* pa);
void show(array<double, Seasons> da);
int main() {
array<double, Seasons> expenses;
fill(&expenses);
show(expenses);
return 0;
}
void fill(array<double, Seasons>* pa) {
for (int i = 0; i < Seasons; i++) {
cout << "Enter " << Snames[i] << " expenses: ";
cin >> (*pa)[i];
}
}
void show(array<double, Seasons> da) {
double total = 0.0;
cout << "\nEXPENSES\n";
for (int i = 0; i < Seasons; i++) {
cout << Snames[i] << ": $" << da[i] << endl;
total += da[i];
}
cout << "Total Expenses: $" << total << endl;
}
7.9 递归