C++ primer plus书之--C++函数和C语言字符串, 结构体, string

函数和C风格字符串

要将C风格字符串作为参数传递给函数, 表示字符串的方式有三种:

1.char数组

2.用""扩起来的字符串常量

3.被设置为字符串地址的char指针

来看一个例子:

// c风格字符串例子
#include "iostream"
using namespace std;
unsigned int c_in_str(const char* str, char ch);

int main() {
	// char型数组表示字符串
	char str1[10] = "qwertqwqt";
	// char型指针 表示字符串
	char* p_str = "asdfasa";
	
	unsigned int n_str1 = c_in_str(str1, 'q');
	unsigned int n_p_str = c_in_str(p_str, 'a');
	cout << str1 << " has " << n_str1 << " q " << endl;
	cout << p_str << " has " << n_p_str << " a " << endl;
	return 0;
}


unsigned int c_in_str(const char* str, char ch)
{
	unsigned int count = 0;
	// 判断是不是\0 空字符
	while(*str)
	{
		if (*str == ch)
			count++;
		str += 1;
	}
	return count;
}

程序运行结果:

返回c风格的字符串的函数

c语言中没有字符串, 但是我们可以返回一个char型指针, 该指针指向字符串的首地址即可, 看一个demo:

// c风格字符串例子
#include "iostream"
using namespace std;
char* getstr(char ch, int n);

int main() {
	int times;
	char ch;
	
	cout << "Enter a character: ";
	cin >> ch;
	cout << "Enter a num : ";
	cin >> times;
	
	char* ps1 = getstr(ch, times);
	cout << ps1 << endl;
	// 用完记得回收
	delete ps1;
	ps1 = getstr('*', 10);
	cout << ps1 << endl;
	delete [] ps1;
	
	return 0;
}


char* getstr(char ch, int n)
{
	// 要考虑\0字符
	char* p_ch = new char[n + 1];
	while (n-- > 0)
	{
		p_ch[n] = ch;
	}
	return p_ch;
}

 程序运行结果:

注意, 变量p_ch的作用域是在函数getstr函数内部, 当函数执行完毕后, p_ch(而不是字符串)使用的内存将被释放, 但由于函数返回了p_ch的值, 因此程序仍可以通过main()里的指针ps1来访问新建的字符串, mian中当new创建的字符串不再需要时通过delete释放该字符串占用的内存.

函数和结构体

虽然结构变量和数组一样可以存储多个数据项, 单在涉及到函数时, 结构变量的行为更接近于基本单值变量, 也就是说与数组不同, 结构将其数据组成单个实体或数据对象, 该实体被视为一个整体. 按值传递结构, 就像普通变量那样, 函数将使用原始结构的副本. 另外函数也可以返回结构. 与数组名就是数组的第一个元素的地址不同的是, 结构名只是结构的名称, 要获得结构的地址, 必须使用地址运算符&.

传递和返回结构

当结构比较小时, 按值传递结构最合理.

看个简单的结构体做函数参数的demo:

// 函数和结构体
#include "iostream"
using namespace std;

struct travel_time
{
	int hours;
	int mins;
};

const int Mins_per_hour = 60;

travel_time sum(travel_time t1, travel_time t2);
void show(travel_time t);

int main() {
	
	// 定义并初始化一个结构体变量
	travel_time d1 = {2, 38};
	travel_time d2 = {3, 46};
	
	travel_time sum1 = sum(d1, d2);
	// 打印的是sum1的首地址可以和sum方法中的地址进行比较, 可以看出来两个地址不是同一个
	// 也就是这种传递结构体的方式, 传递的是副本而不是地址
	cout << "address of sum1 in main fun : " << &sum1 << endl;
	cout << "two-day total : ";
	show(sum1);
	
	travel_time d3 = {6, 58};
	cout << "Three-day total:";
	show(sum(sum1, d3));
	return 0;
}

travel_time sum(travel_time t1, travel_time t2)
{
	travel_time total;
	total.hours = t1.hours + t2.hours + (t1.mins + t2.mins) / Mins_per_hour;
	total.mins = (t1.mins + t2.mins) % Mins_per_hour;
	// 打印total的首地址
	cout << "address of total in sum fun : " << &total << endl;
	return total;
}

void show(travel_time t)
{
	cout << "hours : " << t.hours << ", mins : " << t.mins << endl;
}

运行结果为:

注意看代码中的注释, 理解到底是传递的地址还是传递的是值的副本

再看一个简单的demo, 就是坐标系的转换, 一种是记录点到圆点的距离以及和x轴的夹角, 一种是记录点在坐标系中的x, y值:

// 函数和结构体
#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);

