《C++ Primer Plus》学习笔记 第7章 函数——C++编程的基础(编辑中)

形参:用于接收传递值得变量实参:传递给函数的值

在循环中采用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 递归

猜你喜欢

转载自blog.csdn.net/Perce_Issac/article/details/81839305