函数II (10)

一、设计函数

能拆成函数的尽量拆成函数

函数设计时尽可能考虑健壮性扩展性

二、函数调用的关系

调用者和被调用者

int main(void) 
{
   printf("%d\n",getMonthDays()); 
   return 0;
}

这个里面:
main ---成为调用者  ---main函数是整个程序的入口,只能调用者 
getMonthDays --- 在此处是 被调用者
              

getMonthDays()
{
   isLeapYear();
} //函数的嵌套调用 


main -->getMonthDays -->   isLeapYear

注:
  函数不支持 嵌套定义,但是可以嵌套调用

函数名 --- 函数的入口地址   

函数调用的本质:
   实际是利用的栈的结构  ---先进后出 --保证了函数可以层层嵌套调用

  1. 栈的使用:C语言使用栈(stack)来管理函数调用过程中的数据和控制信息。当一个函数被调用时,函数的参数、局部变量以及函数返回地址等信息都会被压入栈中。

  2. 参数传递:函数调用时,参数的传递通常是通过栈来实现的。在调用函数时,调用者将参数值压入栈中,然后被调用函数将栈中的参数值弹出来使用。

  3. 函数调用约定:C语言并没有强制规定函数调用的具体方式,而是由编译器和操作系统决定。常见的调用约定包括 cdecl、stdcall 等,它们影响参数的传递方式和堆栈的清理责任。

  4. 返回值处理:被调用函数执行完毕后,会将返回值存放在事先约定的位置(通常是寄存器或者栈中),然后返回到调用者处。调用者会根据约定从事先约定的位置取得返回值。

  5. 跳转和恢复:当一个函数被调用时,CPU会保存当前的执行位置(通常是返回地址)并跳转到被调函数的起始位置执行。当被调函数执行完毕后,CPU会根据保存的返回地址跳转回调用函数的执行位置继续执行。

再调用函数前会保存当前位置在栈中(保存现场),调用结束后返回当前位置继续执行(恢复现场)

栈:
  数据结构 --- (表示数组组织形式)
  特点:
      先进后出 (First In Last Out) //FILO 
  
  c语言角度的栈:
  1.本质上是一块内存空间
  2.只是按照 栈 这种数据结构 来处理和使用的
  
  栈:
     局部变量 //空间 自动申请 自动释放  
     
     
C语言程序:
  把内存划分了5个区域 

栈             //主要    用来存放, 自动变量 或 函数调用的数据 
堆             //空间大  堆上的空间 ,手动申请,手动释放 
字符串常量区   // "hello" (只读)
静态区(全局区) // 全局变量 和 静态变量  
代码区         // 只读的 
程序 = 代码 + 数据 

三、特殊嵌套调用---递归

递归:
     自己调用自己  //
直接递归 
间接递归 

递归思路:
   要求问题n 
   依赖于问题n-1的解决 
   
递归代码实现思路:
1.递推关系 
  怎么从 问题 n 到 问题n-1

例如:求前一百个数的和

  sum(100) => sum(99)+100
  sum(99)  => sum(98)+99
  
  sum(n) = sum(n-1)+n;
2.递推结束条件 
  n = 1

//3.代码

代码实现

#include<stdio.h>

int sum(int n)
{
	if(n == 1)
	{
		return 1;
	}else
	{
		return sum(n-1) + n;
	}
}

int main(void)
{
	int ret;
	scanf("%d",&ret);
	ret = sum(ret);
	printf("ret = %d\n",ret);

	return 0;
}

练习:求前n项的阶乘

factorial(5)
   |---factorial(4)*5
         |---factorial(3)*4
         
factorial(n) = factorial(n-1)*n

#include<stdio.h>

int sum(int n)
{
	if(n == 1)
	{
		return 1;
	}else
	{
		return sum(n-1) * n;
	}
}

int main(void)
{
	int ret;
	scanf("%d",&ret);
	ret = sum(ret);
	printf("ret = %d\n",ret);

	return 0;
}

