文章目录
1. 函数是什么?
数学中我们常见到函数的概念。但是你了解C语言中的函数吗?维基百科中对函数的定义:子程序。
- 在计算机科学中,子程序是一个大型程序中的某部分代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。
- 一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
C语言中函数的分类:
- 库函数
- 自定义函数
2. 库函数
查阅C语言库函数的网站:cplusplus.com
学习C语言的网站:en.cppreference.com
C语言常用的库函数有:
- IO函数
- 字符串操作函数
- 字符操作函数
- 内存操作函数
- 时间/日期函数
- 数学函数
- 其他库函数
2.1 字符串拷贝
//#include<string.h>
//
//int main()
//{
// char arr1[] = "bit";
// char arr2[20] = "#########";
// strcpy(arr2, arr1);
// //这里把'\0'也拷贝过去了,其实拷贝后arr2中放的是【bit\0#####】
// //只打印【bit】是因为,'\0'是字符串结束标志,即遇到'\0'就停止打印了
// printf("%s\n", arr2); //bit
// //strcpy - string copy - 字符串拷贝
// //strlen - string length - 字符串长度有关
// return 0;
//}
2.2 memset
#include<stdio.h>
#include <string.h>
//memset
//memory - 内存 set - 设置
int main()
{
char arr[] = "hello world";
memset(arr, '*', 5);
printf("%s\n", arr); //***** world
return 0;
}
3. 自定义函数
//int Add(int x, int y)
//{
// return x + y;
//}
//
//int main()
//{
// int a = 10;
// int b = 20;
// int sum = Add(a, b);
// printf("%d\n", sum);
// return 0;
//}
交换a和b的值
错误写法:
错误原因:a,b和x,y在内存中的地址不一样,在swap函数中只是交换了x,y中存放的值,并没有a,b中存放的值。
正确写法:
3.1 实参与形参
实际参数
真实传递给函数的参数。可以是:常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,他们都必须有确定的值,以便把这些值传送给形参。
形式参数
即函数名后括号中的变量,形式参数只有在函数被调用的过程中才实例化(分配内存单元)。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
3.2 函数调用
传值调用
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
传址调用
- 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
- 这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
3.3 练习
计算素数和
描述:输入两个正整数m和n(m<n),求m到n之间(包括m和n)所有素数的和,要求定义并调用函数isprime(x)来判断x是否为素数(素数是除1以外只能被自身整除的自然数)。https://www.dotcpp.com/oj/problem1054.html
#include<stdio.h>
int isPrime(int num) {
if (num == 1) return 0;
if (num == 2) return 1;
for (int i = 2; i < num; i++)
{
//如果num能被除1和num之外的数整除 --> 不是素数
if (num % i == 0)
{
return 0;
}
}
return 1;
}
int main()
{
int m = 0;
int n = 0;
int sum = 0;
scanf("%d %d", &m, &n);
for (int i = m; i <= n ; i++)
{
if (isPrime(i))
{
sum += i;
}
}
printf("%d\n", sum);
return 0;
}
判断闰年
https://www.nowcoder.com/practice/a7bcbe3cb86f435d9617dfdd20a16714?tpId=290&tqId=39843&ru=/exam/oj
#include<stdio.h>
int isLeapYear(int y)
{
//如果y能被4整除且不能被100整除 or y能被400整除,则y是闰年
if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0)
{
return 1;
}
return 0;
}
int main()
{
int year=0;
scanf("%d", &year);
if (isLeapYear(year))
{
printf("yes\n");
}
else
{
printf("no\n");
}
return 0;
}
二分查找
https://leetcode.cn/problems/binary-search/
int search(int* nums, int numsSize, int target){
int left=0;
int right=numsSize-1;
while(left<=right)
{
int mid=(left+right)/2;
if(nums[mid]>target)
{
right=mid-1;
}
else if(nums[mid]<target)
{
left=mid+1;
}
else
{
return mid;
}
}
return -1;
}
每调用一次函数,num加1
#include<stdio.h>
void Add(int* p)
{
(*p)++; //++的优先级比*高,这里是对p的值进行+1,而不是对p的地址进行+1,所以要使用括号
}
int main()
{
int num = 0;
Add(&num);
printf("num=%d\n", num);//1
Add(&num);
printf("num=%d\n", num);//2
Add(&num);
printf("num=%d\n", num);//3
return 0;
}
4. 函数的链式访问
把一个函数的返回值作为另外一个函数的参数。
#include<stdio.h>
#include <string.h>
int main()
{
char arr[20] = "hello";
//strcat(s1,s2) -> 将字符串s2连接到字符串s1后面
int ret = strlen(strcat(arr, "bit")); //8
printf("%d", ret);
return 0;
}
//int main()
//{
// printf("%d", printf("%d", printf("%d", 43))); //4321
// //结果是啥?
// //注:printf函数的返回值是打印在屏幕上字符的个数
// return 0;
//}
5. 函数的声明和定义
add.h
放置函数的声明
//函数的声明
int Add(int x, int y);
add.c
放置函数的实现
//函数Add的实现
int Add(int x, int y)
{
return x+y;
}
test.c
对函数进行测试
//引入头文件
#include"add.h"
int main()
{
int a = 1;
int b = 2;
int c = Add(a, b);
printf("%d\n", c);
return 0;
}
6. 递归练习
即自身调用自身。
递归的两个必要条件
- 出口
- 自身反复调用自身
打印整数各位
接受一个整型值,按顺序打印它的每一位。
输入:1234
输出:1 2 3 4
void print(int n)
{
if (n > 9)
{
print(n / 10);
}
printf("%d ", n % 10);
}
int main()
{
int num = 1234;
print(num);
//print(1234)
//print(123) 4
//print(12) 3 4
//print(1) 2 3 4
return 0;
}
图解:
最后一个print函数执行完后,要回到调用它的地方继续执行,即回到上一个print函数的最后一行代码处(这里是打印2 )。
求字符串长度
编写函数不允许创建临时变量,求字符串的长度。
int my_strlen(char* str)
{
if (*str == '\0')
return 0;
str++;
return my_strlen(str)+1;
}
int main()
{
char arr[] = "abc";
int len = 0;
len = my_strlen(arr);//arr是数组,数组传参,传过去的不是整个数组,而是第一个元素的地址
printf("%d\n", len);
return 0;
}
图解:
求n的阶乘
int jiecheng(int n)
{
if (n <= 1)
return 1;
return n * jiecheng(n - 1);
}
int main()
{
int n = 3;
int ans = 0;
ans=jiecheng(3);
printf("%d\n", ans);
return 0;
}
图解:
求斐波那契数
求第n个斐波那契数。
int fib(int n)
{
if (n <= 2)
return 1;
return fib(n - 1) + fib(n - 2);
}
int main()
{
int n = 0;
int ans = 0;
scanf("%d", &n);
ans = fib(n);
printf("%d\n", ans);
return 0;
}
图解:
迭代改进:
采用迭代的方法改进代码,大幅减少时间复杂度。
//迭代方法
int fib(int n)
{
int a = 1;
int b = 1;
int tmp = 0;
if (n <= 2)
return 1;
while (n > 2)
{
tmp = a + b;
a = b;
b = tmp;
n--;
}
return b;
}
汉诺塔问题
https://www.dotcpp.com/oj/problem2056.html
图解:
伪代码:
//A:起始杆
//B:辅助杆
//C:目的杆
void hanoi(int n,char A,car B,char C){
if(n==1){
move(A,C)
}
else{
//step 1
hanoi(n-1,A,C,B);
//step 2
move(A,C);
//step 3
hanoi(n-1,B,A,C):
}
}
代码:
void move(int n,char A, char B)
{
printf("Move %d from %c to %c\n", n, A, B);
}
void hanoi(int n,char A,char B, char C)
{
if (n == 1)
{
move(n,A, C);
}
else
{
hanoi(n - 1, A, C, B);
move(n, A, C);
hanoi(n - 1, B, A, C);
}
}
int main()
{
int n = 0;
scanf("%d", &n);
hanoi(n, '1', '2', '3');
return 0;
}
青蛙跳台阶
https://leetcode.cn/problems/qing-wa-tiao-tai-jie-wen-ti-lcof/
提示(关键):
将 青蛙跳台阶 转化为 斐波那契数列问题
图解:
代码:
int numWays(int n){
if(n==0) return 1;
if(n<=2) return n;
return numWays(n-1)+numWays(n-2);
}
迭代改进:
int numWays(int n){
int a=1;
int b=2;
int tmp=0;
if(n==0) return 1;
if(n<=2) return n;
while(n>2)
{
tmp=a+b;
//这里取余操作是具体题目的要求,可忽略
a=b%1000000007;
b=tmp%1000000007;
n--;
}
return b;
}