目录
函数
设计函数
1.能拆成函数的尽量拆成函数
2.函数设计时,可能考虑健壮性、扩展性
健壮性是指某一个程序在面对输入错误或者意外条件等异常情况的时候,能够维持程序正常运行或者进行稍微适当的处理,能够处理多种情况
扩展性是指中程序不改变其基础的情况下,能够适应新的需求,新的数据,新的用户。
函数调用的关系
调用者与被调用者
例如:
#include<stdio.h>
int leapyear(int year)
{
return year%4 == 0 && year%100 != 0||year%400 ==0;
}
int monthdays(int year,int month)
{
int days[]= {31,28,31,30,31,30,31,31,30,31,30,31};
return month==2?days[month-1]+leapyear(year):days[month-1];
}
int main(void)
{
int y,m;
scanf("%d %d",&y,&m);
printf("天数= %d\n",monthdays(y,m));
return 0;
}
int main(void)
{
printf("天数= %d\n",monthdays(y,m));
return 0;
}
在这段代码中,main成为调用者(main函数是整个程序的入口,只能是调用者);
monthdays函数在此处是被调用者。
int monthdays(int year,int month)
{
int days[]= {31,28,31,30,31,30,31,31,30,31,30,31};
return month==2?days[month-1]+leapyear(year):days[month-1];
}
而mothsdays中又含有leapyear,这种称为函数的嵌套适使用。
注意:函数不支持嵌套者定义,但是可以嵌套调用。
函数名是函数的入口地址
函数调用的本质
实际是利用栈的结构,将数据进行入栈出栈,进行先进后出的规则,它保证了函数可以层层嵌套使用
c语言程序把内存划分为了5个区域:
栈——主要用来存放自动变量/函数调用的数据;它是数据结构(表示数组组织形式),特点:空间自动申请自动释放。
从C语言角度看栈:1.本质上是一块内存空间;2.只是按照栈这种数据结构来处理和使用的。
堆——空间大,堆上的空间特点:手动申请,手动释放
字符常量区——只读
代码区——只读
程序=代码+数据
特殊嵌套调用—递归
直接递归:函数自己调用自己;
间接递归:在其他函数中调用自己,然后自己再调用其他函数
递归是一种特殊的循环(类似循环);
递归思路
1.要求问题n
2.依赖于问题对n-1的解读
递归代码实现思路
1.递推关系
2.递推结束条件
递归求和
#include<stdio.h>
int sum(int n)
{
if(n==1)
return 1;
else
return sum(n-1)+n;
}
int main(void)
{
int n;
scanf("%d",&n);
int ret = sum(n);
printf("sum = %d\n",ret);
return 0;
}
阶乘
#include<stdio.h>
int factorial(int n)
{
if(n==1)
return 1;
else
return factorial(n-1)*n;
}
int main(void)
{
int n;
scanf("%d",&n);
int ret = factorial(n);
printf("factorial = %d\n",ret);
return 0;
}
斐波那契数列的第n项
#include<stdio.h>
int fb(int n)
{
if(n==1||n==2)
return 1;
else
return fb(n-1)+fb(n-2);
}
int main(void)
{
int n;
scanf("%d",&n);
int ret = fb(n);
printf("fb = %d\n",ret);
return 0;
}
汉诺塔
#include<stdio.h>
void move(int pole1,int pole2)
{
printf("%c--------->%c\n",pole1,pole2);
}
void hanoi(int n,int begin,int mid,int end)
{
if(n==1)
{
move(begin,end);
}
else
{
hanoi(n-1,begin,end,mid);
move(begin,mid);
hanoi(n-1,mid,begin,end);
}
}
int main(void)
{
int n,A,B,C;
scanf("%d",&n);
hanoi(n,'A','B','C');
return 0;
}
函数参数
数组作为函数参数
1.数组元素作为函数参数
2.数组本身作为函数参数
总结:
1.一维整型数组做参数
形参——写成数组形式,还需要数组长度
实参——数组名,数组长度
eg:
形式语法支持这样的写法
printarray(int a[],int len)//形参
编译器最终理解的形式为:
printarray(int *a,int len )
指针类型在32位为4字节,在64位为8字节
调用形式:
printarray(a,len);
int a[10];
数组名代表类型—int[10]这种数组类型
数组名代表的值—首元素的地址(数组所占内存空间的首地址)
一维字符型数组做函数参数
用来存放字符串
形参:数组形式
实参:数组名
注意:因为处理的是字符串,字符串操作的时候不需要考虑数组长度,只需要关注结束标'\0'
封装函数实现一些字符串的常用操作
输出字符串
void Puts(char s[])
{
int i=0;
while(s[i]!=0)
{
printf("%c",s[i]);
++i;
}
putchar('\n');
}
输入字符串
void Gets(char s[])
{
int i=0;
while((s[i]=getchar())!='\n')
{
i++;
}
s[i]='\0';
}
字符串长度
int Strlen(char s[])
{
int i=0;
while(s[i]!='\0')
++i;
printf("%d",i);
putchar('\n');
}
复制字符串
void Strcpy(char dest[],char src[])
{
int i=0;
while(src[i]!=0)
{
dest[i] = src[i];
i++;
}
src[i]='\0';
}
拼接字符串
void Strcat(char dest[],char src[])
{
int i=0,j=0;
while(dest[i] !='\0' )
i++;
while(src[j]!='\0')
{
dest[i] = src[j];
++i;
++j;
}
dest[i]='\0';
}
比较字符串大小
int Strcmp(char dest[],char src[])
{
int i=0;
while(dest[i]==src[i]&&dest[i]!=0&&src[i]!=0)
{
++i;
}
return dest[i]-src[i];
}
二维数组做函数参数
形参—二维数组形式+行数(本质就是一堆数组的长度)
实参—数组名+行数
打印二维数组
void printArray(int (*a)[4],int row)
{
int i=0,j=0;
for(i=0;i<row;++i)
{
for(j=0;j<4;++j)
printf("%d ",a[i][j]);
putchar('\n');
}
}
二维数组求和
void sum(int (*a)[4],int row)
{
int i=0,j=0;
int sum =0;
for(i=0;i<row;++i)
{
for(j=0;j<4;++j)
sum = sum + a[i][j];
}
printf("sum = %d",sum);
putchar('\n');
}
对角线最大值
void djmax(int (*a)[4],int row)
{
int i=0,j=0;
int djmax = a[0][0];
for(i=0;i<row;++i)
{
for(j=0;j<4;++j)
{
if(i==j&&djmax<a[i][j])
djmax=a[i][j];
}
}
printf("%d ",djmax);
}
二维字符型数数组做函数参数
与二维整型数组使用方式相同
形参—二维数组形式+行数(本质就是一堆数组的长度)
实参—数组名+行数
输出二维字符串
void printStr(char s[][10],int row)
{
int i = 0;
for (i = 0; i < row; ++i)
{
puts(s[i]);
}
}
输入二维字符串
void inputStr(char (*s)[10],int row)
{
int i = 0;
for (i = 0; i < row; ++i)
{
gets(s[i]);
}
}
二维字符串选择排序
void Insertion(char s[][10],int row)
{
int i = 0;
int j = 0;
for (i = 1;i < row; ++i)
{
char t[10];
strcpy(t,s[i]);
j = i;
while (j > 0&&strcmp(t,s[j-1]) < 0)
{
strcpy(s[j],s[j-1]);
--j;
}
strcpy(s[j],t);
}
}
二分查找字符串
int binarysearch(char s[][10],int row, char str[])
{
int begin = 0;
int end = row - 1;
int ret = -1;
int mid = 0;
while (begin <= end)
{
mid = (begin+end)/2;
if (strcmp(s[mid],str) < 0)
{
begin = mid + 1;
}else if (strcmp(s[mid],str) > 0)
{
end = mid - 1;
}else
{
ret = mid;
break;
}
}
return ret;
}
注意:
1.不要和一维字符型数组传参搞混;一维字符型数组,主要用来存放字符串数据,而字符串数据有结束标志('\0'),所以传参时不需要传长度。
2.二维字符型数组,用来存储多字字符串,操作时往往操作多个字符串,而多个字符串没有所谓结束的标志,看的是数组长度(行数)。