【C语言】函数详解_c语言函数大全及详解-程序员宅基地

技术标签: c语言  【C语言】  开发语言  

目录

一、函数的定义和函数的分类

1. 函数的定义

2. 函数的分类

二、库函数和自定义函数

1. 库函数

2. 自定义函数

三、函数的参数

1. 实际参数(实参)

 2. 形式参数(形参)

 四、 函数的调用

1. 传值调用

2. 传址调用

 五、 函数的嵌套调用和链式访问

1. 函数的嵌套调用

 2. 链式访问

 六、 函数的声明和定义

1. 函数声明

2. 函数定义

七、函数递归和迭代

1. 什么是递归?

 2. 递归的两个必要条件

3. 递归与迭代 


一、函数的定义和函数的分类

1. 函数的定义

说到函数,在数学中,我们会想到函数的自变量和因变量

其中在维基百科中对函数的定义为:子程序

(1)在计算机中,子程序,是一个大程序中的某部分代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。

(2)一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。

2. 函数的分类

(1)库函数

(2)自定义函数

二、库函数和自定义函数

1. 库函数

(1) 什么是库函数

在我们平时学习C语言时,经常会用到具有一些功能的函数,比如平时想要打印结果到屏幕上的  printf (格式化输出函数,最后一个字母f format),获取键盘的输入 scanf,计算字符串长度的 strlen.

像上面完成一定基础功能的,它们不是业务性的代码,都是平时我们每个程序员又经常使用的。

所以为了提高我们平时的工作效率,C语言基础库中提供了一些类似的库函数,方便程序员进行软件开发。

库函数一般是C语言标准规定好,由编译器厂家提供实现。

(2)如何学习库函数?

强烈推荐

首先,我们先进 www.cplusplus.com 进去后点击 REFERENCE 如图

在C Library 中 会看到,我们经常使用的 stdio.h 这样的头文件等

 点击进去,在左侧栏中会看到一些常用的函数,

继续点击可以看到含函数的详解,如图

 简单对库函数总结:其中常见的库函数:

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

注意:使用库函数,一定要包含 #include 对应的头文件 ,一定不要忘记了

2. 自定义函数

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

函数的定义形式

ret_type fun_name(para1, * )
{
statement;//语句项
}
ret_type 返回类型
fun_name 函数名
para1   函数参数
类型说明符 函数名( 形参类型 参数1,形参类型 参数2,......)
{
    stamens;
    //语句项
    
}

(1)【举个例子】

写一个可以在两个数中找出最大值的函数。

#include <stdio.h>
//get_max函数的设计
int get_max(int x, int y)
{
    return (x>y)?(x):(y);
}
int main()
{
    int num1 = 10;
    int num2 = 20;
    int max = get_max(num1, num2);
    printf("max = %d\n", max);
    return 0;
}

(2) 再来个例子【重点来了】

写一个可以交换两个整型变量的函数

//实现成函数,但是不能完成任务
void Swap1(int x, int y)
{
	int tmp = 0;
	tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int num1 = 1;
	int num2 = 2;
	Swap1(num1, num2);
	printf("Swap1::num1 = %d num2 = %d\n", num1, num2);
	return 0;
}

注意代码有问题,存在对参数理解的错误,正确代码请看下方

#include <stdio.h>
//正确的版本
void Swap2(int* px, int* py)
{
	int tmp = 0;
	tmp = *px;
	*px = *py;
	*py = tmp;
}
int main()
{
	int num1 = 1;
	int num2 = 2;
	Swap2(&num1, &num2);
	printf("Swap2::num1 = %d num2 = %d\n", num1, num2);
	return 0;
}

接下来函数的参数详细讲解参数的问题

三、函数的参数

1. 实际参数(实参)

实参:真实传给函数的参数(也就是数学中的自变量)。

参数可以是:常量,变量,表达式,函数等

但是,在进行函数调用的时候,它们都必须有确定的值,以便传给形参。

在上述的求和代码

 2. 形式参数(形参)

形式参数 是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。

形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。

 总结:形参实例化后相当于实参的的一份临时拷贝

 四、 函数的调用

1. 传值调用

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

当我们写一个函数的时候,写一个打印 两数相加函数 add(),那么函数调用需要两个数值,这里比如传 3和5的和 ,那么add(3,5)

2. 传址调用

(1)传址调用 是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。

(2)这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操
作函数外部的变量。

所以上述 那个交换两个数的代码,函数需要传址调用,才能修改外部变量的值

