C/C++ 第九讲 函数(续)

9.1 返回指针值的函数

  • 函数的返回值可为变量地址、数组名或指针变量。

  • 在说明/定义返回值为指针值的函数时,只需在函数前加一指针类型说明符。

    例:

    1
    2
    float *fun(float x[],float y);
    char *strcat1(char *strDest,const char *strSource)

例:编一函数,将这字符串中ASCII码最大的字符的地址返回,主程序中输出最大数之后的所有数。

分析:

  1. 如何定义函数的类型?

“将这字符串中ASCII码最大的字符的地址返回”,函数应为指针类型。

char *getmax(char s[])

  1. 如何表示字符串中ASCII码最大的字符的地址?

用该数组元素地址 &s[imax]s+imax表示,imax:该字符在数组中的位置。

e.g:

1
2
3
4
5
6
7
8
9
10
11
char *getmax(char s[])
{
int i=1,imax=0;
while(s[i]!='\0')
{
if(s[i]>s[imax])
imax=i;
i++;
}
return s+imax; //return &s[imax];
}

调用:

1
2
3
char *max; //赋给指针类型的变量。
max=getmax(s);
puts(max); //puts()里面是地址,所以可以”puts(i+imax)“
  • 返回值为指针的函数,要注意返回值的指向。

9.2 作用域与存储类别

作用域与生存期的概念

  • 作用域(可见性):在何范围内可被访问;
  • 生存期:在什么 时间内存在。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "iostream"
using namespace std;
int digit(long x)
{
int k=0;
while (x!=0);
return k; //digit函数中x的作用域
}
int main()
{
long x;
cin>>x;
cout<<digit(x)<<endl;
system("pause");
return 0; //main函数中x的作用域
}//整个程序:x的生存期

根据作用域和生存期,变量可分为:

  • 全局变量(外部变量):
    • 在函数外部定义的变量
  • 周期变量(内部变量):
    • 在函数内部定义的变量,包括形式参数

存储类型:

  • auto

    • 局部,生存期:定义它的分程序内
  • static

    • 局部,生存期
  • 外部变量

自动局部变量

  • 存储类型auto,可缺省
  • 作用域:定义点开始到所在的分程序结束。
  • 生存期:所在分程序执行期间。
  • 开始执行分程序就生成,执行结束就消亡。
  • 初始化:缺省值为随机值。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "iostream"
using namespace std;
int f(int x)
{
int k=0;
while (x!=0);
return k; //digit函数中x的作用域
}
int main()
{
long x;
cin>>x;
cout<<digit(x)<<endl;
system("pause");
return 0; //main函数中x的作用域
}

自动局部变量示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "iostream"
using namespace std;
int f(int x)
{
x++;
int k=5; //auto int k=5;
k++;
return x+k;
}
int main()
{
int k=2; //auto int k=2;
cout<<f(k)<<endl;
cout<<f(k)<<endl;
system("pause");
return 0;
}

问题:

  • f函数中的k和main函数中的k会冲突吗?

答:不会冲突。不同的函数内可使用相同名称的自动局部变量,它们占用不同的存储单元,只能在各自的函数内被使用,彼此互不干扰。

  • main函数中两次调用f函数,第二次调用时,f函数中的x,k和第一次调用时使用相同的内存单元吗?

答:若一个函数被多次调用,每一次函数调用时,系统都会为自动局部变量重新分配存储单元,并重新初始化。函数调用结束后,释放为自动局部变量分配的存储单元,函数中自动局部变量的值不会带到下一次调用中

静态局部变量

  • 存储类别static
  • 作用域:定义点开始到所在的分程序结束。
  • 生存期:程序的整个执行周期。
  • 初始化:可初始化,缺省值为0或’\0’

静态局部变量VS自动局部变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void test()
{
int i=0;
static int j=0;
i++;j++;
cout<<"i="<<i<<"j="<<j<<endl;
}
int main()
{
for(int i=0;i<2;i++)
test();
system("pause");
return 0;
}
  • 自动局部变量i,每次调用函数都重新为他分配存储单元并初始化。
  • 静态局部变量j,只在第一次调用函数时分配存储单元并初始化。

静态局部变量特点:

  • 只在第一次调用函数时为静态局部变量分配存储空间,函数调用结束不会释放为静态局部变量分配新的存储空间。
  • 静态局部变量只初始化一次
  • 若一个函数被调用多次,对静态局部变量而言,前一次调用的结果会带到下一次去。

问:

第一次调用test结束,返回main函数后,j的存储空间没有释放,在main函数能访问j吗?

答:在一个分程序中定义的静态局部变量,在其他分程序中,是生存期,不是作用域,不可访问。

全局变量

  • 作用域:定义点开始到所在的文件结束。
  • 生存期:程序的整个执行周期。
  • 初始化:可初始化,缺省值为0或’\0’

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "iostream"
using namespace std;
int m=10;//全局变量m
void f1(int n) //形参n(局部变量),传值参数,在f1中的变化并不改变真正的n
{
n=2*n;m=m/3;
}
int n; //全局变量n
void f2()
{n=5;m++;++;}
int main()
{
int n=2; //局部变量n
f1(n);f2();
cout<<m<<" "<<n<<endl;
system("pause");
return 0;
}

image-20231102103809883

注意:

  • 当全局变量与局部变量同名且作用域有重叠时,在局部变量的作用域内, 起作用的是局部变量,而全局变量被“屏蔽”掉。

image-20231102104244452

