C语言函数详解

[C语言函数详解

C语言数组详解

C语言操作符详解

C语言初阶指针详解

C语言结构体详解




函数

一、函数的参数

1、实参(实际参数)
  • 真实传给函数的参数,叫实参。

  • 实参可以是: 常量变量表达式函数等。

  • 无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。

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

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

3、示例
  1. 使用函数求两个数中的较大值
int get_max(int a, int b)
{
	//int max = 0;
	//if (a >= b)return a;
	//else return b;
	return(a > b ? a : b);
}
int main()
{
	int a = 0;
	int b = 0;
	while (1)
	{	
		scanf("%d %d", &a, &b);
		int max = get_max(a, b);
		printf("较大的是:%d\n", max);
	}
	return 0;
}
  • 输出:

  •   23 65
      较大的是:65
    
  1. 使用函数交换两个整形变量的内容
// 形参
//void Exchange(int x, int y)
//{
//	int Temp = x;
//	x = y;
//	y = Temp;
//}
//实参传递给形参时,形参会拷贝一份实参的内容,而并不会改变实参
void Exchange(int* x, int* y)
{
	int Temp = *x;
	*x = *y;
	*y = Temp;
}
int main()
{
	int a = 0;
	int b = 0;
	while (1)
	{	
		scanf("%d %d", &a, &b);
		printf("交换前A=%d B=%d\n", a, b);
		//Exchange(a, b); //a,b为实参
		Exchange(&a, &b);
		printf("交换后A=%d B=%d\n", a, b);
	}
	return 0;
}
  • 输出:

  •   22 66
      交换前A=22 B=66
      交换后A=66 B=22
    
    • 实参传递给形参时,形参会拷贝一份实参的内容,而并不会改变实参

二、函数的调用

1、传值调用
  • 函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
2、传指调用
  • 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。

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

eg1、使用函数实现质数的判断
  • 质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数(规定1既不是质数也不是合数)。
#include <stdio.h>
//使用函数判断一个数是不是素数(质数)
#include <math.h>
//sqrt()开平方,返回值为double类型
int Prime(int A)
{
	int i = 0;
	for (i = 2; i <= (int)sqrt(A); i++)
	{
		if (A % i == 0)
		{
			return 0;//不是质数
		}
	}
	return 1;//是质数
}
int main()
{
	int a = 0;
	while (1)
	{
		scanf("%d", &a);
		if (Prime(a) == 1)
			printf("%d->是质数\n", a);
		else printf("%d->不是质数\n", a);
	}
	return 0;
}

输出:

3
3->是质数
4
4->不是质数
5
5->是质数
6
6->不是质数
sqrt()的使用

doubel sqrt(double A)

  • 包含在头文件include <math.h>

  • 返回值是其中参数的开平方(double类型)

eg2、利用函数打印1000 - 2000年之间的闰年
#include <stdio.h>
//打印1000-2000年之间的闰年
//闰年:1、能被四整除且不能被100整除;2、能被100整除
int main()
{
	int i = 0;
	for (i = 1000; i <= 2000; i++)
	{
		if (((i % 4 == 0) && (i % 100 != 0)) || (i % 400 == 0))
		{
			printf("%d ", i);
		}
	}
	return 0;
}

输出:

1004 1008 1012 1016 1020 1024 1028 1032 1036 1040 1044 1048 1052 1056 1060 1064 1068 1072 1076 1080 1084 1088 1092 1096 1104 1108 1112 1116 1120 1124 1128 1132 1136 1140 1144 1148 1152 1156 1160 1164 1168 1172 1176 1180 1184 1188 1192 1196 1200 1204 1208 1212 1216 1220 1224 1228 1232 1236 1240 1244 1248 1252 1256 1260 1264 1268 1272 1276 1280 1284 1288 1292 1296 1304 1308 1312 1316 1320 1324 1328 1332 1336 1340 1344 1348 1352 1356 1360 1364 1368 1372 1376 1380 1384 1388 1392 1396 1404 1408 1412 1416 1420 1424 1428 1432 1436 1440 1444 1448 1452 1456 1460 1464 1468 1472 1476 1480 1484 1488 1492 1496 1504 1508 1512 1516 1520 1524 1528 1532 1536 1540 1544 1548 1552 1556 1560 1564 1568 1572 1576 1580 1584 1588 1592 1596 1600 1604 1608 1612 1616 1620 1624 1628 1632 1636 1640 1644 1648 1652 1656 1660 1664 1668 1672 1676 1680 1684 1688 1692 1696 1704 1708 1712 1716 1720 1724 1728 1732 1736 1740 1744 1748 1752 1756 1760 1764 1768 1772 1776 1780 1784 1788 1792 1796 1804 1808 1812 1816 1820 1824 1828 1832 1836 1840 1844 1848 1852 1856 1860 1864 1868 1872 1876 1880 1884 1888 1892 1896 1904 1908 1912 1916 1920 1924 1928 1932 1936 1940 1944 1948 1952 1956 1960 1964 1968 1972 1976 1980 1984 1988 1992 1996 2000

使用函数:

#include <stdio.h>
//利用函数打印1000 - 2000年之间的闰年
int Get_Leap_Year(int year)
{
	//是闰年返回1
	//不是闰年返回0
	if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
		return 1;
	else 
		return 0;
}
int main()
{
	int i = 0;
	for (i = 1000; i <= 2000; i++)
	{
		if(Get_Leap_Year(i))
			printf("%d ", i);		
	}
	return 0;
}

输出:

1004 1008 1012 1016 1020 1024 1028 1032 1036 1040 1044 1048 1052 1056 1060 1064 1068 1072 1076 1080 1084 1088 1092 1096 1104 1108 1112 1116 1120 1124 1128 1132 1136 1140 1144 1148 1152 1156 1160 1164 1168 1172 1176 1180 1184 1188 1192 1196 1200 1204 1208 1212 1216 1220 1224 1228 1232 1236 1240 1244 1248 1252 1256 1260 1264 1268 1272 1276 1280 1284 1288 1292 1296 1304 1308 1312 1316 1320 1324 1328 1332 1336 1340 1344 1348 1352 1356 1360 1364 1368 1372 1376 1380 1384 1388 1392 1396 1404 1408 1412 1416 1420 1424 1428 1432 1436 1440 1444 1448 1452 1456 1460 1464 1468 1472 1476 1480 1484 1488 1492 1496 1504 1508 1512 1516 1520 1524 1528 1532 1536 1540 1544 1548 1552 1556 1560 1564 1568 1572 1576 1580 1584 1588 1592 1596 1600 1604 1608 1612 1616 1620 1624 1628 1632 1636 1640 1644 1648 1652 1656 1660 1664 1668 1672 1676 1680 1684 1688 1692 1696 1704 1708 1712 1716 1720 1724 1728 1732 1736 1740 1744 1748 1752 1756 1760 1764 1768 1772 1776 1780 1784 1788 1792 1796 1804 1808 1812 1816 1820 1824 1828 1832 1836 1840 1844 1848 1852 1856 1860 1864 1868 1872 1876 1880 1884 1888 1892 1896 1904 1908 1912 1916 1920 1924 1928 1932 1936 1940 1944 1948 1952 1956 1960 1964 1968 1972 1976 1980 1984 1988 1992 1996 2000
eg3、函数实现整形有序数组的二分查找
#include <stdio.h>
//函数实现整形有序数组的二分查找
int Search_Array(int *arr, int a, int sz)
{
	int left = 0;
	int right = sz - 1;
  //找到了返回下标
  //找不到返回-1(数组第一个元素下标为0)
	while (left <= right)
	{
		int mid = left + (right - left) / 2;
		if (arr[mid] > a)
			right = mid - 1;
		else if (arr[mid] < a)
			left = mid + 1;
		else
			return mid;
	}
	return -1;
}
int main()
{
	int arr[] = { 1,3,4,6,7,8,9,10,11,12,13,15,16,17,19,20 };
	int a = 6;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int Sub = Search_Array(arr, a, sz);
	if (Sub == -1)
		printf("数组中没有这个数\n");
	else
		printf("%d在数组中的下标是%d\n", a, Sub);
	return 0;
}

输出:

6在数组中的下标是3

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

1、函数的嵌套调用

函数的嵌套调用

void pr1()
{
	printf("***********\n");
}
void pr2()
{
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		pr1();
	}
}
int main()
{
	pr2();
	return 0;
}
  • 输出:

  •   ***********
      ***********
      ***********
      ***********
      ***********
    

