函数和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数组也响应的发生了改变