int main() {
	// 定义两个结构体变量
	rect rplace;
	polar pplace;
	cout << "Enter the x and y value : ";
	// 借助cin来判断用户输入的是否合法, 因为x,y是double类型, 所以只有输入double类型的时候才是合法的
	while (cin >> rplace.x >> rplace.y)
	{
		pplace = rect_to_polar(rplace);
		show(pplace);
		cout << "Next x, y nums, q to quit : ";
	}
	cout << "******End********" << endl;
	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 dapos)
{
	const double Rad_to_deg = 57.296;
	cout << "distance = " << dapos.distance;
	cout << ", angle = " << dapos.angle * Rad_to_deg;
	cout << " degrees" << endl;
}

看运行结果:

代码没有什么特殊的地方是为了, 和传递结构体地址的函数做对比的代码, 只需要注意while循环判断中的判断方法即可

传递结构体的地址

假设要传递结构的地址而不是整个结构以节省时间和空间, 应该使用指向结构的指针, 需要修改以上代码的三个地方:

1.调用函数时, 将结构的地址(&pplace)而不是结构本身(pplace)传递给函数

2.将形参生命为指向polar的指针(polar *)类型, 如果函数内不需要修改结构体, 可以声明成 const polar * 类型

3.由于形参是指针而不是结构, 因此应该使用间接成员运算符(->), 而不是成员运算符(.)

以下是修改之后的程序:

// 函数和结构体
#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(const polar* polar);
void changeValue(polar* polar);

int main() {
	// 定义两个结构体变量
	rect rplace;
	polar pplace;
	cout << "Enter the x and y value : ";
	// 借助cin来判断用户输入的是否合法, 因为x,y是double类型, 所以只有输入double类型的时候才是合法的
	while (cin >> rplace.x >> rplace.y)
	{
		// 这里要使用&传递地址
		pplace = rect_to_polar(&rplace);
		show(&pplace);
		cout << "Next x, y nums, q to quit : ";
	}
	cout << "******End********" << endl;
	
	cout << "测试是否是传递的地址" << endl;
	cout << "before change : " << endl;
	show(&pplace);
	// 这里将pplace的内容做了修改, 看是否传递的是地址, 也就是在changeValue里修改, 在函数外是否也有效
	changeValue(&pplace);
	cout << "after change : " << endl;
	show(&pplace);
	return 0;
}

polar rect_to_polar(rect* xypos)
{
	polar answer;
	// 开方
	// 注意这里xypos已经是指针了, 所以不能使用.调用属性, 而应该使用->
	answer.distance = sqrt(xypos->x * xypos->x + xypos->y * xypos->y);
	// 计算角度
	answer.angle = atan2(xypos->y, xypos->x);
	return answer;
}

void show(const polar* dapos)
{
	const double Rad_to_deg = 57.296;
	cout << "distance = " << dapos->distance;
	cout << ", angle = " << dapos->angle * Rad_to_deg;
	cout << " degrees" << endl;
}

void changeValue(polar* polar)
{
	// 将数据都设置成10
	polar->distance = 10;
	polar->angle = 10;
}

看运行结果:

需要注意的地方都在注释里写清楚了.

函数和string

虽然c风格字符串和string对象的用途几乎相同, 但与数组相比, 写法上string对象与结构题更为相似

// 函数和结构体
#include "iostream"
#include "string"
using namespace std;

const int Size = 3;

// 函数原型
void show(string sarr[], int n);

int main() {
	// 声明了一个字符串数组
	string list[Size];
	cout << "Enter your " << Size << " favorite num : ";
	for (int i = 0; i < Size; i++)
	{
		cout << i + 1 << " : ";
		getline(cin, list[i]);
	}
	cout << "Your list : " << endl;
	show(list, Size);
	// 重新打印list看看数据是否发生了改变
	cout << "list[0]" << " : " << list[0] << endl;
	cout << "*(list + 1)" << " : " << *(list + 1) << endl;
	return 0;
}

void show(string sarr[], int n)
{
	for(int i = 0; i < n ; i++)
	{
		// 两种输出方法均可
		cout << i + 1 << " : " << sarr[i] << endl;
		cout << i + 1 << " : " << *(sarr + i) << endl;
	}
	
	// 这里做一个修改, 看看传递的是字符串数组的副本还是传递的是地址
	sarr[0] = -1;
	*(sarr + 1) = -2;
}

看一下输出结果:

这里传递的是string数组, 所以数组名仍然是指针, 也就是如果在方法内对数据进行了修改, 在外面的string数组也响应的发生了改变

猜你喜欢

转载自blog.csdn.net/c1392851600/article/details/84575036