函数可以嵌套调用但是不能嵌套定义

嵌套定义:❌❌❌

void pr2()
{
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		void pr1()//嵌套定义Err
 		{
			printf("***********\n");
 		}
	}
}
int main()
{
	pr2();
	return 0;
}

无法运行!!!

2、函数的链式访问

一个函数的返回值可以作为其它函数的参数

int main()
{
	printf("%d", printf("%d", printf("%d", 43)));
	return 0;
}
  • 输出结果:

  •   4321
    
printf()的返回值

printf()返回打印的字符数,如果发生错误,则返回负值。

四、函数的声明和定义

在C语言中,函数的声明和定义是两个不同的概念,它们在不同的文件中或同一文件的不同部分进行。

#include <stdio.h>

int add(int a, int b);//函数的声明

int main()
{
	int a = 10;
	int b = 20;
	printf("%d", add(a, b));

	return 0;
}

int add(int a, int b)//函数的定义
{
	return (a + b);
}
  •   30
    

1、函数声明

  • 函数声明告诉编译器函数的名称返回类型参数类型数量(不包含函数体的实现代码)。

  • 函数声明通常放在头文件中或在需要调用函数之前进行声明。要满足先声明后使用

示例:

int add(int a, int b);

这里,add 是函数名,int 是返回类型,括号中的 int a, int b 是参数列表。

