当前位置: 首页 > news >正文

C语言初阶3-函数

1.函数是什么?

数学中我们其实就⻅过函数的概念,⽐如:⼀次函数 y=kx+b ,k和b都是常数,给⼀个任意的x,y就得到⼀个值。
其实在C语言也引⼊函数(function)的概念,有些翻译为:⼦程序,我认为⼦程序这种翻译更加准确⼀些。

  • 在计算机科学中,子程序(英文:Subroutine, procedure, function, routine, method, subprogram, callable unit),是一个大型程序中的某部分代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。

1.1 C语言中函数的分类

  1. 库函数
  2. 自定义函数

2.库函数

为什么会有库函数?

我们在学习c语言的过程中,会经常用到一些基础功能,不是业务性的代码,每个程序员都可能用到,为了支持可移植性和提高程序的效率,所以c语言的基础库中提供了一系列库函数,方便程序员进行开发。

如何学习库函数?
学习库函数的网站:
cplusplus.com
cppreference.com

简单总结,C语言常用的库函数都有:

  • IO函数 输入输出
  • 字符串操作函数
  • 字符操作函数
  • 内存操作函数
  • 时间/日期函数
  • 数学函数
  • 其他库函数

3.自定义函数

自定义函数和库函数一样,有函数名,返回值类型和函数参数。

但是不一样的是这些都是我们自己来设计。这给程序员一个很大的发挥空间。

函数的组成:

ret_type fun_name(para1, * )    
{statement;       //语句项
}
ret_type返回类型
fun_name函数名
para1函数参数
statement函数体

例 1 (写一个函数可以找出两个整数中的最大值):

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int get_max(int x,int y)
{if (x>y)return x; if (x<y)return y;    
}
int main()
{int a = 10;int b = 20;int max = get_max(a,b);printf("max=%d\n",max);return 0;
}

例 2 (写一个函数可以交换两个整形变量的内容):
错误案例 :

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void Swap1(int x,int y)        //void表示空,无返回值
{int tmp = 0;tmp = x;x = y;y = tmp;
}
int main()
{int a = 10;int b = 20;printf("a=%d,b=%d\n",a,b);Swap1(a,b) ;                       //失败缘由:swap函数创造了x,y最终x,y值互换,但这并不改变a,b的值printf("a=%d,b=%d\n",a,b);       //(类似将a,b的值拷贝到x,y中,对x,y的操作对a,b无影响)return 0;
}

正确案例 :

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void Swap2(int* pa, int* pb)
{int tmp = 0;tmp = *pa;*pa = *pb;*pb = tmp;
}
int main()
{int a = 10;int b = 20;Swap2(&a,&b);printf("Swap2:a=%d,b=%d\n", a, b);return 0;
}

分析:当实参 ( a , b ) 传递给形参 ( x , y) 的时候,形参是实参的一份临时拷贝,对形参的修改不能改变实参,那么对x和y的值进行交换是不能 影响a和b的值的用int * pa接收a的地址,int * pb接收b的地址,才能对a和b的值进行修改.

4.函数参数

4.1 实际参数(实参):

  • 真实传给函数的参数,叫实参。
  • 实参可以是:常量、变量、表达式、函数等。
  • 无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。

4.2 形式参数(形参):

  • 形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数
  • 形式参数当函数调用完成之后就自动销毁了,因此形式参数只在函数中有效。

所以:形参是实参的一份临时拷贝

5.函数调用

5.1 传值调用

函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参

5.2 传址调用

  • 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式
  • 这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量

6.练习(写函数)

6.1可以判断一个数是不是素数

