一、函数递归

函数需要被另一个函数调用才能执行,而主函数main在程序运行时会被自动调用。其实函数也可以自己调用自己

#include<stdio.h>
void func(int n)
{if(n==5)//n为5时,结束递推
return;
printf("%d\n",n);
func(n+1);}//再将n值传回func,然后继续打印
int main()
{func(0);//被主函数调用,并且n为0,进入func函数
return 0;}

Android控制递归次数 递归 调试_#include

 

 蓝色线条画出了从下级回归的流程。箭头方向和标号数字代表执行顺序。流程在n小于5之前,一直递推至下级函数。当n为5时,从下级函数开始回归。

例题:用递归计算阶乘n!

#include<stdio.h>
int f(int n)
{if(n==0||n==1)
{return 1;}
return n*f(n-1);}
int main()
{int result=f(4);
printf("%d\n",result);
return 0;

递推流程中,可以确定当前的n分别为4,3,2,1。接着进入了递归调用f(n-1),直到n为1时,开始回归。

Android控制递归次数 递归 调试_#include_02

回归到n为2时,计算2 * 1。回归到n为3时,计算3 * (2 * 1)。回归到n为4时,计算4 * (3 * 2 * 1)

 

二、指针

1.cpu由三部分构成:

 1.算术、逻辑单元:对数据执行运算(例如加法、减法)的电路。2.控制单元:协调机器活动的电路。3.寄存器组:数据临时存储。

但是,寄存器能够存储的信息量很少,仅仅是当前工作所必要的。所以需要配合内存使用

Android控制递归次数 递归 调试_#include_03

 

因此记录一个数据对象在内存中的存储位置,需要两个信息:

1.数据对象的首地址。2.数据对象占用存储空间大小。

2.取地址运算符&

写在一个数据对象的左边,可以获取一个数据对象的首地址和所需存储空间大小。

3.声明指针类型的变量

声明一个指针:目标类型*变量名

int n;
int*pn=&n;//声明一个保存了int类型的首地址和大小的变量。
char c;
char*pc=&c;//声明一个保存了char类型的首地址和大小的变量。

指针的定义:设一个数据对象为x,设另一个数据对象为p。p存储了x的首地址和所占空间大小。那么,p称之为x的指针,或者说p指向x。

对于上面的代码:pn被称作n的指针,或者说pn指向n。pc被称作c的指针,或者说pc指向c。

首地址:事实上,指针类型的值就是目标数据对象的首地址。例如pn=12588464,那么这个值即为n的首地址

空间大小

//尝试让pn赋值为pc
编译出现了错误,无法将char*转换为int*

因为,C语言中通过不同的指针类型来标记目标数据对象的空间大小。

指向int的指针类型标识了目标对象的空间大小为sizeof(int)。指向char的指针类型标识了目标对象的空间大小为sizeof(char)。

如果尝试试将pc赋值给pn,虽然首地址可以正确地从pc赋值给pn。但是指针类型的变化导致数据长度的变化,因此无法进行自动转换。

 

三、使用指针

1.取值运算符*

①可以根据指针中存储的首地址和空间大小找到目标数据对象

int n=123;
int*pn=&n;
printf("%u\n",pn);//打印n的首地址
printf("%d\n",*pn);
结果为2636516
123

 

②也可以通过指针修改所指向的数据对象。

#include<stdio.h>
int main()
{int n=0;
int*pn=&n;
char c=0;
char*pc=&c;
//使用指针修改所指向数据对象
*pn=123;
*pc='A';
printf("n=%d\n",n);
printf("c=%c\n",c);
//使用指针访问所指向数据对象
printf("n=%d\n",*pn);
printf("c=%c\n",*pc);
return 0;}

 

结果为n=123

c=A
n=123
c=A

 

2.指针类型的大小

#include<stdio.h>
int main()
{int n=0;
int*pn=&n;
char c=0;
char*pc=&c;
printf("sizeof pn=%d\n",sizeof(pn));
printf("sizeof pc=%d\n",sizeof(pc));
return 0;}

 

结果为

sizeof pn=4
sizeof pc=4

char*与int*存储的均为数据对象的地址,因此它们所占用的空间是相同的。

 

3.强制转换指针类型

#include<stdio.h>
int main()
{int n=1431655765;
int*pn=&n;
char*pc=(char*)pn;
 
printf("pn=%u\n",pn);
printf("pc=%u\n",pc);
 
printf("n=%d\n",n);
printf("*pn=%d\n",*pn);
printf("*pc=%d\n",*pc);
return 0;

 

结果为pn=2545224

pc=2545224
n=1431655765
*pn=1431655765
*pc=85

使用强制转换可以将pn转换为char*后赋值给pc。赋值后,pn与pc均存储了n的首地址,均打印出了同样的首地址。

但是,C语言使用不同的指针类型标识目标对象的空间大小。

pn为int*类型,*pn表达式会从首地址开始处取sizoef(int)字节,将其转换成int类型作为表达式结果。因此,结果为1431655765。

pc为char*类型,*pc表达式会从首地址开始处取sizoef(char)字节,将其转换成char类型作为表达式结果,因此,结果为85

 

4.指针运算

①指针与整型加减

整型数据的值可以被当作首地址,但是目标数据空间大小却无法表示。无法直接对指针赋值。需要将整型转换为对应的指针类型后,再进行赋值。

#include<stdio.h>
int main()
{char*pc; short*ps; int*pn; long*pl; longlong*pll; float*pf; double*pd;
//现将整型转换为对应的指针类型再赋值
pc=(char*)100;
ps=(short*)100;
pn=(int*)100;
pl=(long*)100;
pll=(longlong*)100;
pf=(float*)100;
pd(double*)100;
 
pc=pc+1;
ps=ps+1;
pn=pn+1;
pl=pl+1;
pll=pll+1;
pf=pf+1;
pd=pd+1;
 
printf("pc=%u\n",pc);
printf("ps=%u\n",ps);
printf("pn=%u\n",pn);
printf("pl=%u\n",pl);
printf("pll=%u\n",pll);
printf("pf=%u\n",pf);
printf("pd=%u\n",pd);
return 0;}

 

结果为

pc=101
ps=102
pn=104
pl=104
pll=108
pf=104
pd=108

可以看出增加的数值分别是对应的目标数据对象的空间大小。

规律:sizeof(目标数据对象)被称作步长。

指针类型加n后。其首地址向后移动n*步长字节。

指针类型减n后。其首地址向前移动n*步长字节。

Android控制递归次数 递归 调试_#include_04

Android控制递归次数 递归 调试_#include_05

 

 

 

 指针的加减可以指向相应的数据,我们可以通过指针的加减来访问和改变目标数据

 

②同类型指针减法运算

#include<stdio.h>
int main()
{int arr[10];//声明一个含有10个元素的数组
printf("&arr[0]=%d\n",&arr[0]);//第1个元素的首地址和大小
printf("&arr[5]=%d\n",&arr[5]);//第6个元素的首地址和大小
printf("&arr[5]-&arr[0]=%d\n",&arr[5]-&arr[0]);
return 0;}

 

结果

&arr[0]=20970856
&arr[5]=20970876
&arr[0] - &arr[5]=5
因为5=20/4,所以

规律:sizeof(目标数据对象)被称作步长。指针类型与指针类型相减后,其结果为两首地址差值除以步长。(代表相隔几个元素)