(3)函数练习

1. 写一个函数可以判断一个数是不是素数。

#include <stdio.h>
int Is_prime(int n) 
{
	int i = 0;
	for (i = 2; i < n;i++)
	{
		if (n%i == 0)
		{
			return 0;//不是素数返回0
		}
	}
	return 1;//for循环完成后,如果是素数返回1
}
int main() 
{
	int n = 0;
	scanf("%d",&n);
	if (Is_prime(n))
		printf("%d is prime", n);
	else
		printf("%d is not prime", n);
	return 0;
}

素数就是只能被1和它本身整除的正整数,那么能被其他数(不包括1和它本身)整除就不是素数,拓展一下,一个数被其他数整除,

比如 16  ,16 = 2 * 8; 16 =  4 * 4; 16 = 8 * 2;  这里可以看出一个数不是素数要计算2,4,8

那木其实只需计算到  \sqrt[]{16} = 4,因为 2*8 与 8*2 一个满足 那么就不是素数。就 i <= 一个数的开平方,i<= \sqrt[]{n}。根据此原理下方代码为

求素数方法二

#include <stdio.h>
#include <math.h>
int Is_prime(int n)
{
	int i = 0;
	for (i = 2; i <=sqrt(n); i++)
	{
		if (n % i == 0)
		{
			return 0;
		}
	}
	return 1;
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	if (Is_prime(n))
		printf("%d is prime", n);
	else
		printf("%d is not prime",n);
	return 0;
}

2. 写一个函数判断一年是不是闰年。

#include <stdio.h>
int is_leap_year(int y)
{
	if (y % 4 == 0 && y % 100 != 0 || y % 400 == 0)
		return 1;
	else
		return 0;

}
int main() 
{
	int y = 0;
	scanf("%d",&y);
	if (is_leap_year(y))
		printf("%d is leap year\n", y);
	else
		printf("%d is not leap year\n",y);

	return 0;
}

普通闰年是指公历年份是4的倍数的且不是100的倍数,世纪闰年则必须是400的倍数。

3. 写一个函数,实现一个整形有序数组的二分查找。

#include <stdio.h>
int binary_search(int arr[],int num ,int sz)
{
	int left = 0;
	int right = sz - 1;
	int mid = 0;
	while (left<=right)
	{
		mid = (left + right) / 2; //可以优化为mid = left+(right-left)/2;
		if (arr[mid]>num)
		{
			right = mid - 1;
		}
		else if (arr[mid]<num)
		{
			left = mid + 1;
		}
		else 
		{
			return mid;
		}
	}
	return -1;
}
int main() 
{
	int arr[] = {1,2,3,4,5,6,7,8,9,10};
	int num = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);//数组元素的个数
	scanf("%d",&num);	//输入一个要查找的数
	int ret = binary_search(arr, num, sz);
	if (ret != -1)
		printf("找到了,该数下标为:%d",ret);
	else
		printf("没有找到 !");
	
}

4. 写一个函数,每调用一次这个函数,就会将 num 的值增加1。

#include <stdio.h>
void add(int * p)
{
	*p = *p + 1;
}
int main() 
{
	int num = 0;
	add(&num);//调用第一次,调用传的是地址
	printf("%d\n",num);
	add(&num);//第二次
	printf("%d\n",num);
	add(&num);//第三次
	printf("%d\n",num);
	return 0;
}

 五、 函数的嵌套调用和链式访问

1. 函数的嵌套调用

函数与函数之间可以相互调用的,注意 函数可以嵌套调用,但是不能嵌套定义

#include<stdio.h>
void test3() 
{
	printf("666\n");
}
void test1() 
{
	int i = 0;
	while (i < 3)
	{
		test3();
		i++;
	}
}
int main() 
{
	test1();
	return 0;
}

 2. 链式访问

把一个函数的返回值作为另一函数的参数 

#include <stdio.h>
int main()
{
  printf("%d", printf("%d", printf("%d", 43)));
  //注:printf函数的返回值是打印在屏幕上字符的个数
  return 0;
}

结果是:4321

 六、 函数的声明和定义

1. 函数声明

#include<stdio.h>
int add(int x, int y);
  • 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数
    声明决定不了。
  •  函数的声明一般出现在函数的使用之前。要满足先声明后使用
  • 函数的声明一般要放在头文件中的。

2. 函数定义

函数的定义是指函数的具体实现,交待函数的功能实现。

