一、函数递归
函数需要被另一个函数调用才能执行,而主函数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;}
蓝色线条画出了从下级回归的流程。箭头方向和标号数字代表执行顺序。流程在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时,开始回归。
回归到n为2时,计算2 * 1。回归到n为3时,计算3 * (2 * 1)。回归到n为4时,计算4 * (3 * 2 * 1)
二、指针
1.cpu由三部分构成:
1.算术、逻辑单元:对数据执行运算(例如加法、减法)的电路。2.控制单元:协调机器活动的电路。3.寄存器组:数据临时存储。
但是,寄存器能够存储的信息量很少,仅仅是当前工作所必要的。所以需要配合内存使用
因此记录一个数据对象在内存中的存储位置,需要两个信息:
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*步长字节。
指针的加减可以指向相应的数据,我们可以通过指针的加减来访问和改变目标数据
②同类型指针减法运算
#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(目标数据对象)被称作步长。指针类型与指针类型相减后,其结果为两首地址差值除以步长。(代表相隔几个元素)