# C++基础7-函数1
本文是《C++ Primer Plus》的笔记,本文中的案例均自己实践过,如需转发请在转发开头贴上原文地址,谢谢!
# 函数
# 函数的概念
函数可以想象成建筑施工中的一个门,一扇窗,门有门把手和门板,窗有玻璃和木框,相应的它在程序上一个逻辑上的模块,从外部来说,关注一个函数会关注它的输入输出,从内部来说,便是这个函数的内部处理细节。在C++中,它自带一个包含函数的大型库(标准ANSI库加多个C++类),但是如果想要更高效率地编程,还需要深入学习STL和BOOST C++两个大库。
# 函数的定义
returnType functionName(parameterList){
语句块;
return;
}
2
3
4
C++的返回值类型不能是数组,除此之外可以是任何类型:整数,浮点数,指针,甚至是结构和对象!
# 函数原型和调用
函数原型一般是函数头去掉语句块,后面添加分号。
为什么需要原型呢?原型描述了函数到编译器的接口,将函数返回值的类型以及参数的类型和数量告诉编译器。如果没有原型,编译器在编译的时候需要在文件中查找,这样效率很低,当函数不存在文件中时这将也是一个大问题。避免使用原型唯一方法就是在首次使用函数之前定义它,但C++的编程风格是main()放在最前面,因为它通常提供了程序的整体结构。
long double calcSeries(int n);
int main(int argc,char *argv[])
{
cout << calcSeries(3) << endl;
}
long double calcSeries(int n) {
long double result = 1;
for (int i = 1; i <= n; i++)
{
result *= i;
}
return result;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 函数和数组
如前面在指针一节中指出,数组和指针在函数的实参和形参中是等价的。另外数组作为形参时,不要试图使用方括号表示法来传递数组长度:
void test(int arr[10])
看下面的事例:
void printArr(int arr[], int n) {
for (int i=0;i<n;i++)
{
cout << arr[i] << endl;
}
}
void printArr(int * beginPtr, int * endPtr) {
while (beginPtr != endPtr) {
cout << *beginPtr << endl;
beginPtr++;
}
}
int main(int argc,char *argv[])
{
int a[10] = { 1,2,3,4,5,6,7,8,9 };
printArr(a + 3, 3);
cout << "------------" << endl;
printArr(a + 2, a + 8);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
如果希望传入的数组不想改变的话,可以在数组前面加const。
# 函数和二维数组
//int sum(int(*arr)[3], int size) {
//注意下面不是一个数组而是一个指针。
int sum(int arr[][3], int size) {
int sumResult = 0;
for (int i = 0; i < size;i++) {
for (int j = 0; j < 3; j++) {
sumResult += arr[i][j];
}
}
return sumResult;
}
int main(int argc,char *argv[])
{
int data[2][3] = {{1,2,3},{4,5,6}};
cout << sum(data , 2);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 函数和C风格字符串
C风格字符串有三种:
- char数组
- 用括号括起的字符串常量
- 指向字符串的char指针
int printStr(char * str) {
int sum = 0;
while (*str++) {
sum++;
}
return sum;
}
char * buildStr(char ch,int n) {
char * returnPtr = new char[n + 1];
returnPtr[n] = '\0';
while (n-- > -1) {
returnPtr[n] = ch;
}
return returnPtr;
}
void printStr(string a1) {
cout << a1 << endl;
}
int main(int argc,char *argv[])
{
//char cstr1[5] = "hello";
//这种情况会报错,因为没有空间在末尾添加'\0'
char cstr1[6] = "hello";
cout << strlen(cstr1) << endl;
cout << sizeof(cstr1)/sizeof(cstr1[0]) << endl;
char cstr2[5] = { 'h','e','l','l','o' };
//这种情况就不会报错,但是末尾没有'\0'
cout << strlen(cstr2) << endl;
char cstr3[6] = { 'h','e','l','l','o' };
//如果空间大于初始化字符集个数的话,那么会自动添加'\0'
cout << strlen(cstr3) << endl;
cout << printStr(cstr2) << endl;
cout << printStr(cstr3) << endl;
string str1 = "hello";
const char * str1Ptr = "hello";
//需要在char前面添加const
cout << strlen(str1Ptr) << endl;
cout << buildStr('a', 10) << endl;
char t1[20] = "hello world!";
printStr(t1);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
c++中string类定义了从char *到string的转换功能,可以使用C风格字符串来初始化string对象。
# 函数和结构
struct personInfo {
const char * name;
int age;
};
personInfo * dealpersonInfo(personInfo * ptr1,const char * name) {
ptr1->name = name;
ptr1->age = 20;
return ptr1;
}
void dealPersonRef(personInfo & ptr1, const char * name) {
ptr1.name = name;
ptr1.age = 25;
}
int main(int argc,char *argv[])
{
personInfo p1;
personInfo * p2 = dealpersonInfo(&p1, "xie");
cout << p2->name << " " << p2->age << endl;
dealPersonRef(p1,"xie2");
cout << p2->name << " " << p2->age << endl;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 函数和string对象
void display(const string list[],int n) {
for (int i = 0; i < n; i++) {
cout << list[i] << endl;
}
}
int main(int argc,char *argv[])
{
const int SIZE = 3;
string list[SIZE];
for (int i = 0; i < SIZE; i++) {
getline(cin, list[i]);
}
display(list, SIZE);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 函数的递归
递归是通过已知和未知之间的联系来不断推断,直到已知情况再推回结果。比较常见的一个例子:在电影院看电影的时候我们假如我们只能看到前一排,假如我们想知道自己是第几排,那么我们会问前面的人,前面的人如果也不知道的话再问前面的人,直到问到第一排,第一排告诉第二排,这是第一排,第二排告诉第三排这是第二排。。。直到我们自己前面那排告诉我们他是第几排。
在函数中递归的形式:
void recursiveFunc(argumentlist){
语句块1;
if(test)
recursiveFunc(arguments);
语句块2;
}
实例:
void countLevel(int n) {
cout << "asking level :" << n << endl;
if (n > 1) {
countLevel(n-1);
}
cout << "this is :" << n << "st level stair." << endl;
}
int main(int argc,char *argv[])
{
countLevel(5);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
函数中的多个递归调用:
void printArr(char * arr, int n) {
for (int i = 0; i < n; i++) {
cout << arr[i];
}
cout << endl;
}
void divideStr(char arr[],int left,int right) {
if ((right - left) > 1) {
int middle = (right - left) / 2 + left;
if ((right - left) % 2 == 0) {
arr[middle] = '|';
divideStr(arr,left,middle);
divideStr(arr,middle,right);
}else{
arr[middle] = arr[middle + 1] = '|';
divideStr(arr, left, middle);
divideStr(arr, middle+1,right);
}
}
}
void divideStr2(char arr[], int left, int right,int deep) {
if (deep > 0) {
int middle = (right - left) / 2 + left;
deep--;
if ((right - left) % 2 == 0) {
arr[middle] = '|';
divideStr2(arr, left, middle,deep);
divideStr2(arr, middle, right,deep);
}
else {
arr[middle] = arr[middle + 1] = '|';
divideStr2(arr, left, middle,deep);
divideStr2(arr, middle + 1, right,deep);
}
}
}
int divideStr3(char arr[], int left, int right, int deep=1) {
if ((right - left) > 1) {
int middle = (right - left) / 2 + left;
deep++;
if ((right - left) % 2 == 0) {
divideStr3(arr, left, middle, deep);
//divideStr3(arr, middle, right, deep);
return divideStr3(arr, middle, right, deep);
}
else {
divideStr3(arr, left, middle, deep);
//divideStr3(arr, middle + 1, right, deep);
return divideStr3(arr, middle + 1, right, deep);
//注意,在visual studio 2017中,将上面的话换成上面注释的行数,不会报错,正常运行,但是如果运行下面的 cout << deep << endl; 则会导致出错。使用其他的编译器不知道会不会有这个问题。
}
//cout << deep << endl;
}
else {
return deep;
}
//cout << deep << endl;
}
int main(int argc,char *argv[])
{
const int arrLen = 40;
char arr[arrLen];
arr[0] = arr[arrLen - 1] = '|';
for (int i = 1; i < arrLen-1; i++) {
arr[i] = ' ';
}
//divideStr(arr, 0, arrLen - 1);
//printArr(arr,arrLen);
int totalDivide = divideStr3(arr, 0, arrLen - 1);
cout <<"output:"<< totalDivide << endl;
for (int i = 0; i < totalDivide; i++) {
divideStr2(arr, 0, arrLen - 1, i);
printArr(arr,arrLen);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# 函数参数解析
# 例1:
int addTarget(int &n) {
n += 1;
return n;
}
int multiplyTarget(int &n) {
n *= 2;
return n;
}
int main(int argc,char *argv[])
{
int intTest = 1;
cout << multiplyTarget(intTest) << ":" << addTarget(intTest) << endl;
cout << addTarget(intTest) << ":" << multiplyTarget(intTest) << endl;
}
在c++ 14中得到结果:
4:2
9:8
在c++ 17中得到结果:
2:3
4:8
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
结论:
- cout链式计算在c++ 14中是从右往左计算,在c++ 17中是从左往右计算。
- 他们都是从左往右输出。
# 例2:
int add(int a, int b, int c)
{
cout << a << ":" << b << ":" << c << endl;
return a + b + c;
}
int main(int argc,char *argv[])
{
auto i = 0;
cout << add(i++, i, ++i) << endl;
i = 0;
cout << add(++i, i, i++) << endl;
}
在c++ 14中得到结果:
1:2:2
5
2:2:0
4
在c++ 17中得到结果:
1:1:1
3
2:1:0
3
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
结论:
- 函数实参计算在两个标准中都是从右往左计算压栈。
- 在c++14中后置自增相当于将变量给一个临时变量,而前置自增相当于引用变量自身。当变量自增时,前置自增依旧会增加。
- c++14中是实参全部计算完再压栈,而c++17中是实参计算一个立即压栈。
# 函数指针
指向函数的指针,这部分将融合前面的指针数组,数组指针,函数指针,写一些比较复杂的表达式。
# 例1
int * getInt2(int a[]) {
(*a) *= 2;
return a;
}
int main(int argc,char *argv[])
{
int intInit = 1;
int * (*intPtr)(int *) = getInt2;
cout << intInit++ << ":" << intInit << endl;
cout << *intPtr(&intInit) << ":" << intInit<<endl;
cout << intInit << endl;
cout << *intPtr(&intInit) << endl;
//(* ptr)函数书写
cout << *(*intPtr)(&intInit) << ":" << *(*intPtr)(&intInit) << endl;
cout << intInit << endl;
}
在c++ 14中得到结果:
1:2
4:2
4
8
32:16
32
在c++ 17中得到结果:
1:2
4:4
4
8
16:32
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
由例1和例2能理解上面的结果,c++17输出的是我原来期望的结果。
# 例2
int * getInt1(int * a) {
(*a) *= 1;
return a;
}
int * getInt2(int a[]) {
(*a) *= 2;
return a;
}
int * getInt3(int * a) {
(*a) *= 3;
return a;
}
int main(int argc,char *argv[])
{
int intInit = 1;
int * (*intPtrArr[3])(int *) = {getInt1,getInt2,getInt3};
//这里不能使用auto,因为自动类型推断只能用于单值初始化,而不能用于初始化列表。
int * (*(*intPtrArr2)[3])(int *) = &intPtrArr;
//使用typedefine简化上面的步骤
typedef int * (*funPtr)(int *);
funPtr t1[3]= { getInt1,getInt2,getInt3 };
funPtr (*t2)[3]= &t1;
typedef int * (*funPtr2[3])(int *);
funPtr2 * t3 = &t1;
cout << *(*t3[0])(&intInit) << endl;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
← C++基础6-文件 C++基础8-函数2 →