//函数的实现
int add(int x,int y)
{
    return x + y;
}

七、函数递归和迭代

1. 什么是递归?

递推+回归

程序调用自身的编程技巧称为递归

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

 2. 递归的两个必要条件

  • 有限制条件,条件满足限制条件时,递归停止
  • 每一次递归后,都接近这个限制条件

 【例】(1)接受一个整型值(无符号),按照顺序打印它的每一位

#include <stdio.h>
void print(int n)
{
	if (n>9)		//限制条件
	{
		print(n/10);	//每次递归越来越接近这个限制条件
	}
	printf("%d ",n%10);	//打印n 的每一位
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	print(n);
	return 0;
}

 

 图解 其中 5 -> 6 -> 7 -> 8 是打印顺序 2 0 2 3 

 【例】(2)编写函数不允许创建临时变量,求字符串的长度

#include <stdio.h>
int Strlen(char* str)
{
	if (*str == '\0')
		return 0;
	else
		return 1 + Strlen(str+1);//如果还有字符就继续递归判断下一个字符
}
int main()
{
	char *str = "abc";
	int len = Strlen(str);
	printf("%d\n",len);
	return 0;
}

 【图解】按照箭头和步骤耐心看,会有很大收获

3. 递归与迭代 

迭代也可以理解为循环

【例】求第n个斐波那契数(不考虑溢出)

斐波那契数 1 1 2 3 5 8 13 21 ......         第1,2个数为1,后一项等于前两项的和

下方代码是 用递归方式实现

#include <stdio.h>
int fib(int n)
{
	if (n <= 2)
		return 1;
	else
		return fib(n - 1) + fib(n - 2);
}
int main()
{
	int n = 0;
	scanf("%d",&n);
	int ret = fib(n);
	printf("%d\n", ret);
	return 0;
}

但是这样写会有一定的问题,那就是求第50以后的斐波那契数计算的时间很长,再去求一个非常大的数可能会发生 stack overflow 即栈溢出。

因为系统分配给程序的栈空间是有限的,容易出现死循环(死递归),这样有可能导致一
直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象我们称为栈溢出。

解决上述问题可以 使用

  • 递归改为非递归
  •  使用static对象替代 nonstatic 局部对象。

下方是求 非递归 方式实现

#include<stdio.h>
int fib(int n ) 
{
	int a = 1;
	int b = 1;
	int c = 1;
	while (n>2)
	{
		c = a + b;
		a = b;
		b = c;
		n = n - 1;
	}
	return c;
}
int main() 
{
	int n = 0;
	scanf("%d", &n);
	int ret = fib(n);
	printf("%d\n",ret);
	return 0;
}

所以平时 如果递归很容易想到,没有明显的bug时,可以使用递归。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_72505850/article/details/131939330

智能推荐

240320俄罗斯方块java,JAVA游戏编程之三----j2me 手机游戏入门开发--俄罗斯方块_2-程序员宅基地

文章浏览阅读202次。packagecode;//importjava.awt.*;//importjava.awt.Canvas;//importjava.awt.event.*;//importjavax.swing.*;importjava.util.Random;importjavax.microedition.lcdui.*;//写界面所需要的包/***//***俄罗斯方块*高雷*2007年1..._240×320java游戏

在线电影院售票平台(源码+开题报告)-程序员宅基地

文章浏览阅读779次,点赞14次,收藏19次。然后,实现系统的数据管理和服务功能,包括用户的注册与登录、电影的分类与展示、电影信息的查询与推荐、座位的选择与预订、在线支付与电子票生成等。此外,随着在线视频平台的兴起,越来越多的人选择在线观看电影,这对传统电影院产生了巨大的冲击。研究意义: 开发在线电影院售票平台对于提升用户的观影体验、优化电影院的运营效率、促进电影产业的发展具有重要的意义。该系统旨在通过技术手段解决传统电影院售票中的问题,提供一个集成化的电影信息展示、座位选择、在线支付和用户评价平台,同时也为电影院和电影制作方提供有效的工具。

程序员熬夜写代码,用C/C++打造一个安全的即时聊天系统!_基于c++的即时聊天系统设计-程序员宅基地

文章浏览阅读509次。保护我们剩下的人的通话信息安全,使用TOX可以让你在和家人,朋友,爱人交流时保护你的隐私不受政府无孔不入的的偷窥.关于TOX:其他牛逼的软件因为一些细化服务问你要钱的时候, TOX分文不取 . 你用了TOX, 想干嘛就干嘛.网友评论:项目源码展示:源码测试效果:最后,如果你学C/C++编程有什么不懂的,可以来问问我哦,或许我能够..._基于c++的即时聊天系统设计