全局变量可在多个函数内使用,前一次的计算结果会带到下一次使用中。

扩展全局变量的作用域

  • 全局变量的作用域:定义点开始到所在的文件结束。
  • 可用extern关键字扩展全局变量作用域。

扩展到定义点以前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;
//extern int i
void fun()
{
//extern int i
cout<<i;
}
int i=5;
int main()
{
int j=20;
cout<<j;
fun();
system("pause");
return 0;
}

作用域横向扩展

  • 1个C/C++项目可包含多个源文件
  • 在一个源文件中要引用另一个源文件中定义的全局变量,使用前须用extern声明符对其进行扩展

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//file1.cpp
int max1,min1;
void maxmin(int x[],int n)
{
max1=x[0];min1=x[0];
for(int i=0;i<n;i++)
{
if(x[i]>max1)max1=x[i];
if(x[i]<min1)min1=x[i];
}
}
//file2.cpp
#include ……
extern int max1,min1;
int main()
{
int a[10]={11,2,3,-4,5,6,7,8,0,20};
maxmin(a,10);
cout<<max1<<" "<<min1<<endl;
system("pause"); return 0;
}

9.3 递归函数 (考试只考概念,不考编程)

递归的概念

用自身的结构描述自身

例:递归的阶乘定义

n!=n*(n-1)!1 (n>0)

递归反映问题分解的思维。

递归函数

函数在函数体内直接或间接地调用自身,称递归函数。

  • 直接递归
  • 间接递归:函数通过其他函数间接调用自身

例1:求n!

思路:达到终结条件,直接求解;未达终结条件,递归调用。

1
2
3
4
5
6
7
8
9
10
11
long fac(int n)
{
if (n==0)
return 1;
return (n*fac(n-1));
}
int main()
{
cout<<fac(3)<<endl;
system("pause"); return 0;
}

例2:计算斐波那契数列

f(n)=f(n-1)+f(n-2)

1
2
3
4
5
6
7
int fib(int n)
{
if(n==0n==1)
return 1;
else
return (fib(n-1)+fib(n-2));
}

注意递归函数一定要有终结条件

递归与迭代

任何用递归解决的问题都可以用迭代非递归解决。

例1:求n!

1
2
3
s=1;
for(i=1;i<=n;i++)
s=s*i;

迭代算法效率高。

根据问题特征决定用迭代还是递归

递归使用

例:二分法查找

终结条件:

  • key==a[mid]
  • low>high
1
2
3
4
5
6
7
8
9
10
11
int Binary_Search(int low,int high,int key)
{
if(low>high)
return -1;
if(key==a[mid])
return mid;
else if(key<a[mid])
return Binary_Search(low,mid-1);
else
return Binary_Search(mid+1,high);
}

例:进制转换

1
2
3
4
5
6
7
8
9
void convert(int m,int n)
{
char b[17]="0123456789ABCDEF";
if(m!=0)
{
convert(m/r,r);
cout<<b[m%r];
}
}

作业

1

编写递归函数int sum(int a[],int n),其功能是求长度为n的数组的累加和,在主函数中随机产生10个两位数,调用sum函数,求这10个数的和。

1
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
#include "iostream"
#define N 256
#include<time.h>

using namespace std;
//sum1:迭代
int sum1(int a[], int n)
{
int sos=0;
for (int i = 0; i < n; i++)
sos += a[i];
return sos;
}
//sum2:递归
int sum2(int a[], int n)
{
int sos(0);
if (n > 0)
sos = a[n - 1] + sum2(a, n - 1);
if (n == 0)
return sos;
}
int main()
{
int n,i,s[N],sos1,sos2;
for (i = 0; i < 10; i++)
{
s[i] = 10 + rand() % 89;
}
for (i = 0; i < 10; i++)
{
cout << s[i] << ' ';
}
cout << endl;
sos1 = sum1(s, 10);
sos2 = sum2(s, 10);
cout << "10个数迭代求得之和为:" << sos1 << endl;
cout << "10个数递归求得之和为:" << sos2 << endl;
system("pause");
return 0;
}

2

编写函数get_max,其功能是将字符串s中最大字符的地址返回,再编写一个主函数,调用该函数,将字符串s中从最大字符开始的子串中小写字母转换成大写字母,然后输出新字符串s。例如,假设s的内容为“qwertyou”,则从最大字符’y’开始的子串为“you”,处理后的s为“qwertYOU”。

函数形式为:char *get_max(char s[])

1
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
#include "iostream"
#define N 256
using namespace std;
char* get_max(char s[])
{
int imax=0;
for (int i=0;s[i]!='\0'; i++)
{
if (s[i] > s[imax])
imax = i;
}
return &s[imax];
}

int main()
{
int n,i;
char* imax;
char a[N];
gets_s(a);
imax = get_max(a);
for (i = 0;*(imax+i)!='\0'; i++)
*(imax + i) -= 32;
puts(imax);
system("pause");
return 0;
}

类似的课上例:

【例5.9】search函数的功能是在一字符串中查找某一指定的字符,若找到,则返回字符串中这个字符的地址,否则返回NULL,主函数调用search函数,并输出查找到的那个字符后的子串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
using namespace std;
char *search(char s[],char ch)
{
int i=0;
while( )
if(s[i]!=ch)
i++;
else
;
return NULL;
}
int main()
{
char s[100],ch,*p;
gets(s);
cin>>ch;
;
if(p!=NULL)
cout<<p<<endl;
else
cout<<"not found\n";
system("pause");
return 0;
}