赞
踩
库函数
自定义函数
先写段程序,复习下自定义函数
- #include <iostream>
-
- void simple(void);
- using namespace std;
-
- int main(void)
- {
-
- cout << "main() will call the somple() function " << endl;
- simple();
-
- return 0;
- }
- void simple(void)
- {
- cout << "I'm simple but a function." << endl;
- }

自定义函数的三个部分:
函数定义,函数声明,函数调用,缺一不可
函数分为有返回值和没有返回值的
没有返回值就是void函数,通用格式如下:
void functionName (parameterList)
{
statement(s)
return; //可写可不写,没返回值一般不写
}
其中的paramenterList是指传递给函数的参数类型和数量,例如:
void cheers (int n)
{
for(int i = 0; i <n ; i++)
cout << "Cheers!";
cout << endl;
}
这里的int意思就是,在调用cheers函数的时候,应该给它一个Int值作为参数传递给它
有返回值的函数将生成一个值,并将它返回给调用函数,通用格式如下:
typeName functionName(paramterList)
{
statements;
return value;
}
对于有返回值的函数,必须使用返回语句,以便把值返回给调用函数。
值本身可以是常量、变量、表达式,只是其结果的类型必须为typeName类型或者可以被转换为typeName(比如说我们返回类型是double,但是返回一个int表达式,int会被强转为double类型)
C++对于返回值的类有限制:不能是数组,但是可以是其他任何类型,比如结构对象等(但是可以把数组作为结构或对象组成部分来返回)
函数在执行返回语句后结束。如果函数包含多条返回语句,则函数在执行遇到第一条返回语句后结束。
int bigger (int a , int b)
{
if(a>b)
return a;
else
return b;
}
看如下程序,体会自定义有返回值和没有返回值的函数
- #include <iostream>
-
- using namespace std;
- void cheers(int n);//声明
- double cube(double x);
-
- int main(void)
- {
- cheers(5);//调用
-
- cout << "Give me a number: " << endl;
- double side;
- cin >> side;
-
- double volume = cube(side);
- cout << side << " side cube = " << volume << endl;
-
- cheers(cube(2)); //自定义函数调用自定义函数,并进行double转int的操作
-
- return 0;
- }
- void cheers(int n) //无返回值
- {
- for (int i = 0; i < n; i++)
- {
- cout << "Cheers!" << endl;
- }
- }
- double cube(double x) //有返回值
- {
- x = x * x * x;
- return x;
- }

结果:
Cheers!
Cheers!
Cheers!
Cheers!
Cheers!
Give me a number:
3
3 side cube = 27
Cheers!
Cheers!
Cheers!
Cheers!
Cheers!
Cheers!
Cheers!
Cheers!
*
1)自定义无返回值cheers,自定义有返回值cube
2)把输入作为cube的参数,返回double类型
3)自定义函数cheers引用自定义函数cube
1)为什么需要原型?
就是头文件下面为什么要再声明一下
函数原型描述了函数到编译器的结构,也就是它将函数的返回值类型及参数类型数量告诉编译器
cube()函数完成计算后,把返回值放到了指定位置——可能是寄存器,可能是内存,然后调用函数(main())从这个为之取得返回值
C++编译器并不会在文件中进一步查找,这样效率太低了,所以最好能提供原型
2)原型的语法
函数原型并不需要提供变量名,有类型列表有足够了。比如对于cheer()原型,我们只提供了参数类型:
void cheers(int);
比方说我们去饭店吃饭,只需要打电话告诉饭店,我们有几个人,我们吃什么菜,我们有几个人,而不需要把每个人的名字告诉饭店一个道理
所以说比方说下面的原型:
double cube(double x)写成double cube (double) 一样是可以编译的!
3)原型的功能
要确保以下几点:
·编译器正确处理函数返回值;
·编译器检查使用的参数数目是否正确
·编译器检查使用的参数类型是否正确
但是如果是较大的数据类型转换为较小类型时,有些编译器会发出警告,指出数据可能丢失
比如把2.33E27转为int类型,这种值就不会被正确转换
C++通常按值传递参数,这意味着将数值参数传递给函数,而后者将其赋给新的变量
比如:double volume = cube (side);
→ side是一个变量,前面定义为5
cube的函数头如下:double cube (double x)
→ 函数被调用时,该函数创建一个新的名为x的double变量,把它初始化为5
→ 这样cube()执行的操作将不会影响main()中的数据。
→ cube使用的是side的副本,而不是原来的数据
用于接收传递值得变量被称为形参,比如这里double 的x
传递给函数的值被称为实参,比如原来的side
1)函数内定义两个变量,哪怕是相同类型,也要写全
void fifi (float a, float b)
2)与一个参数一样,原型中的变量名不必与定义中的变量名相同,甚至可以省略,但是最好写上,至少知道人家是干嘛用的
见如下程序:演示修改形参并不会影响调用函数中的数据
- #include <iostream>
-
- using namespace std;
-
- void n_chars(char c, int n);
-
- int main(void)
- {
- char ch;
- int times;
-
- cout << "Please enter a character: ";
- cin >> ch; //cin.get()也行
-
- while(ch != 'q')
- {
- cout << "Enter a integer: ";
- cin >> times;
-
- n_chars(ch,times);
- cout << endl;
- cout << "Enter another character or press q_key to quie: " << endl;
- cin >> ch;
- }
- return 0;
- }
-
- void n_chars(char c, int n)
- {
- while (n-- >0)
- {
- cout << c;
- }
- }