linux Java服务swap分区被占用内存泄露问题故障及解决方法_linux swap占用很高-程序员宅基地

文章浏览阅读584次。鱼弦:CSDN内容合伙人、CSDN新星导师、全栈领域创作新星创作者 、51CTO(Top红人+专家博主) 、github开源爱好者(go-zero源码二次开发、游戏后端架构 https://github.com/Peakchen)当Java服务在Linux系统中运行时,可能会出现swap分区被占用的内存泄露问题,导致系统性能下降或者崩溃。下面是该问题的故障及解决方法、底层结构、架构图、工作原理、使用场景详解和实际应用方式、原理详细描述、相关命令使用示例以及文献材料链接。_linux swap占用很高

word中利用宏替换标点标点全角与半角-程序员宅基地

文章浏览阅读662次。Alt+F11,然后插入-模块:复制下面代码到编辑窗口:Sub 半角标点符号转换为全角标点符号()'中英互译文档中将中文段落中的英文标点符号替换为中文标点符号 Dim i As Paragraph, ChineseInterpunction() As Variant, EnglishInterpunction() As Variant Dim MyRange..._替换半角宏

Android WebView使用总结_android webview真正加载完成-程序员宅基地

文章浏览阅读2.8k次。#.简介: WebView是Android提供的用来展示展示web页面的View,内部使用webkit浏览器引擎(一个轻量级的浏览器引擎),除了展示Web页面外,还可与Web页面内的JS脚本交互调用。WebView内部的WebSetting对象负责管理WebView的参数配置; WebViewClient负责处理WebView的各种请求和通知事件,在对应事件发生时会执行WebViewClient的对应回调; ChromeWebviewClient辅助Webview处理与JS一些交互......_android webview真正加载完成

随便推点

bitcoin 调试环境搭建-程序员宅基地

文章浏览阅读1.6k次。_bitcoin 调试环境搭建

曲线生成 | 图解B样条曲线生成原理(基本概念与节点生成算法)-程序员宅基地

文章浏览阅读4.3k次,点赞93次,收藏94次。为了解决贝塞尔曲线无法局部修正、控制性减弱、曲线次数过高、不易拼接的缺陷,引入B样条曲线(B-Spline)。本文介绍B样条曲线的基本概念:节点向量、支撑性、次数阶数、加权性质、节点生成算法等,为后续曲线计算打下基础。_样条曲线生成

CDH安装宝典之ClouderaManager_/opt/cloudera/cm-agent/service/mgmt/mgmt.sh: line -程序员宅基地

文章浏览阅读902次。配置本地repo库下载我的阿里云盘文件文件放置#创建目录mkdir -p /opt/cloudera/parcel-repo/mkdir -p /opt/cloudera/cm/yum install createrepoCDH 6.2.0 的三个文件放到/opt/cloudera/parcel-repo/中,并且注意把sha256后缀的文件名修改为sha#执行createrepo命令生成rpm元数据 最终/opt/cloudera/parcel-repo/会多一个repodata目录_/opt/cloudera/cm-agent/service/mgmt/mgmt.sh: line 76: /usr/java/jdk1.8.0_181

uni.canvasToTempFilePath在app正常,微信小程序报错: fail canvas is empty-程序员宅基地

文章浏览阅读943次,点赞2次,收藏2次。uni.canvasToTempFilePath_uni.canvastotempfilepath

SDRAM笔记_sdram 干扰-程序员宅基地

文章浏览阅读3.1k次。SRAM :静态RAM,不用刷新,速度可以非常快,像CPU内部的cache,都是静态RAM,缺点是一个内存单元需要的晶体管数量多,因而价格昂贵,容量不大。DRAM:动态RAM,需要刷新,容量大。SDRAM:同步动态RAM,需要刷新,速度较快,容量大。DDR SDRAM:双通道同步动态RAM,需要刷新,速度快,容量大。........................_sdram 干扰

Excel转SQL语句_excel数据怎么生成sql语句-程序员宅基地

文章浏览阅读7.3k次。假设表格有A、B、C、D四列数据,希望导入到你的数据库中表格table,对应的字段分别是col1、col2、col3、col4。_excel数据怎么生成sql语句

推荐文章

热门文章

相关标签