练习:
   斐波拉契数列 
   
   1 1 2 3 5 8
  
   求斐波拉契数列第n项

fibo(5)
  |--fibo(4)+fibo(3) 
       |      |-- fibo(2) + fibo(1)
       |--fibo(3) + fibo(2)
            |-- fibo(2) + fibo(1)
                  1         1

#include<stdio.h>

int feibo(int n)
{
	if(n==1 || n==2)
	{
		return 1;
	}else 
	{
		return feibo(n-1)+feibo(n-2);
	}
}

int main(void)
{
	int a;
	scanf("%d",&a);
	printf("%d\n",feibo(a));

	return 0;
}

四、数组作为函数参数

1.数组元素作为函数参数 

int a[10] = {1,2,3};

int add(int a,int b)
{
   return a + b;
}

add(a[0],a[1]);

2.数组本身作为函数参数 

     eg:
       printArrray(int a[],int len) //形参 
       //printArrray (int *a,int len) ---编译器最终理解的形式 
       //调用
       printArray(a,len); 

int a[10];
数组名 代表类型 ---int[10] 这种数组类型 
数组名 代表的值 ---首元素的地址 //(数组所占内存空间的首地址) 

一维整型数组 做函数 参数 
      形参  --写成数组形式  还需要 数组长度 
      实参  --数组名,数组长度

形参与实参的对应

在C语言中,形参的定义方式可以是数组形式 int a[],也可以是指针形式 int *a。实际上,这两种方式在函数调用时都可以接受数组作为参数,因为在函数调用过程中,数组名会被隐式转换为指向数组首元素的指针。

所以,无论是使用 int a[] 还是 int *a,在函数调用时都可以传递一个整型数组 a,同时需要传递数组的长度 len

编译器的最终理解

在编译阶段,不论你声明函数时使用 int a[] 还是 int *a,编译器都会将它们解释为指针形式 int *a。因此,实际上函数声明可以写成 void printArray(int a[], int len);void printArray(int *a, int len);,它们在编译器眼中是等效的。

使用函数输出一个数组

#include<stdio.h>

void printArray(int a[])
{
	int len = sizeof(a) / sizeof(a[0]);
	for (int i = 0; i < len; ++i) 
	{
        printf("%d ", a[i]);
    }
    printf("\n");
}

int main(void)
{
	int a[] = {1,2,3,4,5,6,7,8,9};
	printArray(a);
	return 0;
}

原因解析

  1. 数组参数传递为指针: 在C语言中,数组作为函数参数时会自动转换为指向数组首元素的指针。因此,int a[] 在函数参数中实际上被理解为 int *a。在函数内部,a 是一个指针,它指向传递给函数的数组的首地址。

  2. sizeof在函数中的行为: 当你在函数内使用 sizeof(a) 时,它返回的是指针 a 的大小,而不是整个数组的大小。在大多数情况下,指针的大小是固定的,通常是4或8个字节(取决于编译器和操作系统)。

  3. 计算数组长度的错误方式int len = sizeof(a) / sizeof(a[0]); 这行代码试图通过指针 a 的大小除以 a[0] 的大小来计算数组的元素个数。但由于 a 是一个指针,这个计算并不会得到你期望的结果,而是得到一个较小的值(通常为2),这解释了为什么你的代码只输出了两个数。

正确做法为

#include<stdio.h>

void printArray(int a[],int len)
{
	int i = 0;
	for(i = 0;i < len;++i)
	{
		printf("a[%d] = %d\n",i,a[i]);
	}
}

int main(void)
{
	int a[] = {1,2,3,4,5,6,7,8,9};
	int len = sizeof(a)/sizeof(a[0]);
	printArray(a,len);

	return 0;
}

练习:
    准备一个数组,实现一个函数,找出最大值 

#include<stdio.h>