结果:
Please enter a character:
1
Enter a integer: 10
1111111111
Enter another character or press q_key to quie:
q
*
1)声明了ch和times分别作为n_chars的两个参数,然后通过函数返回内容
题目如下:
美国许多州都采用某种纸牌游戏来发行彩票,让参与者从卡片中选择一定数目的选项。
例如:从51个数字中选取6个,随后管理者随机抽取6个数。如果参与者选择的数字与这6个完全相同,赢得几百万美金。函数计算中奖概率
首先,必须从51个数中选取6个数,而中奖的概率为1/R,R的计算公式为:
选择6个数时,分母为6!,分子是连续6个整数的乘积,从51开始连续递减,可以采用for循环计算
long double result = 1.0;
for(n = numbers , p = picks ; p > 0 ; n-- , p--)(numbers是一共从多少个数中选择)
result = result * n / p;(pick是选取数的个数)
程序:
- #include <iostream>
-
- using namespace std;
- long double probability(unsigned int numbers,unsigned int picks);
-
- int main(void)
- {
- unsigned int total,choices;
- cout << "Enter the total number of choices on the game card and the number of picks allowed: " << endl;
- while((cin >> total >> choices) && choices <= total)
- {
- cout << "You have one chance in " << probability(total,choices) << " of winning";
- cout << "Please enter next two number(q to quit)";
- }
- cout << "Bye";
-
- return 0;
- }
- long double probability(unsigned int numbers,unsigned int picks)
- {
- double n,p;
- long double result = 1.0;
-
- for(n = numbers, p = picks; p > 0; n--,p--)
- result = result * (n / p);
-
- return result;
- }

结果(输出是R,概率是1/R):
Enter the total number of choices on the game card and the number of picks allowed:
49
6
You have one chance in 1.39838e+07 of winningPlease enter next two number(q to quit)100
10
You have one chance in 1.73103e+13 of winningPlease enter next two number(q to quit)
*
1)这个程序就演示了可以在函数中使用两种局部变量。
首先是形参(numbers和picks);其次是其他局部变量(result、n、p)
形参和局部变量的主要区别是,形参从调用函数那里获得自己的值,而其他变量是从函数中获得自己的值
2)注意可以这样写:
while((cin >> total >> choices) && choices <= total),代表如果输入成功
如何把函数和数组结合起来?
假设使用一个数组来记录家庭野餐中每人吃了多少甜品,现在要统计总数,来使用函数实现
我们返回值部分不能为数组,但是参数部分可以是
比如以下声明:
int sum_arr ( int arr[] , int n )
这里的arr看起来是一个数组,其实它是一个指针,但是函数编写时,可以把它看成是数组
下面程序将演示如同使用数组名那样使用指针
- #include <iostream>
-
- using namespace std;
-
- const int ArSize = 8;
-
- int sum_arr(int arr[],int n);
-
- int main(void)
- {
- int cookies[ArSize] = {1,2,4,8,16,32,64,128};//数组定义好了
-
- int sum = sum_arr(cookies,ArSize); //函数返回总数
- cout << "Total cookies eaten: " << sum << endl;
-
- return 0;
- }
- int sum_arr(int arr[],int n)//它用了两个参数,一个是数组,一个是数组数量
- {
- int total = 0;
- for(int i = 0; i < n; i++)//很明显,我们用数组的数量和求和
- {
- total += arr[i];
- }
- return total;
- }

结果:Total cookies eaten: 255
1:函数如何使用指针处理数组
大多数情况下,C++和C一样,也将数组名视为指针,它将数组名解释为第一个元素的地址:
cookies == & cookies [0]
但是也有一些例外,比如
1)数组声明使用数组名来标记存储位置
2)对数组名使用sizeof将得到整个数组的长度(字节为单位)
3)&用于数组名时,返回整个数组的地址
再来看这个函数调用:int sum = sun_arr(cookies , ArSize)
cookies是数组名,而C++规定它是第一个元素的地址,因此函数传递的是地址。所以cookies类型必须是int指针,即int *。所以正确的函数头应该是:
in sun_arr ( int * arr , int n)
在函数头或函数原型中,int * arr和int arr[]含义相同,它们都意味着arr是一个int指针
arr[i] == *(ar + i)
&arr [i] == ar + i
将指针(数组名+1)意味着加上了一个与指针指向的类型的长度相等的值,就是指针偏移
sum_arr把cookies的第一个元素的地址和数组中的元素传递给了sum_arr函数。
sum_arr函数将cookies的地址赋给指针变量arr,将ArSize赋给int变量n
函数在传递数组的时候,使用的是原来的数组
看下面的函数,这回我们使用指针来演示上面的程序
- #include <iostream>
-
- using namespace std;
-
- const int ArSize = 8;
-
- int sum_arr(int arr[],int n);
-
- int main(void)
- {
- int cookies[ArSize] = {1,2,4,8,16,32,64,128};//数组定义好了
- cout << "array adress: " << &cookies << endl;
- cout << "size of cookies: " << sizeof(cookies) << endl; //32
-
- int sum = sum_arr(cookies,ArSize); //函数返回总数
- cout << "Total cookies eaten: " << sum << endl;
-
- sum = sum_arr(cookies,3);
- cout << "3 cookies eaten: " << sum << endl;
-
- return 0;
- }
- int sum_arr(int arr[],int n)//它用了两个参数,一个是数组,一个是数组数量
- {
- int total = 0;
- cout << "arr adress: " << arr << endl;
- cout << "size of arr: " << sizeof arr << endl;//这里返回的不是数组,而是指针,8个字节,int *占用8字节
-
- for(int i = 0; i < n; i++)//很明显,我们用数组的数量和求和
- {
- total += arr[i];
- }
- return total;
- }