自己写的
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <math.h>
void shushu(int x)
{int i;for (i = 2; i <= sqrt(x); i++){if (0 == x % i){printf("这是一个素数");break;}}if (i > sqrt(x))printf("这不是一个素数");}
int main()
{int a=0;scanf("%d", &a);//  "%d"中不能写\n(写了必须手动换行)shushu(a);return 0;
}
别人写的
int is_prime(int n)
{int j = 0;for (j = 2; j <= sqrt(n); j++)            //函数要写头文件{if (n % j == 0)return 0;}return 1;}
int main()
{int count = 0;int i = 0;for (i = 101; i <= 200; i+=2)            //偶数不用考虑了{if (is_prime(i)){printf("%d ",i);count++;}}printf("\n共有%d个素数", count);return 0;
}

6.2判断一年是不是闰年

别人写的
define _CRT_SECURE_NO_WARNINGS
include<stdio.h>
int is_leap_year(int y)//是闰年返回1
{if ((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0))return 1;else return 0;
}
int main()
{
int year= 0;
for (year = 1000;year<= 2000; year++)
{if (is_leap_year(year)){printf("%d ",year);}        
}
return 0;
}

6.3 实现一个整形有序数组的二分查找

我写的(错误的)

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <math.h>
int search(int arr[], int n)                    //此处的arr是一个指针,传过来的是原arr首元素的地址
{int right = sizeof(arr) / sizeof(arr[0]);    //right=1int left = 0;while (right=>left){int midsize = (right+left) / 2;if (n > arr[midsize]){left = midsize;}else if (n > arr[midsize]){right = midsize;}else{printf("找到了,下标是%d\n", midsize);return 1;}}if (right = left)return 0;
}
int main()
{int arr[] = { 1,2,4,6,8,9,10,11,33 };int k;scanf("%d", &k);int a=search(arr, k);printf("%d", a);return 0;
}

别人写的(正确的)

define _CRT_SECURE_NO_WARNINGS
include<stdio.h>
int binary_search(int arr[], int k, int sz)
{int left = 0;int right = sz - 1;while(left<=right){int mid = left + (right - left) / 2;if (arr[mid] < k){left = mid + 1;}else if (arr[mid] > k){right = mid - 1;}else{return mid;//找到了,返回下标mid}}return -1;//没有找到
}
int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int k = 7;int sz = sizeof(arr) / sizeof(arr[0]);          //在此处改动即可int ret = binary_search(arr, k,sz);if (ret == -1)printf("找不到\n");elseprintf("找到了,下标是:%d\n", ret);return 0;
}

6.4 每调用一次这个函数,就会将num的值增加1

define _CRT_SECURE_NO_WARNINGS
include<stdio.h>
int Add(int *p)
{(*p)++;                   //++优先级高于*,若为*p++,结果为0,0.
}
int main()
{int num = 0;Add(&num);printf("%d\n", num);//1Add(&num);printf("%d\n", num);//2return 0;
}

7.函数的嵌套调用与链式访问

  • 函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的

7.1嵌套调用

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void new_line()
{printf("hehe\n");
}
void three_line()
{int i = 0;for (i = 0; i < 3; i++){new_line();}
}
int main()
{three_line();return 0;
}

注意:
函数可以嵌套调用,但是不能嵌套定义,每个函数地位都是相等的,并列的关系

7.2链式访问

把一个函数的返回值作为另外一个函数的参数
链式访问的条件:函数必须有返回值

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{int len = strlen("abcdef");printf("%d\n", len);printf("%d\n", strlen("abcdef"));                //链式访问printf("%d", printf("%d", printf("%d", 43)));    //printf的返回值为打印在屏幕上字符的个数return 0;
}

8.函数的声明与定义

8.1函数声明

  • 函数声明是告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了
  • 函数的声明一般出现在函数的使用之前。要满足先声明后使用
  • 函数的声明一般要放在头文件中的
int Add(int x, int y);                //函数的声明,结尾加分号
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);//加法int sum = Add(a, b);printf("%d\n", sum);return 0;
}
//函数的定义(如果不进行函数声明,程序可以编译运行,但是会出现警告)
int Add(int x, int y)
{return x + y;
}

