前言:
数学中常见到的函数,在C语言中也可以实现同样的功能
子程序:
在计算机科学中,子程序是一个大型程序中的某部分代码,由一个或多个语句块组成。
它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。
这些代码通常被集成为软件库
C语言标准规定函数的组成部分:
(1)、函数的功能 ---- 如:求字符串长度
(2)、函数名 -------- 如:strlen
(3)、函数参数 ------ 如:const char* str
组成:int strlen(const char* str);
库函数:是C语言标准中约定好的,由编译器的厂商提供实现。
通常封装一些经常频繁使用的功能,提升代码移植性和程序的执行效率
如:printf、scanf…
C语言中常见的库函数:
(1)、IO函数(输入输出函数)
(2)、字符串操作函数
(3)、字符操作函数
(4)、内存操作函数
(5)、时间/日期函数
(6)、数学函数
(7)、其他库函数
补充:一组或功能一类的都存入同一头文件中声明,如:stdio.h
pow次方函数例程
double pow(double base,double exponent);
参数:base ---- 基础值
参数:exponent — 阶乘
#include
#include //pow次方函数所需头文件
int main()
{
int n = 0;
n = (int)pow(2, 5);
printf("%d\n",n);
return 0;
}
char* strcpy(char* destination,const char* source)
参数:destination ----- 目标值
参数:source ---- 源值
#include
#include //strcpy次方函数所需头文件
int main()
{
//char arr1[20] = {0};
char arr1[20] = "xxxxxxxxxxxxxxxx";//方便监视'\0'
char arr2[] = "hello bit ";
strcpy(arr1,arr2);//拷贝检测到'\0'结束,多余xxx不打印
printf("%s\n", strcpy(arr1, arr2));//返回值就是destination
//printf("%s\n", arr1);
return 0;
}
void* memset(void* ptr,int value,size_t num);
//将value的数据,以num个数量(字节),存放在被ptr指向的空间地址里
参数:ptr ---- 设置指向的空间的值,单位字节
参数:value ---- 将要被设置的值
参数:num ---- 被指向空间的值的数量
#include
#include
int main()
{
char arr[] = "hello bit";
memset(arr, 'c', 5);
printf("%s\n",arr);
return 0;
}
用户自己定义执行需要的功能的函数,自定义函数
与库函数一样,有函数名、参数、返回值类型
格式:
ret_tpye fun_name(paral,*)
{
statement;//函数体
}
//ret_tpye ---- 返回值类型
//fun_name ---- 函数名
//paral ------- 函数参数
说明:写一个函数可以找出两个整数中的最大值
#include
int get_max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d",&a,&b);
int max = get_max(a,b);
printf("%d\n",max);
return 0;
}
说明:写一个函数可以交换两个整数的值
#include
void exchange(int* a,int* b)//以地址可正常传参 --- 传址
{
int temp = 0;
temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d",&a,&b);
printf("交换前:%d,%d\n",a,b);
exchange(&a,&b);
printf("交换后:%d,%d\n",a,b);
return 0;
}
说明:a,b传入函数为局部变量 ,传出来会失败,作用域超出范围
#include
void exchange(int a, int b)//形参(调试发现与传的实参,数值相同但地址不同)
{
int temp = 0;
temp = a;
a = b;
b = temp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:%d,%d\n", a, b);//3,5
exchange(a, b);//传的实际参数 --- 实参
//函数调用的时候,将实参传递给形参
//形参本质是实参的临时拷贝
//对形参的修改,不会改变实参
printf("交换后:%d,%d\n", a, b);//3,5 --- 结果没达到效果,局部变量
return 0;
}
真实传递的参数,即实参
实参可以是:常量、变量、表达式、函数等。
无论是何种类型,在函数进行调用时,它们必须都有确定的值,以便把值传给形参。
形式参数指函数名后括号里的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。
形式参数当函数完成调用之后就会自动销毁,因此只在函数内部有效,超出作用域失效。
说明:
写一个函数可以找出两个整数中的最大值 — 链式访问应用。
#include
int get_max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int max = get_max(a, b);
printf("%d\n", max);
get_max(3,5);
get_max(a,8+2);
get_max(a, get_max(3, 4));//函数的参数可以作为另一个函数的参数
return 0;
}
小结:
形参实例化之后相当于实参的一份临时拷贝;
实参可以是:常量、变量、表达式、函数等。
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
传址调用是把函数外部创建的变量的内存地址传递给函数的参数的一种调用函数的方式。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
说明:
写一个函数打印100~200的素数
常规写法:
#include
int main()
{
int i = 0;
for (i = 100; i <= 200; i++)
{
int j = 0;
for (j = 2; j < i; j++)
{
if (i % j == 0)
printf("%d ", i);
}
}
return 0;
}
函数写法:以及sqrt开平方函数的应用
#include
#include //调用sqrt需调用的头文件
int is_prime(int n)
{
//拿2~sprt(n)之间的数试除
int j = 0;
//for (j = 2; j < n; j++)
//{
// if (n % j == 0)
// return 0;//能被除1和本身以外的数除,所以不是素数
//}
//优化
for (j = 2; j <=sqrt(n); j++)//sqrt开平方效率更高
{
if (n % j == 0)
return 0;//能被除1和本身以外的数除,所以不是素数
}
return 1;//是素数
}
int main()
{
int i = 0;
int count = 0;
for (i = 101; i <= 200; i+=2)//优化
{
if (is_prime(i))
{
count++;
printf("%d ",i);
}
}
printf("\ncount = %d",count);
return 0;
}
补充:
C语言中有一个布尔类型 — 由C99中引入
_Bool 布尔类型的变量只有两种取值,true / false (真/假)
布尔写法:
#include
#include //调用sqrt需调用的头文件
#include //调用布尔类型需调用的头文件
bool is_prime(int n)//bool布尔类型
{
int j = 0;
//优化
//拿2~sprt(n)之间的数试除
for (j = 2; j <=sqrt(n); j++)
{
if (n % j == 0)
return false;//能被除1和本身以外的数除,所以不是素数
}
return true;//是素数
}
int main()
{
int i = 0;
int count = 0;
for (i = 101; i <= 200; i+=2)//优化
{
if (is_prime(i))
{
count++;
printf("%d ",i);
}
}
printf("\ncount = %d",count);
return 0;
}
说明:
写一个函数判断是否为闰年,打印1000~2000年之间的闰年 — 用闰年判断函数
#include
#include
//判断闰年函数
//bool is_leap_year(int y)
//{
// //能被4整除但不能被100整除的数,或能被400整除的数
// if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
// return true;
// else
// return false;
//}
//判断闰年函数 ---- 优化
bool is_leap_year(int y)
{
//能被4整除但不能被100整除的数,或能被400整除的数
return (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0));
}
int main()
{
int y = 0;
int count = 0;
for (y = 1000; y <= 2000; y++)
{
//判断y是否为闰年
if (is_leap_year(y))//真,则执行
{
count++;
printf("%d ",y);
}
}
printf("\ncount = %d",count);
return 0;
}
说明:
写一个函数实现一个整型的有序数组的二分查找
#include
//找到了,就返回下标
//找不到,返回-1
int binary_search(int arr[],int k,int sz)
{
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (arr[mid] < k)
left = mid + 1;
else if (arr[mid] > k)
right = mid - 1;
else
{
return mid;
}
}
return EOF;
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int k = 0;
scanf("%d",&k);//要查找的值
int sz = sizeof(arr)/sizeof(arr[0]);
int ret = binary_search(arr,k,sz);
if (ret == -1)
{
printf("找不到!\n");
}
else
{
printf("找到了,下标是: %d",ret);
}
return 0;
}
注意:
使用int mid = (left+right)/2;//有时是会出错的,不兼容大额数字
举例说明:溢出
#include
int main()
{
int num1 = 2147483646;
int num2 = 2147483644;
int avg = (num1 + num2) / 2;//-3 ----- 溢出最大值了
printf("%d\n",avg);
return 0;
}
所以优化:a+(b-a)/2;
#include
int main()
{
int num1 = 2147483646;
int num2 = 2147483644;
int avg = num1 + (num2 - num1) / 2;//2147483645
printf("%d\n", avg);
return 0;
}
函数的调用例程3 ------ 优化a+(b-a)/2
#include
//找到了,就返回下标
//找不到,返回-1
int binary_search(int arr[],int k,int sz)
{
int left = 0;
int right = sz - 1;
while (left <= right)
{
//int mid = (left + right) / 2;
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 EOF;
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int k = 0;
scanf("%d",&k);//要查找的值
int sz = sizeof(arr)/sizeof(arr[0]);
int ret = binary_search(arr,k,sz);
if (ret == -1)
{
printf("找不到!\n");
}
else
{
printf("找到了,下标是: %d",ret);
}
return 0;
}
说明:
写一个函数,当调用这个函数使得num的值,+1
写法一:
#include
void Add(int* p)
{
*p += 1;
}
int main()
{
int num = 0;
scanf("%d",&num);
printf("加1前:%d\n", num);
Add(&num);
printf("加1后:%d\n",num);
return 0;
}
写法二:
#include
int Add(int n)
{
n += 1;
return n;
}
int main()
{
int num = 0;
scanf("%d", &num);
printf("加1前:%d\n", num);
int ret = Add(num);
printf("加1后:%d\n", ret);
return 0;
}
函数和函数之间可以根据实际的需求进行组合的,也就是相互调用
注意:函数可以嵌套调用,不可以嵌套定义
#include
void test2()
{
printf("可嵌套调用,不可嵌套定义\n");
}
void test()
{
test2();
}
int main()
{
test();
return 0;
}
#include
void test()
{
int test2()//不可嵌套定义
{
return 0;
}
}
int main()
{
test();
return 0;
}
一个函数的返回值,作为另一个函数的参数
#include
int main()
{
int len = strlen("abc");
printf("%d\n",len);
//一个函数的返回值,作为另一个函数的参数
printf("%d\n", strlen("abc"));
char arr1[20] = { 0 };
char arr2[] = "abc";
printf("%d\n",strlen(strcpy(arr1,arr2)));
return 0;
}
补充:
printf()的返回值 ------ 字符的个数
int printf(const char* format, …)
#include
int main()
{
int num = 123456;
printf("%d",printf("%d",printf("%d",num)));
//123456
//printf("%d", printf("%d", 6));
//1234566
//printf("%d", 1);
//12345666
//123456661
return 0;
}
#include
int main()
{
printf("%d",printf("%d",printf("43")));//4321
return 0;
}
学习好函数,对于思维能力是非常有用的,能够帮助程序员在编写程序时,使用恰当能够大大提高程序的模块化特性,有利于扩展和维护。
因此,编写程序时应该充分发挥函数的作用,并合理地搭配其他语句和特性使用,以便更好地保证程序的正确性、可靠性以及严谨性。