结果:
array adress: 0x3cebbff7d0
size of cookies: 32
arr adress: 0x3cebbff7d0
size of arr: 8
Total cookies eaten: 255
arr adress: 0x3cebbff7d0
size of arr: 8
3 cookies eaten: 7
*
1)
cout << "size of cookies: " << sizeof(cookies) << endl; 这里打印的是数组的大小
2)cout << "arr adress: " << arr << endl;
我们sum_array的地址和cookies地址完全一样,所以我们知道sum_array其实就是使用了cookies的地址来进行运算
3)cout << "size of arr: " << sizeof arr << endl;//这里返回的不是数组,而是指针,8个字节,int *占用8字节
先看一个案例:考虑对房地产数组执行的操作。两个基本操作是,将值读入到数组中和显示数组的内容。
另外添加另一个操作:重新评估每种房地产的值。(所有房地产都以相同的比率增加或减少)
- #include <iostream>
-
- using namespace std;
- const int Max = 5;
-
- int fill_array(double arr[],int limit);
- void show_array(const double arr[],int n);
- void revalue(double r,double arr[], int n);
-
- int main(void)
- {
-
- double properties[Max];//5个double类型数组
-
- int size = fill_array(properties,Max);//添加数组的方法
-
- show_array(properties,size);
-
- if(size > 0)
- {
- cout << "Enter revaluation factor: ";
- double factor;
-
- while(!(cin >> factor))
- {
- cin.clear();
- while ((cin.get() != '\n'))
- {
- continue;
- }
- cout << "Bad input : input process terminated." << endl;
- }
- revalue(factor,properties,size);
-
- show_array(properties,size);
- }
- cout << "Done!\n";
- cin.get();
- cin.get();
- return 0;
- }
- int fill_array(double arr[],int limit)//
- {
- double temp;
- int i;
- for (i = 0; i < Max; i++)
- {
- cout << "Enter value #" << i + 1 << ":";
- cin >> temp;
- if(!cin)
- {
- cin.clear();
- while (cin.get() != '\n')
- {
- continue;
- }
- cout << "Bad input : input process terminated." << endl;
- break;
- }
- else if(temp < 0)
- break;
- else
- arr[i] = temp;
- }
- return i;
- }
- void show_array(const double arr[],int n)//加上const以后,就不能通过指针来修改对象了
- {
- for (int i = 0; i < n; i++)
- {
- cout << "Property #" << i+1 << " : $";
- cout << arr[i] << endl;
- }
- }
- void revalue(double r,double arr[], int n)//r是比例系数
- {
- for(int i = 0; i < n ; i++)
- {
- arr[i] *= r;
- }
- }

结果:
输入:
Enter value #1:1000
Enter value #2:2000
Enter value #3:3000
Enter value #4:4000
Enter value #5:5000
显示:
Property #1 : $1000
Property #2 : $2000
Property #3 : $3000
Property #4 : $4000
Property #5 : $5000
修改因子:
Enter revaluation factor: 2
Property #1 : $2000
Property #2 : $4000
Property #3 : $6000
Property #4 : $8000
Property #5 : $10000
Done!
*程序说明:
1)填充数组
该函数指定两个参数,一个是数组名,另一个是指定读取的最大元素数,该函数返回实际读取的元素数,还要注意判断它输入是否正确和输入是否大于0这两个条件,然后使用for循环,创建临时值,挨个放进去
2)显示数组
这个就是循环+显示数组元素
3)修改数组
它需要给函数传递三个参数,分别是因子,数组和元素数目,没有返回值
除了上面例子中提供数组中的数据类型、数组的起始位置和数组中元素数量,还有另一种方法:
即指定元素区间(range),这可以通过传递两个指针来完成:一个指针标识数组开头,另一个指针标识数组的尾部,看下例(还是饼干):
- #include <iostream>
-
- using namespace std;
-
- const int ArSize = 8;
-
- int sum_arr(const int * begin, const int * end);
-
- int main(void)
- {
- int cookies[ArSize] = {1,2,4,8,16,32,64,128};
-
- int sum = sum_arr(cookies, cookies + ArSize);
- cout << "Total cookies eaten: " << sum << endl;
-
- return 0;
- }
- int sum_arr(const int * begin, const int * end)
- {
- int total = 0;
- const int * pt;
-
- for(pt = begin; pt != end; pt++)
- total += *pt;
- return total;
- }

结果:Total cookies eaten: 255
*
1)定义了指针的开头和指针的结尾,求和就用指针内容
2)调用函数的开头结尾位置采用cookies和cookies + ArSize
三个const的用法
①const int *pt
②int *const pt;
③const int *const pt;
①const int *pt等效于 int const *pt
使用const,就不能通过指针修改数组了,比如如下程序:
- int main(void)
- {
- int n =10;
- int *pt = &n;
-
- cout << "1)n= " << n << endl;
-
- *pt = 20;//通过指针修改了n的对象
- cout << "2)n= " << n << endl;
-
- return 0;
- }
1)n= 10
2)n= 20
但是如果加上const,就不能通过指针来修改了
- int main(void)
- {
- int n =10;
- const int *pt = &n;
-
- cout << "1)n= " << n << endl;
-
- *pt = 20;//通过指针修改了n的对象
- cout << "2)n= " << n << endl;
-
- return 0;
- }
结果如下:
报错说*pt是只读,所以不能通过const修改后的指针来修改原值
但是指针还是可以指向其他地方的,如下:
- #include <iostream>
-
- using namespace std;
-
-
- int main(void)
- {
- int n =10;
- int m = 100;
- const int *pt = &n;
-
- cout << "1)n= " << n << endl;
-
- //*pt = 20;//通过指针修改了n的对象
- pt = &m;
- cout << "*pt = " << *pt << endl;
- cout << "m = " << m << endl;
-
- return 0;
- }