int max(int a[],int len)
{
	int max = a[0];
	int temp;
	int i;
	for(i = 1;i < len;++i)
	{
		if(max < a[i])
		{
			temp = a[i];
			a[i] = max;
			max  = temp;
		}
	}
	return max;
}

int main(void)
{
	int n;
	printf("请输入数组长度:");
	scanf("%d",&n);
	int a[n];
	int len = sizeof(a)/sizeof(a[0]);
	printf("请输入数组元素:");
	int i = 0;
	for(i = 0;i < len;++i)
	{
		scanf("%d",&a[i]);
	}

	printf("max = %d\n",max(a,len));
	

	return 0;
}

由于传递的是指针地址,为直接在原数组地址上操作,所以函数可以改变数组的值

练习:
    实现数组逆序

#include<stdio.h>

int nixu(int a[],int len)
{
	int i;
	int temp;
	for(i = 0;i < len/2;++i)
	{
		temp = a[i];
		a[i] = a[len-i-1];
		a[len-i-1] = temp;

	}
}

int main(void)
{
	int n;
	printf("请输入数组长度:");
	scanf("%d",&n);
	int a[n];
	int len = sizeof(a)/sizeof(a[0]);
	printf("请输入数组元素:");
	int i = 0;
	for(i = 0;i < len;++i)
	{
		scanf("%d",&a[i]);
	}
	nixu(a,len);

	for(i = 0;i < len;++i)
	{
		printf("a[%d] = %d\n",i,a[i]);
	}
	

	return 0;
}

练习:
    排序 使用插入排序

#include<stdio.h>

void charu(int a[],int len)
{
	int i,j;
	int temp;
	for(i = 1;i < len;++i)
	{
		temp = a[i];
		j = i;
		while(j > 0 && temp < a[j-1])
		{
			a[j] = a[j-1];
			--j;
		}
		a[j] = temp;

	}
}

int main(void)
{
	int n;
	printf("请输入数组长度:");
	scanf("%d",&n);
	int a[n];		// = {4,5,3,2,4,7,8,3,2,12,18};
	int len = sizeof(a)/sizeof(a[0]);
	printf("请输入数组元素:");
	int i = 0;
	for(i = 0;i < len;++i)
	{
		scanf("%d",&a[i]);
	}

	charu(a,len);
	for(i = 0;i < len;++i)
	{
		printf("a[%d] = %d\n",i,a[i]);
	}


	return 0;
}

练习:
    查找 

#include<stdio.h>

int chazhao(int a[],int len,int m)
{
	int begin = 0;
	int mid;
	int end = len - 1;

	while(begin <= end)
	{
		mid = (begin + end)/2;
		if(a[mid] > m)
		{
			end = mid -1;
		}else if(a[mid] < m)
		{
			begin = mid + 1;
		}else
		{
			break;
		}
	}
	if(begin <= end)
	{
		return mid;
	}
	else
	{
		return -1;
	}
}
void charu(int a[],int len)
{
	int i,j;
	int temp;
	for(i = 1;i < len;++i)
	{
		temp = a[i];
		j = i;
		while(j > 0 && temp < a[j-1])
		{
			a[j] = a[j-1];
			--j;
		}
		a[j] = temp;

	}
}

int main(void)
{
	int n;
	printf("请输入数组长度:");
	scanf("%d",&n);
	int a[n];		// = {4,5,3,2,4,7,8,3,2,12,18};
	int len = sizeof(a)/sizeof(a[0]);
	printf("请输入数组元素:");
	int i = 0;
	for(i = 0;i < len;++i)
	{
		scanf("%d",&a[i]);
	}

	charu(a,len);

	printf("排序过后数组为:\n");
	for(i = 0;i < len;++i)
	{
		printf("a[%d] = %d\n",i,a[i]);
	}

	int m;
	printf("请输入要查找的数:");
	scanf("%d",&m);

	int s = chazhao(a,len,m);
	if(s >=0)
	{
		printf("a[%d] = %d\n",s,a[s]);
	}
	return 0;
}

  • 18
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值