函数的思想:从上到下,逐步求解问题的过程。将一个大的问题拆开成一个个小问题,每一个小问题都有与之对应的解决方案,这个解决方案就是“函数”。
函数的定义:(表示把功能实现出来)
1.main函数之前:
2.main函数之后:需要在调用函数之前声明该函数,函数头+分号 就是函数声明
表示形式:
类型标识符 函数名(形式参数)
{
函数体代码;
}
类型标识符:表示函数要带出的结果的类型,既返回值的数据类型。
注意:
1、数组类型也是数据类型,但这里不能用数组类型来表示返回值的类型。
2、如果函数不需要带出什么结果,此时返回结果的类型说明符一般设计为void。如果类型为void,一般不写return。
3、如果返回结果的类型与类型说明符不一致,以类型说明符为准,最终结果的类型都会转为类型说明符表示的类型。
4、类型说明符如果不写,默认是int型。
函数名:函数的入口地址,命名规则符合标识符的命名规则。(由字母、数字、下划线组成组成,不能以数字开头)
形式参数:表示该函数要用到的数据,表明将来使用时需要用到的实际参数该怎么写。
注意:
1、每一个形参变量都必须明确指定类型,不能写成(int a,b)。
2、实参和形参对应的关系:类型匹配,个数相同,顺序一一对应。
3、函数传参,传递的是实际参数的数值(值传递)。
4、如果不需要接收实际参数,形参一般设计为void(表示空类型)。
函数体代码:这是用来实现函数具体功能的代码。
eg:写一个函数,实现两数求和。
1.先确定函数名
2.考虑函数需要用到哪些数据 ---- 形参
形参的写法:
(数据类型 形参变量名1,数据类型 形参变量名2 ...)
3.处理数据---- 函数体的具体实现
4.考虑需不要带出结果 --- (返回值对应)类型说明符
#include <stdio.h>
//函数的定义
int add(int num1,int num2)
{
int sum;
sum = num1 + num2;
return sum;
}
int main()
{
int ret;
//函数的调用
ret = add(1,2);
printf("ret = %d\n",ret);
return 0;
}
函数的调用
1、函数语句
把函数调用作为一个语句。如"printstar(); "这时不要求函数带回值, 只要求函数完成一定的操作。
2、函数表达式
函数出现在一个表达式中,这种表达式称为函数表达式。这时要求函数带回一个确定的值以参加表达式的运算。例如: c=2*max(a,b);函数 max 是表达式的一部分,它的值乘2再赋给c。
3、函数参数
函数调用作为一个函数的实参。
例如: m=max(a,max(b,c));
其中 max(b, c) 是一次函数调用,它的值作为 max 另一次调用的实参。 m的值是a,b,c三者中的最大者。
又如: printf ("%d\n", max (a,b));
也是把 max(a, b) 作为 printf 函数的一个参数。
函数调用作为函数的参数,实质上也是函数表达式形式调用的一种,因为函数的参数本来就要求是表达式形式。
在函数里又调用另一个函数,叫做函数的嵌套调用。
注意:函数不支持嵌套定义(在定义的函数里又定义新的函数),但是可以嵌套调用。
函数调用的关系:
调用者与被调用者
int main(void)
{
printf("%d\n",getMonthDays());
return 0;
}
这个个示例的代码里面:
main 为调用者 ---main函数是整个程序的入口,只能作为调用者
getMonthDays --- 在此处是被调用者
函数调用的本质
实现函数调用的本质,实际上是利用了栈的结构,先入后出,保证了函数可以层层调用。
栈:实际上是一种数据结构,数据结构表示数据的一种组织形式。栈的特点是:先进后出(first in last out)FILO。
从c语言角度的栈,本质上是一块内存空间,只是按照栈这种数据结构来处理和使用的。
栈还可以放局部变量,变量的空间自动申请,自动释放。
c语言程序把内存划分了五个区域,栈只是其中的一个区域,栈(主要用来存放自动变量或函数调用的数据),堆(空间大,堆上的空间使用需要手动申请,手动释放),字符串常量区(只读),静态区,也叫全局区(用来存放全局变量和静态变量),代码区(只读)。
在 Linux 系统中,默认情况下栈的大小通常为 8MB,这是一个常见的默认设置。 然而,这个默认值是可以修改的。修改栈大小的方式可能因不同的情况和需求而有所不同。 一种常见的方法是通过编译器的选项来设置。例如,在使用 GCC 编译器时,可以使用 `-Wl,--stack=<size>` 选项来指定栈的大小,其中 `<size>` 是以字节为单位的栈大小值。 另外,在一些特定的环境或应用场景中,可能还可以通过系统配置文件或相关的内核参数来进行修改。 需要注意的是,修改栈大小应该谨慎进行。如果将栈大小设置得过小,可能会导致程序在运行时出现栈溢出的错误。相反,如果设置得过大,可能会浪费系统资源。 例如,如果一个程序需要处理大量的递归操作,可能需要适当增加栈的大小以避免栈溢出。但如果是一个简单的小型程序,过大的栈大小可能是不必要的。
特殊嵌套调用——递归
递归就是函数自己调用自己,递归有两种形式,一是直接递归,二是间接递归。递归类似于循环——递归是一种特殊的循环。
下面是递归的几个简单的示例:
#include <stdio.h>
int pbnq(int n) //求斐波那契数列的第n项
{
if(n==1 || n==2)
{
return 1;
}
else
{
return pbnq(n-1)+pbnq(n-2);
}
}
int sum(int n) //求1-n的累加求和
{
if (n==1)
{
return 1;
}
else
{
return sum(n-1)+n;
}
}
int jieCheng(int n) //求n的阶乘
{
if (n==1)
{
return 1;
}
else
{
return jieCheng(n-1)*n;
}
}
int main()
{
int n;
scanf("%d",&n);
//printf("%d\n",sum(100));
//printf("%d\n",jieCheng(n));
printf("%d\n",pbnq(n));
return 0;
}
经典的汉诺塔问题:
汉诺塔(Tower of Hanoi)是一个经典的数学问题和递归算法的示例。
问题描述:
有三根柱子 A、B、C ,在 A 柱上有 n 个圆盘,圆盘大小不等,大的在下,小的在上。要把这 n 个圆盘从 A 柱移动到 C 柱,在移动过程中始终保持大盘在下,小盘在上。每次只能移动一个圆盘,并且只能在三根柱子之间移动。解决思路:
通过递归的方式来解决。
当只有一个圆盘时,直接将其从 A 柱移动到 C 柱。
当有多个圆盘时,把上面 n - 1 个圆盘看成一个整体,先将这 n - 1 个圆盘从 A 柱借助 C 柱移动到 B 柱,然后把最大的圆盘从 A 柱移动到 C 柱,最后再把 B 柱上的 n - 1 个圆盘借助 A 柱移动到 C 柱。
#include <stdio.h>
void move(int befor, int after)
{
printf("%c ---> %c \n",befor,after);
}
int hannuo(int n, int begin, int mid, int end)
{
if(n == 1)
{
move(begin,end);
}
else
{
hannuo(n-1,begin,end,mid);
move(begin,end);
hannuo(n-1,mid,begin,end);
}
}
int main()
{
int n;
scanf("%d",&n);
hannuo(n,'A','B','C');
return 0;
}
数组作为函数参数
普通变量可以作为函数参数,数组也可以作为函数参数。
数组作为函数参数, 传递的是数组首元素的地址。---数组名可以做形参,也可以做实参。
1、数组元素作为函数参数
由于实参可以是表达式,而数组元素可以是表达式的组成部分,因此数组元素当然可作为函数的实参,与用变量作实参一样,是单向传递,即“值传送”方式。
2、数组本身作为函数参数
总结:一维整形数组做函数的参数
做形参:写成数组形式,还需要传数组长度
做实参:传数组名,数组长度
eg:
printArrray(int a[],int len) //函数头,数组作为形参
//printArrray (int *a,int len) ---编译器最终理解的形式
printArray(a,len); //调用函数
练习:
#include <stdio.h>
void printfArray(int a[],int len)
{
int i;
for ( i=0; i<len; i++ )
{
printf("a[%d] = %d\n",i,a[i]);
}
}
void nixu(int a[], int len)
{
int i=0;
int j=len-1;
int temp;
while(i<j)
{
temp = a[i];
a[i] = a[j];
a[j] = temp;
i++;
j--;
}
return ;
}
void crpaixu(int a[], int len)
{
int i=0, j=0;
int temp;
for( i=1; i<len; i++ )
{
temp = a[i];
j = i;
while(j>0 && temp<a[i-1])
{
a[i] = a[i-1];
j--;
}
a[j] = temp;
}
return ;
}
int chazhao(int a[], int len, int n)
{
int begin, mid, end;
begin = 0;
end = len - 1;
while ( begin <= end )
{
mid = (begin+end)/2;
if( n < a[mid] )
{
end = mid - 1;
}
else if ( a[mid] < n )
{
begin = mid + 1;
}
else
{
break;
}
}
if (begin <= end)
{
return mid;
}
else
{
return -1;
}
}
int main()
{
int a[] = {1,2,3,4,5,6,7,8,9};
int len = sizeof(a)/sizeof(a[0]);
/*printfArray(a,len);
printf("-------------------\n");
nixu(a,len);
printfArray(a,len);
printf("-------------------\n");
crpaixu(a,len);
printfArray(a,len);
printf("-------------------\n");
*/
int n;
printf("input n : ");
scanf("%d",&n);
chazhao(a,len,n);
if(chazhao(a,len,n)>=0)
printf("found!\n");
else
printf("no found!\n");
return 0;
}