还是可以指向其他位置,比如m
1)n= 10
*pt = 100
m = 100
②int * const pt;
- #include <iostream>
-
- using namespace std;
-
- int main(void)
- {
- int n =10;
- int *const pt = &n;
-
- cout << "1)n= " << n << endl;
- *pt = 20;
- cout << "2)n = " << n << endl;
-
- return 0;
- }
这时候把*放到了const的前面,就可以通过pt指针修改原n值
结果:
1)n= 10
2)n = 20
再把pt指针指向m
- #include <iostream>
-
- using namespace std;
-
- int main(void)
- {
- int n =10;
- int *const pt = &n;
-
- cout << "1)n= " << n << endl;
- *pt = 20;
- cout << "2)n = " << n << endl;
-
- pt = &m;
-
- return 0;
- }

这时候就报错了,说明*const pt可以修改指向数值的值,但是只能指向一个值,不能指向任意变量
③const int *const pt;
这就说明了,你既不能通过指针修改内容,也不能指向其他位置
尽可能使用const
将指针参数声明为指向常量数据的指针有两条理由:
·这样可以避免由于无意间修改数据而导致的编程错误
·使用const使得函数能够处理const和非const实参,否则将只能接收非const数据
如果条件允许,则应将指针形参声明为指向const的指针
如果函数形参是const,那么不管函数是const或者非const,都能进行调用
如果函数形参是非const,那么如果函数是const,就不能进行调用
所以在使用的时候,最好给形参设置成const指针!
跟一维数组类似,假设
int date[3] [4] = {{1,2,3,4},{5,6,7,8},{2,4,6,8}};
int total =sum(data , 3),sum函数就是数组名+数组中的元素
data是一个数组名,有三个元素,第一个元素本身是数组,有4个int组成,因此data类型是指向由4个int组成的数组的指针,正确的原型如下:
int sum(int(*ar2)[4], int size))(指针的数组)
这声明了将4个指向int的指针组成的数组
而不是由一个指向由4个int组成的数组的指针
如:int ar2[4],(这是数组的指针)
二维数组函数写成下面这样子,可读性更强;
int sum(int ar2[] [4] , int size);
因此二维数组的遍历就需要循环嵌套,外层访问行,内层访问列
- int sum(int are[][4],int size)
- {
- int total = ;
- for(int r = 0; r < size; r++)
- {
- for(int c = 0;c <4; c++)
- {
- total += ar2[r][c];
- }
- }
- return total;
- }
可以用数组表示法的原因是:ar2指向数组的第一个元素,因此ar+r指向编号为r的元素,又因为元素本身又是数组,随意ar2[r]是由4个int组成的数组的名称,所以ar2[r][c]是4个int组成的数组中的一个元素(最简单的方法),必须对指针执行两次才能解除引用,如下:
ar2[r][c] == *(*(ar2+r)+c)
先对行指针+r,利用*取出行元素,就是数组,然后对数组名+c,就是针对这个数组偏移,然后再*取出元素
·char数组 char ghost[15] = "galloping";
·用引号括起来的字符串常量(字符串字面值)
·被设置为字符串的地址的char指针 char * str = "galumping"
看如下例子:使用一个函数来计算特定的字符再字符串中出现的次数
- #include <iostream>
-
- using namespace std;
-
- unsigned int c_in_str(const char *str, char ch);
-
- int main(void)
- {
- char mmm[15] = "minimum";//第一种字符数组的方式
- const char * wail = "ululate"; //第二种字符指针方式 //char * wail = (char *"ululate")也行
-
- unsigned int ms = c_in_str(mmm,'m');
- unsigned int us = c_in_str(wail,'u');
-
- cout << ms << " m characters in " << mmm << endl;
- cout << us << " u characters in " << wail << endl;
-
- return 0;
- }
- unsigned int c_in_str(const char *str, char ch)
- {
- unsigned int count = 0;
-
- while ((*str))
- {
- if(*str == ch)
- count++;
- str++;
- }
- return count;
- }

3 m characters in minimum
2 u characters in ululate
函数无法返回字符串,但可以返回字符串的地址,这样效率更高
下面定义一个buildstr()函数,返回一个指针,接收两个参数,一个字符一个数字。函数使用new创建一个长度和数字参数相等的字符串,然后将每个元素都初始化为该数组
- #include <iostream>
-
- using namespace std;
- char * buildstr(char c, int n);
-
- int main(void)
- {
- char ch;
- cout << "Enter a character: ";
- cin >> ch;
-
- int times;
- cout << "Enter a integer: ";
- cin >> times;
-
- char *ps = buildstr(ch,times);
- cout << ps << endl;
- 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];//new创建数组
- pstr[n] = '\0';//最后一个留给空字符
- for(int i = 0 ; i < n; i++)
- {
- pstr[i] = c;
- }
- return pstr;
- }

