C语言函数
- 一 函数的分类
- 举例:
- *比较两个整数的大小*
- *交换两个整数的值*(传地址)
- 二 参数
- 实参
- 形参
- 三 练习
- 1.写一个函数判断一个数是不是素数
- 2.写一个函数判断这一年是不是闰年
- 3.写一个函数实现一个整型有序数组的二分查找
- 4.写一个函数,每调用一次这个函数,就将num值+1
- 四 函数的嵌套
- 链式访问
- **拒绝传参**
- 五 函数的声明与定义
- 函数的声明
- 第三点,函数的声明一般放在头文件中
- 六 函数的递归
- 1.接受一个无符号整形,按顺序打印其每一位
- 2.编写函数不允许创建临时变量,求字符串的长度(很BT)
- 函数的递归与迭代(循环)
一 函数的分类
库函数:C语言自带的
总结下来就这几种
- IO函数
(输入输出 stdio.h) - 字符串操作函数
- 字符操作函数
- 内存操作函数
- 时间/日期操作函数
- 数字函数
- 其他库函数
自定义函数:自己定义函数
举例:
比较两个整数的大小
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int get_max(int x, int y) // 定义其函数
// 返回的值是int型的,所以开头是 int
// 比较的两个参数也是int型的,所以定义函数里面的参数也是int
// 无需返回就用 void
{
return (x > y ? x : y);
}
int main()
{
int a = 0, b = 0;
scanf("%d %d", &a, &b);
int max = get_max(a, b);
printf("最大值为:%d", max);
return 0;
}
交换两个整数的值(传地址)
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void Swap(int x, int y)
// x,y,z 为形式参数
{
int z = 0;
z = x;
x = y;
y = z;
}
int main()
{
int a = 0, b = 0;
// a,b为实参
scanf("%d %d", &a, &b);
printf("交换前a,b的值为%d,%d", a, b);
Swap(a, b);
printf("交换后a,b的值为%d,%d", a, b);
return 0;
}
12 22
交换前a,b的值为12,22
交换后a,b的值为12,22
此代码交换失败
交换失败的原因:
当实参传递给形参的时候,形参是实参的临时拷贝,对形参的修改不会影响实参
如果对其取 地址,修改值应该可以吧,对其代码进行修改
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void Swap(int* px, int* py)
// x,y,z 为形式参数
{
int z = *px; // z=a
*px = *py; // a=b
*py = z; // b=z
}
int main()
{
int a = 0, b = 0;
// a,b为实参
scanf("%d %d", &a, &b);
printf("交换前a,b的值为%d,%d\n", a, b);
Swap(&a, &b);
printf("交换后a,b的值为%d,%d\n", a, b);
return 0;
}
如果要改变实参的值,就要传地址,如果不只是让实参把值传递过来,就不用传地址
传址操作:可以在函数内部操作函数外部的变量
二 参数
实参
实际参数:真实传给函数的参数
可以是:常量,变量,表达式,函数等
无论实参是何种类型的量,在进行函数调用时,他们必须要有确定的值,以便把这些值传给形参。
形参
形式参数:指函数名后面括号内的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。
形式参数当函数调用完成之后就自动销毁了,因此形式参数只有在函数内有效
三 练习
1.写一个函数判断一个数是不是素数
素数:只能被1和它本身整除的数
题目:
输出100~200之间的素数,并统计其数量
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
int is_prime(int n)
// 是素数就返回1 ,不是素数就返回0
{
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))
// 1为素数,0不为素数
{
count++;
printf("%d ", i);
}
}
printf("\ncount=%d\n", count);
return 0;
}
输出结果:
101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199
count=21
2.写一个函数判断这一年是不是闰年
判断闰年的规则:
1.能被4整除,但不能被100整除
2.能被400整除
题目:
打印出1000~2000之间的闰年
不用函数的方法有两种
方法1:
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int year = 0;
for (year = 1000; year <= 2000; year++)
{
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
{
printf("%d ", year);
}
}
return 0;
}
方法2:
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int year = 0;
for (year = 1000; year <= 2000; year++)
{
if (year % 4 == 0)
{
if (year % 100 != 0)
{
printf("%d ", year);
}
}
if (year % 400 == 0)
// 如果用else if ,则会在if和else if直接二选一,比如2000则打印不出
{
printf("%d ", year);
}
}
return 0;
}
函数方法
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int is_leap_year(int x)
{
if (((x % 4 == 0) && (x % 100 != 0)) || (x % 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;
}
3.写一个函数实现一个整型有序数组的二分查找
一个数组为:{1,2,3,4,5,6,7,8,9,10}
查找数为k,代码里举例为7
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int binary_search(int arr[], int k, int sz)
// arr[]是个指针变量(首地址)
{
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;// 找到了,返回下标
}
}
return -1;//没找到,返回-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]);
// 找到了返回下标,找不到返回-1,因为0,1,等等与下标重叠了
int ret = binary_search(arr, k, sz);
if (ret == -1)
{
printf("没找到\n");
}
else
{
printf("找到了,下标为:%d\n", ret);
}
return 0;
}
4.写一个函数,每调用一次这个函数,就将num值+1
实质上,这题就是考验取地址,来改变实参的值罢了
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void ADD(int* p)
{
(*p)++;
}
int main()
{
int num = 0;
ADD(&num);
printf("%d\n", num);//1
ADD(&num);
printf("%d\n", num);//2
ADD(&num);
printf("%d\n", num);//3
return 0;
}
四 函数的嵌套
函数可以嵌套调用,不能嵌套定义
链式访问
把一个数的返回值作为另外一个函数的参数
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
printf("%d\n", strlen("abcdef"));
// 一条链
return 0;
}
打印“abcdef”字符串的长度
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
打印:
4321
printf()函数的返回值是打印字符的个数
拒绝传参
int main(void)
// void的意思是,不要给main函数传参数
{
return 0;
}
main函数里其实有三个参数
int main(int argc,char* argv[],char *envp[])
{
return 0;
}
五 函数的声明与定义
函数的声明
1.告诉编译器有一个函数,叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了
2.函数声明一般出现在函数的使用之前,要满足,先声明后使用。
3.函数的声明一般放在头文件中的。
第三点,函数的声明一般放在头文件中
以Add()函数为例
一 创建一个头文件,里面放Add函数的声明
二 创建一个源文件,里面放Add函数的定义
三 之后回到自己文件,如果要调用这个函数,直接调用头文件#include “Add.h”
#include <stdio.h>
// <> C语言中自带的
#include "Add.h"
// “ ” 自己编译的头文件
六 函数的递归
直接或间接的调用本身
递归的主要思考方式:把大事化小
递归的两个必要条件:
1.存在限制条件,当满足这个限制条件的时候,递归便不再继续
2.每次递归调用之后越来越接近这个限制条件
题目举例:
1.接受一个无符号整形,按顺序打印其每一位
比如:
输入:1234
输出: 1 2 3 4
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void print(unsigned int n)
{
if (n > 9)
{
print(n/10);
}
printf("%d ", n % 10);
}
int main()
{
unsigned int num = 0;
// unsigned int 无符号整型 就是自然数
// %u 无符号整型格式
scanf("%u", &num);
print(num);
// 接受一个整型,进入函数
return 0;
}
2.编写函数不允许创建临时变量,求字符串的长度(很BT)
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int my_strlen(char* str)
{
if (*str != '\0')
{
return 1 + my_strlen(str + 1);
}
else
{
return 0;
}
}
int main()
{
char arr[] = "abc";
int len = my_strlen(arr);
printf("%d\n",len);
return 0;
}