☰
C语言学习笔记九—— 汉诺塔递归 · 数组作函数参数
2026/9/29 9:51:40 网站建设 项目流程

内容:汉诺塔递归、整型一维数组作函数参数、字符一维数组作函数参数、二维数组作函数参数。


一、回顾

  1. 函数思想:高内聚、低耦合,把功能拆开;输入-处理-输出;
  2. 函数定义、函数调用、函数声明;
  3. 递归思想;
  4. 内存 5 个区:栈、堆、全局/静态区、字符串常量区、代码区。

二、汉诺塔(递归经典题)

2.1 递归两步

写递归必须先想清楚:

  1. 问题 n 和问题 n-1 之间的递推关系;
  2. 结束条件。

2.2 思路

把 n 个盘子从 A 移到 C,借助 B:

1. 将 n-1 个盘子 从 A → B // 递归 2. 将剩下的那个盘子 从 A → C 3. 将 n-1 个盘子 从 B → C // 递归

结束条件:n == 1,直接A → C。

注意三个柱子的身份会变:

起始 辅助 目标 n 个盘子 A B C n-1 A C B

2.3 递归展开(n=3)

hanoi(3,'A','B','C') ├─ hanoi(2,'A','C','B') │ ├─ hanoi(1,'A','B','C') → move('A','C') // 1:A→C │ ├─ move('A','B') // 2:A→B │ └─ hanoi(1,'C','A','B') → move('C','B') // 3:C→B ├─ move('A','C') // 4:A→C └─ hanoi(2,'B','A','C') ├─ hanoi(1,'B','C','A') → move('B','A') // 5:B→A ├─ move('B','C') // 6:B→C └─ hanoi(1,'A','B','C') → move('A','C') // 7:A→C

2.4 代码(hanoi.c)

#include<stdio.h>voidmove(inta,intb){printf("%c-->%c\n",a,b);}// 起始 辅助 目标voidhanoi(intn,intpole1,intpole2,intpole3){if(n==1){move(pole1,pole3);// 只有一个盘,直接从起始挪到目标}else{hanoi(n-1,pole1,pole3,pole2);// ① 把 n-1 个挪到辅助柱move(pole1,pole3);// ② 把第 n 个挪到目标柱hanoi(n-1,pole2,pole1,pole3);// ③ 把 n-1 个从辅助柱挪到目标柱}}intmain(void){intn;printf("Input a num:");scanf("%d",&n);hanoi(n,'A','B','C');return0;}

带步骤号的详细版(hanoi_all_info.c):

#include<stdio.h>voidmove(intn,intpole1,intpole2){staticintstep=1;printf("%03d:[disk %d] : %c --> %c\n",step++,n,pole1,pole2);}// 起始柱 辅助柱 目标柱voidhanoi(intn,intA,intB,intC){if(1==n){move(n,A,C);}else{hanoi(n-1,A,C,B);// n-1 先挪走puts("-------");move(n,A,C);// 第 n 个挪到目标柱puts("-------");hanoi(n-1,B,A,C);}}intmain(void){intn=0;printf("Input numbers of disk: ");scanf("%d",&n);hanoi(n,'A','B','C');return0;}

三、数组作为函数参数

3.1 问题引入

inta[10]={1,2,3,4};
  • 单个元素a[0]就是 int 型变量,传给int max(int a, int b)没问题;
  • 但要把整个数组传进去怎么办?

3.2 整型一维数组作参数

// 形参写法(形式上是数组)voidprintArray(intx[10],intlen){}// 编译器本质上看成voidprintArray(int*x,intlen){}

数组在内存中是一片连续空间,数组名从"代表的值"角度,代表的是首元素的地址:

[ a[0] ] ← 首元素的起始地址 [ a[1] ] [ a[2] ] ...

小结:

写法
形参printArray(int a[], int len)(数组形式 + 数组长度)
本质printArray(int *a, int len)(指针,接收首元素地址)
实参printArray(数组名, 数组长度)

注意:函数内部sizeof(x)拿不到数组长度(x 已经退化成指针),所以长度必须单独传。

3.3 值传递 vs 地址传递

  • 实参给形参,本质是把实参的值拷贝给形参变量;
  • 普通变量是值传递(函数内改形参不影响外面);
  • 数组传的是首地址,函数内通过地址操作,改的就是主函数里那块数组空间——所以reverseArray、selectSort不需要 return 就能改原数组。

3.4 基础代码(array.c)

#include<stdio.h>voidprintArray(intx[],intlen)// 编译器看成 int *x{inti=0;for(i=0;i<len;++i){printf("%d ",x[i]);}putchar('\n');}voidinputArray(intx[],intlen){inti=0;for(i=0;i<len;++i){scanf("%d",&x[i]);}}intmain(void){inta[]={1,2,3,4,5,6};intlen=sizeof(a)/sizeof(a[0]);inputArray(a,len);printArray(a,len);return0;}

3.5 数组常用操作封装(test_array.c)