结果:
Enter a character: V
Enter a integer: 20
VVVVVVVVVVVVVVVVVVVV
++++++++++++++++++++-Done-++++++++++++++++++++
*
1)要注意new字符串的时候多留一位给我们的空格
2)new完了要记得delete
结构变量的行为更接近基本的单值变量,在这种情况下,函数将使用原始结构的副本,另外,函数也可以返回结构
与数组名就是数组的第一个元素的地址不同,结构名只是结构的名称,要获得结构的地址,必须使用地址运算符&
如果结构非常大,则赋值结构将增加内存要求,所以许多C程序员进行函数传递和返回,更喜欢指针来访问结构体的内容
C++提供了第三种选择——按引用类型
看如下程序:
- #include <iostream>
-
- using namespace std;
- const int Mins_per_hr = 60;
-
- struct travel_time
- {
- int hours;
- int mins;
- };
-
- travel_time sum(travel_time t1, travel_time t2);
- void show_time(travel_time t);
-
- int main(void)
- {
- travel_time day1 = {5,45};
- travel_time day2 = {4,55};
-
- travel_time trip = sum(day1,day2);
-
- cout << "Two days :" << trip.hours << " hours , " << trip.mins << " mins." << endl;//这样写还是有些复杂,再定义一个shouwtime函数
-
- travel_time day3 = {4,32};
- cout << "Three days :" ;
- 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_hr;
- total.hours = t1.hours + t2.hours + (t1.mins + t2.mins) / Mins_per_hr; // (t1.mins + t2.mins) / Mins_per_hr会自动取整的
-
- return total;
- }
- void show_time(travel_time t)
- {
- cout << t.hours << " Hours, " << t.mins << "Minutes." << endl;//没返回值
- }

结果:
Two days :10 hours , 40 mins.
Three days :15 Hours, 12Minutes.
*这里所有的参数类型都是结构体
定义两个结构,用于表示两种不同的描述位置的方法,然后开发一个函数,将一种格式转换为另一种格式,并显示结果
- #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(void)
- {
- rect rplace;
- polar pplace;
-
- cout << "Enter the x and y value: ";
- while (cin >> rplace.x >> rplace.y)
- {
- pplace = rect_to_polar(rplace);//将直角坐标系转为极坐标系,输入的一定是直角坐标系新建的
- show_polar(pplace);//要显示极坐标系,参数肯定是极坐标系
- cout << "Next two numbers(q to quit):";
- }
- 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);//atan2返回弧度,还需要转化为角度
- return answer;
- }
-
- void show_polar(polar dapos)
- {
- const double Rad_to_deg = 57.29577951;
- cout << "Distance = " << dapos.distance << endl;
- cout << "Angel = " << dapos.angle * Rad_to_deg << "degree" << endl;
- }

结果:
Enter the x and y value: 30
40
Distance = 50
Angel = 53.1301degree
Next two numbers(q to quit):-100
100
Distance = 141.421
Angel = 135degree
Next two numbers(q to quit):q
*
1)函数引用结构体类型,结构体类型应该放在最前面,不然报错
2)while (cin >> rplace.x >> rplace.y),如果输入成功
可以对上述的程序进行修改
- #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(const polar *pda);
-
- int main(void)
- {
- rect rplace;
- polar pplace;
-
- cout << "Enter the x and y value: ";
- while (cin >> rplace.x >> rplace.y)
- {
- //pplace = rect_to_polar(rplace);
- rect_to_polar(&rplace,&pplace);//两个值,第一个是直角坐标系的指针,第二个是极坐标系的指针
- //show_polar(pplace);
-
- show_polar(&pplace);//直接用地址就行了,没有返回值
- cout << "Next two numbers(q to quit):";
- }
- 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);//atan2返回弧度,还需要转化为角度
- }
-
- void show_polar(const polar *pda)
- {
- const double Rad_to_deg = 57.29577951;
- cout << "Distance = " << pda->distance << endl;
- cout << "Angel = " << pda->angle * Rad_to_deg << "degree" << endl;
- }

结果:
Enter the x and y value: 30
40
Distance = 50
Angel = 53.1301degree
Next two numbers(q to quit):-100
100
Distance = 141.421
Angel = 135degree
Next two numbers(q to quit):q
*
1)函数内部传递的是指针,参数是指针,没有返回值
string对象和结构更为相似,例如,可以将一个结构赋给另一个结构,也可以将一个对象赋给另一个对象。可以将结构作为完整实体传递给函数
下面的程序声明了一个string对象数组,并将该数组传递给一个函数显示内容:
- #include <iostream>
-
- using namespace std;
- const int SIZE = 5;
- void display(const string sa[], int n);
-
- int main(void)
- {
- string list[SIZE];
- cout << "Enter " << SIZE << " favorite food: " << endl;
- for (int i = 0; i < SIZE; i++)
- {
- cout << i+1 << ": ";
- getline(cin,list[i]);
- }
- cout << "Your list" << endl;
- 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;
- }
- }