2、函数定义

函数定义包括函数的实现代码。它包含了函数体,即函数执行的具体操作。

示例:

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

这里,add是函数名,int是返回类型,括号中的 int a, int b是参数列表,大括号{}内是函数体,包含返回语句 return a + b;

3、函数声明与定义的关系

  • 函数声明:告诉编译器函数的存在和参数类型,但不提供实现。
  • 函数定义:提供函数的具体实现。
函数声明的位置

通常将函数声明放在头文件中,这样其他文件可以通过包含这个头文件来知道函数的存在和参数类型。

示例:

// file: add.h
#ifndef ADD_H
#define ADD_H

int add(int a, int b);

#endif
  • 使用静态库时的.h文件

  •   #pragma once//防止头文件被重复包含
      #pragma comment(lib,"add.lib")//导入静态库
      
      
      int Add(int a, int b);//函数的声明
    

然后在需要使用这个函数的文件中包含这个头文件:

// file: main.c
#include <stdio.h>
#include "add.h"

int main() 
{
    int result = add(3, 4);
    printf("Result: %d\n", result);
    return 0;
}
函数定义的位置

函数定义通常放在源文件中,例如 .c 文件。

示例:

// file: add.c
#include "add.h"

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

这样,通过编译这些文件,编译器会将它们链接在一起,形成一个完整的程序。

五、函数递归

  • 递归是一种编程技术,一个函数直接或间接地调用自己。

  • 递归函数可以用于解决许多问题,特别是那些可以分解为相似子问题的问题。

  • 递归通常用于实现分治算法深度搜索树的遍历等。

1、递归函数的特点

  1. 基本条件:递归函数必须有一个或多个基本条件(也称为终止条件),当满足这些条件时,函数将停止递归调用。
  2. 递归步骤:除了基本条件外,函数还需要包含递归调用,即函数调用自身的代码。
  3. 返回值:递归函数通常需要返回一个值,这个值可以是递归调用的结果。
  4. 每次递归调用之后会越来越接近限制条件。

2、递归的工作原理

递归函数通过不断调用自己来解决问题。每次调用都会将问题分解为更小的子问题,直到满足基本条件,然后逐步返回结果。(大事化小)

3、注意事项

  1. 避免无限递归:确保递归函数有一个明确的终止条件,否则会导致无限递归。
  2. 栈溢出:递归调用会消耗内存栈空间,如果递归深度过大,可能会导致栈溢出。
eg1、使用递归,接收一个整形值,按照顺序打印它的每一位
//使用递归
//接收一个整形值,按照顺序打印它的每一位
//例如:输入1234;输出4 3 2 1

void print(unsigned int num)//n=1234
{
	if (num > 9)
	{
		print(num / 10);//123
	}printf("%d ", num % 10);//4
}
int main()
{
	unsigned int num = 0;
	scanf("%u", &num);//%u是输入无符号整形
	print(num);
	return 0;
}

//print(1234)
//print(123) 4
//print(12) 3 4
//print(1) 2 3 4
  • 输出:

  •   1234
      1 2 3 4
    

请添加图片描述

eg2、模拟实现strlen(),返回字符串个数
  • 使用临时变量
//模拟实现strlen(),返回字符串个数

//int my_strlen(char str[])//参数部分写成指针的形式
int my_strlen(char str[])  //参数部分写成数组的形式
{
	int count = 0;//计数,使用临时变量
	while (*str != '\0')
	{
		count++;
		str++;//找下一个字符
	}
	return count;
}
int main()
{
	char str[] = "abcd";//[a b c d \0]
	printf("%d", my_strlen(str));
	return 0;
}
  • 输出:

  •   4
    
  • 使用递归,不创建临时变量