#include<stdio.h>voidprintArray(intx[],intlen){inti=0;for(i=0;i<len;++i){printf("%d ",x[i]);}putchar('\n');}voidinputArray(intx[],intlen){inti=0;for(i=0;i<len;++i){scanf("%d",&x[i]);}}// 求最大值intmaxOfArray(intx[],intlen){intmax=x[0];inti=0;for(i=1;i<len;++i){if(x[i]>max){max=x[i];}}returnmax;}// 逆序voidreverseArray(intx[],intlen){inti=0;intj=len-1;while(i<j){intt=x[i];x[i]=x[j];x[j]=t;++i;--j;}}// 选择排序voidselectSort(intx[],intlen){inti=0,j=0;for(i=0;i<len-1;++i){for(j=i+1;j<len;++j){if(x[i]>x[j]){intt=x[i];x[i]=x[j];x[j]=t;}}}}// 二分查找:找到返回下标,没找到返回 -1intbinaryFind(intx[],intlen,intn){intbegin=0;intend=len-1;intmid=0;while(begin<=end){mid=(begin+end)/2;if(x[mid]>n){end=mid-1;}elseif(x[mid]<n){begin=mid+1;}else{break;}}returnbegin<=end?mid:-1;}intmain(void){inta[10]={1,2,3,4,5,6};intlen=sizeof(a)/sizeof(a[0]);inputArray(a,len);selectSort(a,len);printArray(a,len);intn=0;printf("Input a num:");scanf("%d",&n);intret=binaryFind(a,len,n);printf("ret = %d\n",ret);return0;}

四、字符型一维数组作函数参数

字符数组用来存字符串,字符串本身有 ‘\0’ 结束标志,所以作参数时不需要再传长度。

chars[]="hello";
写法
形参void Strcpy(char dest[], char src[])
实参直接传数组名

完整代码(char.c):手写 string.h 函数

#include<stdio.h>// 手写 putsvoidPuts(chars[]){inti=0;while(s[i]!='\0'){putchar(s[i]);++i;}putchar('\n');}// 手写 strlenlongStrlen(chars[]){inti=0;while(s[i]!='\0'){++i;}returni;}// 手写 strcpy:把 src 拷到 dest,最后补 '\0'voidStrcpy(chardest[],charsrc[]){inti=0;while(src[i]!='\0'){dest[i]=src[i];++i;}dest[i]='\0';}// 手写 strcat:把 src 拼到 dest 后面voidStrcat(chardest[],charsrc[]){inti=0;// 1. 先定位到 dest 的 '\0'while(dest[i]!='\0'){++i;}// 2. 从该位置开始把 src 逐个拷过来intj=0;while(src[j]!='\0'){dest[i]=src[j];++i;++j;}// 3. 保证 dest 是字符串dest[i]='\0';}// 紧凑写法:dest[i++] = src[j++] 会把 '\0' 一起拷过去voidStrcat1(chardest[],charsrc[]){inti=0;while(dest[i]!='\0'){++i;}intj=0;while(dest[i++]=src[j++]);}// 手写 strcmp:返回停止位置上两个字符的差值intStrcmp(chars1[],chars2[]){inti=0;while(s1[i]==s2[i]&&s1[i]!='\0'&&s2[i]!='\0'){++i;}returns1[i]-s2[i];}intmain(void){chars[20]="hello";gets(s);chars1[20];gets(s1);printf("ret = %d\n",Strcmp(s,s1));return0;}

五、二维数组作函数参数

inta[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};

二维数组本质是"一维数组的一维数组",作参数时:

// 形式上写成二维数组voidprintArray(inta[3][4]);// 编译器本质上看成:指向一维数组的指针voidprintArray(int(*a)[4]);

小结:

写法
形参printArray(int a[][4], int row)(第二维列数必须写死,行数用变量传)
实参printArray(数组名, 行数)

代码(2d_array.c)

#include<stdio.h>voidprintArray(intx[][4],introw){inti=0,j=0;for(i=0;i<row;++i){for(j=0;j<4;++j){printf("%2d ",x[i][j]);}putchar('\n');}}intmain(void){inta[][4]={1,2,3,4,5,6,7,8,9,10,11,12};introw=sizeof(a)/sizeof(a[0]);printArray(a,row);return0;}

六、作业参考:双胞胎素数(test_prime.c)

#include<stdio.h>intisPrime(intn){inti=0;intret=1;for(i=2;i<n;++i){if(n%i==0){ret=0;break;}}returnret;}// 打印 n 以内所有素数voidprintPrimeInX(intn){inti=0;for(i=2;i<=n;++i){if(isPrime(i)==1){printf("%d ",i);}}putchar('\n');}// 双胞胎素数:两个素数相差为 2,如 (3,5) (5,7)voidprintTwinsPrimeInX(intn){inti=0;for(i=2;i<=n-2;++i){if(isPrime(i)==1&&isPrime(i+2)==1){printf("(%d,%d)\n",i,i+2);}}}intmain(void){intn=0;printf("Input a num:");scanf("%d",&n);printTwinsPrimeInX(n);return0;}

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询