结果:
Enter 5 favorite food:
1: break
2: milk
3: icecr
4: saled
5: cake
Your list
1: break
2: milk
3: icecr
4: saled
5: cake
假设 要使用一个array对象存储一年四个季度的开支,看如下程序:
- #include <iostream>
- #include <string>
- #include <array>
-
- 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(void)
- {
- array<double,Seasons> expenses;
-
- fill(&expenses);//把指针填充到array里面
- show(expenses);
-
- return 0;
- }
- void fill(array<double,Seasons> *pa)
- {
- for (int i = 0; i < Seasons; i++)
- {
- cout << "Enter " << Snames[i] << " expensens: ";
- cin >> (*pa)[i];//pa是array对象的指针,需要转化成值再进行操作
- }
- }
- void show(array<double,Seasons> da)
- {
- double total = 0.0;
- cout << "EXPENSENS: " << endl;
- for (int i = 0; i < Seasons; i++)
- {
- cout << Snames[i] << ": $" << da[i] << endl;
- total += da[i];
- }
- cout << "Total expenses: " << total << endl;
- }

结果:
Enter Spring expensens: 212
Enter Summer expensens: 256
Enter Fall expensens: 208
Enter Winter expensens: 244
EXPENSENS:
Spring: $212
Summer: $256
Fall: $208
Winter: $244
Total expenses: 920
*cin >> (*pa)[i];//pa是array对象的指针,需要转化成值再进行操作
C++函数又一个特点——可以自己调用自己(但是C++不允许main()调用自己),这种功能被称为递归。
如果递归函数调用自己,则被调用的函数也将调用自己,这将无限循环下去,除非代码又包含终止的内容,一般放在if语句中,如下:void类型的递归函数recurs()代码如下:
- void recurs(argumentlist)
- {
-
- statements1
- if(test)
- resurs(arguments)
- statements2
- }
看如下的例子:
- #include <iostream>
-
- using namespace std;
- void countdown(int n);
-
- int main(void)
- {
- countdown(4);
- return 0;
- }
-
- void countdown(int n)
- {
- cout << "counting down.... " << n << endl;
- if(n>0)//更新条件,递归退出条件
- {
- countdown(n-1);
- }
- cout << n << ": Kandom" << endl;
- }

结果:
counting down.... 4
counting down.... 3
counting down.... 2
counting down.... 1
counting down.... 0
0: Kandom
1: Kandom
2: Kandom
3: Kandom
4: Kandom
*
1)当n递减到0的时候才开始执行下面的代码:cout << n << ": Kandom" << endl;
2)第二个部分将于与函数调用相反的顺序执行5次!
3)每次调用递归都会创建自己的一套变量,因此当程序到第五次调用时,将有5个独立的n变量,其中每个变量的值都不同,对程序如下修改!
- #include <iostream>
-
- using namespace std;
- void countdown(int n);
-
- int main(void)
- {
- countdown(4);
- return 0;
- }
-
- void countdown(int n)
- {
- cout << "counting down.... " << n << "(n at adress: " << &n << ")" << endl;
- if(n>0)//更新条件,递归退出条件
- {
- countdown(n-1);
- }
- cout << n << ": Kandom" << "(n at adress: " << &n << ")" << endl;
- }

结果如下:
counting down.... 4(n at adress: 0x1d3adffc10)
counting down.... 3(n at adress: 0x1d3adffbe0)
counting down.... 2(n at adress: 0x1d3adffbb0)
counting down.... 1(n at adress: 0x1d3adffb80)
counting down.... 0(n at adress: 0x1d3adffb50)
0: Kandom(n at adress: 0x1d3adffb50)
1: Kandom(n at adress: 0x1d3adffb80)
2: Kandom(n at adress: 0x1d3adffbb0)
3: Kandom(n at adress: 0x1d3adffbe0)
4: Kandom(n at adress: 0x1d3adffc10)
在需要将一项工作不断分为两项较小、类似的工作,递归很有用。递归方法有时被称为分而治之策略,比如对标尺找到两端,找到终点并标记,然后同样的操作给标尺的左右两部分继续用。
见如下程序:
- #include <iostream>
-
- using namespace std;
- const int Len = 66;
- const int Divs = 6;
- void subdivide(char ar[], int low, int high,int levels);
-
- int main(void)
- {
- char ruler[Len];//字符数组
- for(int i = 0; i < Len; i++)
- ruler[i] = ' ';//清空数组
-
- int min = 0; //数组边界开始
- int max = Len - 2; //数组边界结束
- ruler[Len-1] = '\0';
- ruler[min] = ruler[max] = '|';
-
- for(int i = 1; i < Divs; i++)
- {
- subdivide(ruler,min,max,i);//这样使用递归好像就可以了
- cout << ruler << endl;
- }
-
- return 0;
- }
- void subdivide(char ar[], int low, int high,int levels)//levels是为了控制递归退出
- {
- if(levels == 0)
- return;
-
- int mid = (low + high) / 2;
- ar[mid] = '|';
-
- subdivide(ar,low,mid,levels-1);
- subdivide(ar,mid,high,levels-1);
- }