//不创建临时变量使用递归
int my_strlen(char* str)
{
	if (*str != '\0')
		return 1 + my_strlen(str + 1);
	else
		return 0;
}
int main()
{
	char str[] = "abcd";//[a b c d \0]
	printf("%d", my_strlen(str));
	return 0;
}
  • 输出:

  •   4
    

请添加图片描述

strcmp()比较字符串
  • 包含于<string.h>头文件中

  • 用于判断两个 字符串(字符数组) 是否相等

  • 使用方法

    • strcmp(字符串1,字符串2) == 0,则字符串1字符串2相等,反之则不相等

getchar()返回ASCII码

getchar()函数实际上是int getchar(void),所以它返回的是ASCII码

int main()
{
	char ch;
	printf("请输入字符:");//1 2 3 回车
	scanf("%c", &ch);// 读取 1
	printf("%c", getchar());// 读取并输出 2
	printf("%c", getchar());// 读取并输出 3
	printf("%c", getchar());// 读取并输出 回车
}
  • getchar()可以用来处理\n

3、递归与迭代

  1. 斐波那契数列问题
  2. 汉诺塔问题
  3. 青蛙跳台阶问题

1. 求n!(n的阶乘)

  • 使用递归n!

  •   //使用递归求n!
      int fac(int n)
      {
      	//if (n > 0)
      	//	return (n * fac(n - 1));
      	//else
      	//	return 1;
      
      	if (n <= 1)
      		return 1;
      	else
      		return n * fac(n - 1);
      }
      int main()
      {
      	int n = 0;
      	while (1)
      	{
      		scanf("%d", &n);
      		printf("%d!=%d\n", n, fac(n));
      	}	
      	return 0;
      }
    
    • 输出:

    •   3
        3!=6
        4
        4!=24
        5
        5!=120
      
  • 使用迭代n!(非递归)

  •   //使用迭代求n!
      int fac(int n)
      {
      	int dev = 1;
      	while(n > 0)
      	{
      		dev *= n;
      		n--;
      	}
      	return dev;
      }
      int main()
      {
      	int n = 0;
      	while (1)
      	{
      		scanf("%d", &n);
      		printf("%d!=%d\n", n, fac(n));
      	}
      	return 0;
      }
    
    • 输出:

    •   4
        4!=24
        5
        5!=120
        6
        6!=720
      

2. 求第n个斐波那契数(Fibonacci sequence)

斐波那契数列又称黄金分割数列(兔子数列)是指这样一个数列: 1,1,2,3,5,8,13,21,34,55,89……这个数列从第3项开始 ,每一项都等于前两项之和。

  • 使用递归

  •   //求第n个斐波那契数(Fibonacci sequence)
      /斐波那契数列(兔子数列)是指这样一个数列:
       1123581321345589……
       这个数列从第3项开始 ,每一项都等于前两项之和。*/
      int fibona(int n)
      {
      	if (n <= 2)
      		return 1;
      	else
      		return fibona(n - 1) + fibona(n - 2);
      }
      //20
      //19          18
      //18    17    17    16
      //17 16 16 15 16 15 15 14
      //......
      int main()
      {
      	int n = 0;
      	while (1)
      	{
      		scanf("%d", &n);
      		printf("第%d个斐波那契数是:%d\n", n, fibona(n));
      	}
      	return 0;
      }
    
    • 输出:

    •   55个斐波那契数是:5
        66个斐波那契数是:8
        77个斐波那契数是:13
        88个斐波那契数是:21
        99个斐波那契数是:34
      
    • 缺点: 使用递归来求斐波那契数列效率很低,随着n的增加,所需要计算的次数也在指数级增长

      20
      19          18
      18    17    17    16
      17 16 16 15 16 15 15 14
      ......
      
  • 使用迭代

  •   //迭代
      int fibona(int n)
      {
      	int n1 = 1;
      	int n2 = 1;
      	int seq = 0;
      	if (n <= 2)
      		return 1;
      	while (n >= 3)
      	{
      		seq = n1 + n2;
      		n2 = n1;
      		n1 = seq;
      		n--;
      	}
      	return seq;
      }
      int main()
      {
      	int n = 0;
      	while (1)
      	{
      		scanf("%d", &n);
      		printf("第%d个斐波那契数是:%d\n", n, fibona(n));
      	}
      	return 0;
      }
    
    • 输出:

    •   1010个斐波那契数是:55
        1111个斐波那契数是:89
        1212个斐波那契数是:144
        1313个斐波那契数是:233
        1414个斐波那契数是:377
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值