以上用法不常见,声明往往放在头文件中,定义与执行(#include “文件名” 不用<>)在不同源文件中

8.2函数定义

#ifndef __ADD_H__
#define __ADD_H__int Add(int x,int y);
#endif                //

9.函数递归

9.1 什么是递归?

  • 程序调用自身的编程技巧称为递归( recursion)递归做为一种算法在程序设计语言中广泛应用。一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量
  • 递归的主要思考方式在于:把大事化小

03-01.png

9.2 递归的必要两个条件

  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续
  • 每次递归调用之后越来越接近这个限制条件

9.3 练习

1.接受一个整型值(无符号),按照顺序打印它的每一位 。例如:输入:1234 输出 1 2 3 4

void print(unsigned int n)
{if (n > 9){print(n/10);}printf("%d ", n % 10);
}
int main()
{unsigned int num=0;scanf("%u", &num);print(num);//接受一个整型值(无符号),按照顺序打印它的每一位。return 0;
}

2.编写函数不允许创建临时变量,求字符串的长度(也就是让我们模拟strlen函数功能功能)

define _CRT_SECURE_NO_WARNINGS
include<stdio.h>
int my_strlen(char* str)//参数部分写成指针形式
{if (*str != '\0')return 1 + my_strlen(str + 1);//str++不可以,传进去的还是a地址elsereturn 0;
}
int main()
{char arr[] = "abc";//char*int len = my_strlen(arr);printf("%d\n", len);//3return 0;
}

9.4 递归与迭代

1
//练习4:求第n个斐波那契数。(不考虑溢出)

//代码演示:
define _CRT_SECURE_NO_WARNINGS
include<stdio.h>
//递归实现
int Fib(int n)
{if (n >= 3)return Fib(n - 2) + Fib(n - 1);elsereturn 1;
}
//迭代实现
int Fib(int n)
{int a = 1;int b = 1;int c = 0;while (n >= 3){c = a + b;a = b;b = c;n--;}return c;//n=1 n=2时返回c}
int main()
{int n = 0;scanf("%d", &n);int ret = Fib(n);printf("ret = %d\n", ret);return 0;
}
http://www.dtcms.com/a/274646.html

相关文章:

  • 翱翔的智慧之翼:Deepoc具身智能如何赋能巡检无人机“读懂”工业现场
  • 电脑开机显示器不亮是怎么回事 这有解决方法
  • 数据治理到底是什么?搞清这四件事,你就彻底明白了!
  • python的病例管理系统
  • 非栈上格式化字符串漏洞(BSS段上)
  • openbmc pldmd分析(event事件分析)
  • 项目开发日记
  • 《财税企业经营管理秘籍(一):行业适配的获客方式》
  • 电商系统高并发订单支付问题:队列、限流、容错全方位解决方案
  • JAVA JVM垃圾收集
  • 上半年净利预增66%-97%,高增长的赛力斯该咋看?
  • 解决Vue页面黑底红字遮罩层报错:Unknown promise rejection reason (webpack-internal)
  • Semi-Supervised Single-View 3D Reconstruction via Prototype Shape Priors
  • LDO选型
  • 手把手一起使用Miniforge3+mamba平替Anaconda(Win10)
  • 【web应用】若依框架中,使用Echarts导出报表为PDF文件
  • Linux中LVM逻辑卷扩容
  • 第七章 愿景05 莹姐画流程图
  • 企业采购成本越来越贵?根源在哪,数据怎么分析?
  • Linux操作系统从入门到实战:怎么查看,删除,更新本地的软件镜像源
  • Python 类型注解实战:`Optional` 与安全数据处理的艺术
  • 递归与树形结构在前端的应用
  • 林吉特危机下的技术革命:马来西亚金融系统升维作战手册
  • 【深度探究系列(5)】:前端开发打怪升级指南:从踩坑到封神的解决方案手册
  • U-Net网络学习笔记(1)
  • ARM单片机OTA解析(二)
  • cesium添加原生MVT矢量瓦片方案
  • 在 Spring Boot 中使用 WebMvcConfigurer
  • 【SpringBoot】配置文件学习
  • linux kernel struct regmap_config结构详解