结果:
| | |
| | | | |
| | | | | | | | |
| | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
*
1)刚开始是两边两条斜杠,第二次变成三条,第三次变成5条,一直递归下去,
2)将递归次数写进了形参,然后形成判断条件
与数据项相似,函数也有地址。函数的地址是存储机器语言代码的内存的开始地址。我们可以编写一个将另外一个函数的地址作为参数的函数,这样第一个函数能找到第二个函数并运行,特殊的是,它允许在不同的时间传递不同函数的地址,这意味着可以在不同的时间使用不同的函数
比如我们要设计一个estimate()函数来估算指定行数的代码需要的时间,并希望不同的此恒许愿都将使用该函数。我们将程序员要使用的算法函数地址传递给esitmate(),为此必须:
·获取函数地址
·声明一个指针
·使用函数指针调用函数
1)获取函数地址
使用函数名即可,不用参数,比如说think()是函数,则think是该函数的地址
2)声明函数指针
比如:函数如下
double pan(int);则指针如下
double (*pf)(int);
写的时候直接把原型中的函数名用(*Name)替换了就行了
*一定要加(),不然就是函数名
double (*pf)(int);这时函数指针
double *pf(int),这就是一个函数了
3)使用指针调用函数
现在(*pf)扮演的角色就与函数名相同,因此使用(*pf)时,可将它看作是函数名即可!
例如:
double pam(int);
double (*pf)(int);
pf = pam;
下面的程序演示如何使用函数指针,两次调用estimate()函数,依次传递besty()函数的地址,另一次传递pam()函数的地址。如下
- #include <iostream>
-
- using namespace std;
- double Rick(int lines);
- double Jack(int lines);
- void estimate(int lines, double(*pf)(int));
-
- int main(void)
- {
- int code;
-
- cout << "How many lines of code do you need?\n";
- cin >> code;
-
- cout << "Here is Rick's estimate: " << endl;
- estimate(code,Rick);
-
- cout << "Here is Jack's estimate: " << endl;
- estimate(code,Jack);
-
- return 0;
- }
- double Rick(int lines)
- {
- return lines * 0.05;
- }
-
- double Jack(int lines)
- {
- return (0.03 * lines) + (0.0004 * lines * lines);
- }
-
- void estimate(int lines, double(*pf)(int))
- {
- cout << lines << " Lines code will take " << (*pf)(lines) << " hours." << endl;
- }

结果:
How many lines of code do you need?
100
Here is Rick's estimate:
100 Lines code will take 5 hours.
Here is Jack's estimate:
100 Lines code will take 7 hours.
*
可以看到在最后一行代码我们声明了和Rick,Jack类型相同的函数指针*pf,使用的时候直接用名字参数即可: (*pf)(lines) << " hours." << endl;
看下例:
- #include <iostream>
-
- using namespace std;
- const double *f1(const double *ar,int n);
- const double *f2(const double ar[],int n);
- const double *f3(const double ar[],int n);
-
- int main(void)
- {
- double av[3] = {1112.3,1542.6,2227.9};
-
- //part1: p1是指向函数的指针
- const double *(*p1)(const double *,int) = f1;//第一种方法,使用传统方法进行给指针赋值
-
- auto p2 = f2; //第二种方法:使用auto方法,让编译器自动判断p2的类型
-
- cout << "PART1:--------------------" << endl;
- cout << "Adress Value" << endl;
- cout << (*p1)(av,3) << ": " << *((*p1)(av,3)) << endl; //(*p1)(av,3) == f1(av,3),第二个是把该函数内容再显示出来
-
- cout << p2(av,3) << ": " << *p2(av,3) << endl;//第二种方法,直接把指针名称写出来就行了
-
- //part2: p2是由指针构成的数组
- const double *(*pa[3])(const double *,int) = {f1,f2,f3};//指针数组的内容是函数,注意它的声明方法
-
- auto pb = pa;//这样pb也是函数指针数组
-
- cout << "PART2:--------------------" << endl;
- cout << "Adress Value" << endl;
- for(int i = 0;i < 3; i++)
- cout << pa[i](av,3) << ": " << *pa[i](av,3) << endl;//pa[1]就是f1了,加*就是它里面的内容
- for (int i = 0; i < 3; i++)
- {
- cout << pb[i](av,3) << ": " << *pb[i](av,3) << endl;
- }
-
- //part3:
- //(pc)pd 是指针,指向了一个由函数指针构成的数组
- auto pc = &pa;//pc是指向由三个函数指针作为元素的数组pa的指针
- const double *(*(*pd)[3])(const double *,int) = &pa;//传统方法,首先pd是指针,就是*pd,然后*pd是3个元素数组指针,就是*(*pd)[3],最后每个元素都是函数指针,就有了这个
-
- cout << "PART3:--------------------" << endl;
- cout << "Adress Value" << endl;
- cout << (*pc)[0](av,3) << ": " << *(*pc)[0](av,3) << endl;
-
- const double *pdb = (*pd)[1](av,3);
- cout << pdb << ": " << *pdb << endl;
-
- cout << (*pd)[2](av,3) << ": " << *(*pd)[2](av,3) << endl;
- cout << (*(*pd)[2])(av,3) << ": " << *(*(*pd)[2])(av,3) << endl;//这两种写法其实是一样的
-
- return 0;
- }
- const double *f1(const double *ar,int n)
- {
- return ar;
- }
- const double *f2(const double ar[],int n)
- {
- return ar + 1;
- }
- const double *f3(const double ar[],int n)
- {
- return ar + 2;
- }

结果:
PART1:--------------------
Adress Value
0xf0e7dff6a0: 1112.3
0xf0e7dff6a8: 1542.6
PART2:--------------------
Adress Value
0xf0e7dff6a0: 1112.3
0xf0e7dff6a8: 1542.6
0xf0e7dff6b0: 2227.9
0xf0e7dff6a0: 1112.3
0xf0e7dff6a8: 1542.6
0xf0e7dff6b0: 2227.9
PART3:--------------------
Adress Value
0xf0e7dff6a0: 1112.3
0xf0e7dff6a8: 1542.6
0xf0e7dff6b0: 2227.9
0xf0e7dff6b0: 2227.9
*总结一下
1)
(*pa[2])(av,3) ≠ *pa[2](av,3),括号优先级不一样
pa[2](av,3)来说,pa[2]是指针数组第三个元素,就是函数指针,最后*,就是第三个函数的值
(*pa[2])(av,3)来说,(*pa[2])就是第三个元素函数指针所指向的函数指针,然后赋值,出来的还是地址
2)
对于函数指针来说*pf和pf其实是等价的
3)
请注意pa和&pa的区别(pa是数组),pa是数组第一个元素的地址,即&pa[0],而&pa是整个数组(三个指针)的地址,从数字上说它们相同,但是它们类型不同。
比如pa+1是下一个元素的地址,而&pa+1是后面12个字节内存块的地址。
还有个差别,要得到第一个元素的值,只需要对pa解除即可,而&pa解除两次引用
**&pa == *pa == pa[0]
4)
用auto解放了复杂的声明过程
const double *(*pa[3])(const double *,int) = {f1,f2,f3};
auto pb = pa;//这样pb也是函数指针数组
5)
还可以用typedef进行简化(并没有定义新的东西,而是起了一个别名,第五章说过)
比如:typedef int n,这里就把n变成int类型了
比如:typedef double real;这里real就变成了double类型了,而不是变量
实例如下:
typedef const double *(*p_fun) (const double *,int),可以直接用p_fun类型了
p_fun p1 = f1;
p_fun pa[3] ={f1,f2,f3};
1:使用函数的三个步骤?
定义,调用,声明
2:请创建与下面的描述匹配的函数原型
a:igor()没参数,没返回值
void igor(void);
b:tofu()接收一个int参数,且返回一个float
float tofu(int)
c:mpg()接收两个double,返回一个double
double mpg(double,double)
d:summation()将long数组名和数组长度作为参数,返回一个long
long arr[n];
long summation(long [],int)
e:doctor()接受一个字符串参数(不能修改),并返回double
double doctor(const char *str)
f:ofcourse()将boss结构作为参数,无返回值
void ofcourse(boss bs) 形参名可写可不写
g:plot将map结构的指针作为参数,返回字符串
char *plot(map *pt)
3:编写一个接受3个参数的函数,:int数组名、数组长度、和一个int值 ,并将数组所有元素都设置为该int值
void Set(int ar[], int n, int x)
{
for(i = 0;i<n;i++)
{
ar[i] = x;
}
}
4:编写一个接受三个参数的函数:指向数组区间中第一个元素的指针、指向数组区间最后一个元素后面的指针以及一个int值,并将数组中的每个元素都设置为该int值
- void Set(int *begin,int *end , int x)
- {
- for (int *pt = begin; pt != end ; pt ++ )
- {
- *pt = x;
- }
- }
*注意循环条件
5:编写一个double数组名和数组长度作为参数,并返回该数组中最大值的函数。该函数不应该修改数组内容。
- double Set(const double ar[],int len)
- {
- double max = arr[0];
- for(int i = 1; i < size; i++)
- {
- if(max < arr[i]);
- max = arr[i];
- }
- return max;
- }
6:为什么不对基本类型的函数参数使用const?
我们的C++一般是按值传递参数,而不像数组和结构这种,整体复制,修改副本其实对原来的值不影响,但是如果用指针了就必须加const
7:C++使用哪三种C风格的字符串
char数组。char ch[] = "Hello";
字符串。"hello;";
可以指向字符串首字符的指针。char * pt = "Hello.指的是地址
8:编写一个函数,原型如下:
该函数将字符串中所有的c1都替换为c2,并返回替换次数
int replace(char * str, char c1,char c2)
- int replace(char * str,char c1,char c2)
- {
- int count;
- while(*str)
- {
- if(*str == c1)
- {
- *str = c2;
- count++;
- }
- str++;
- }
- return count;
- }
9:表达式*"pizza"代表:字符串首地址,取出p
"taco[2]"呢,taco是数组,第三个元素,双引号就是首地址
10:C++允许按值传递结构,也允许传递结构的地址。如果glitz是一个结构变量,如何按值传递它?如何传递地址?两种方法有何利弊?
地址传递不能保护原始数据;但是按值传递内存消耗大
11:函数judge()返回类型是int,它将这样一个函数地址作为参数:将const char指针作为参数,并返回int值,写该函数原型
int judge(int (*pf) (const char));
12:结构如下:
struct applicant
{
char name[30];
int ratings[3];
}
a:编写一个显示该结构体内容的函数,参数就是结构体
- void show(applicant ap)
- {
- for (int i = 0; i < 2; i++)
- {
- cout << ap.name[i] << ap.ratings[i] << endl;
- }
-
- }
b.将该结构的地址作为参数,显示参数所指向结构的内容
- void show(applicant *ap)
- {
- for (int i = 0; i < 2; i++)
- {
- cout << ap->name << ap->ratings[i] << endl;
- }
- }
13:假设f1()和f2()原型如下:
void f1(applicant * a);
const char * f2(const applicant * a1,const applicant * a2);
1)请将p1和p2分别声明为指向f1和f2的指针;
2)将ap声明为数组,它包含5个类型与p1相同的指针
3)将pa声明为一个指针,它指向的数组包含10个类型与p2相同的指针
使用typedef方法来实现。
1)
- typedef void *(pf_1)(applicant * a);//使用typedef直接变成类型
- pf_1 p1 = f1;
-
- typedef char const char *(*p_f2)(const applicant *a1,const applicant *a2);
- p_f2 p2 = f2;
2)pf_1 ap[5];
3p_f2 (*pa) [10];
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。