我们已经学习了分支和循环语句的知识,今天我们将学习新的一个章节——函数,我们在初识C语言中曾写过一个函数用来求得两个整型变量的最大值,那么今天我们来研究一下函数到底是什么以及函数是怎么执行的。
目录:
1.
函数是什么2.
库函数3.
自定义函数4.
函数参数5.
函数调用6.
函数的嵌套调用和链式访问7.
函数的声明和定义8.
函数递归(函数递归我们将另外写一篇博客细说)
一·函数
1.1 函数是什么?
我们都知道,一个程序必须有main函数这个主体,这个main函数就是一个主函数,一个工程中只能有一个main函数,我们在main函数中书写我们所需要的语句项,当我们想在main函数内执行一些功能时,我们就会采用函数封装的方法,所以函数也可以称为子程序,是为了完成所需的功能的代码块。
1.2 C语言函数的分类
C语言中函数一般分为库函数和自定义函数。其中库函数是C语言本身所具有的,我们在书写代码时只需调用即可(调用时需要引用头文件),但是C语言并不能包含所有的函数,因为每个程序员的功能和需求不同,所以自定义函数的书写对于我们来说也尤为重要。
这里我将给大家讲解两个库函数的实现功能:
1.strcpy(字符串复制函数)
函数模型:char *
strcpy
(
char *
destination
,
const
char *
source
);
该函数的功能就是把一个字符串的内容复制给另一个字符串,注意参数的位置,被复制的字符串作为函数的第一个参数
//strcpy函数的实现
int main()
{
char arr1[10] = "abcdef";
char arr2[10] = { 0 };
strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
2.memset(内存设置函数)
函数模型:
void *
memset
(
void *
ptr
,
int
value
,
size_t
num
);
该函数的功能是把目标数组中第几个字符开始(即指针)向后num个字节设置为value
int main()
{
char arr1[] = "hello world!";
char arr2[] = "############";
memset(arr2, 'X', 5);
printf("%s\n", arr2);
return 0;
}
1.3 如何学习库函数?
C语言自带的库函数有很多,我们根据每一个函数模型中的参数可以大概猜测出该库函数所执行的功能以及参数的类型,但是我们如何准确的知道每一个库函数的实现以及示例呢?这里我给大家几个网站去学习库函数相关内容。
1.MSDN(Microsoft Developer Network)可自行去官网下载或者私信我发你安装包
2.www.cplusplus.com
3.http://en.cppreference.com(英文版)
4.
http://zh.cppreference.com
(中文版)
1.4 自定义函数
上面浅浅的学习了一下库函数的使用,下面自定义函数的使用和如何执行才是我们学习的主要目标,否则要我们程序员干嘛呢!
首先我们来看一下自定义函数的组成
ret_type
fun_name
(
para1
,
*
){statement
;
//
语句项}ret_type
返回类型fun_name
函数名para1
函数参数
实践是检验真理的唯一标准,下面我们来举一个例子来详细说明:写一个函数可以找出两个整数中的最大值。
int get_max(int x, int y)
{
return ((x > y) ? x : y);
}
int main()
{
int x = 0;
int y = 0;
scanf("%d %d", &x, &y);
int max = get_max(x, y);
printf("%d\n", max);
return 0;
}
有编译结果可知,我们求得了10 20之间的较大值为20,我们书写一个get_max函数来执行这个功能,其返回类型为int,因为我们在主函数中定义了一个int类型的变量max来接受函数执行之后的返回值
我们再举一个例子:写一个函数可以交换两个整形变量的内容
//实现成函数,但是不能完成任务
void Swap1(int x, int y) {
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}
//正确的版本
void Swap2(int* px, int* py) {
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}
int main()
{
int num1 = 1;
int num2 = 2;
Swap1(num1, num2);
printf("Swap1::num1 = %d num2 = %d\n", num1, num2);
Swap2(&num1, &num2);
printf("Swap2::num1 = %d num2 = %d\n", num1, num2);
return 0;
}
这里我写了两个版本Swap1和Swap2函数,其中Swap1函数传递的是变量num1和num2,而Swap2函数传递的是变量num1和num2的地址,为什么前者编译结果错误,后者编译结果正确呢?
原因是:
当我们把变量num的值传递给函数时,将会新建一个变量来接受传递过来的值,但是新建的变量和传递给函数的变量再内存中所占的内存空间并不相同(言下之意就是两个变量只是数值上相等而已),所以对新建变量的修改并不会改变变量num的值,所以我们可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝。
二·函数的参数
2.1 实际参数(实参):
在进行函数调用时,它们都必须有确定的值
,以便把这些值传送给形
2.2 形式参数(形参):
因为形式参数只有在函数被调用的过程中才实例化(分配内
,所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有 效。
Swap1
和
Swap2
函数中的参数
x
,
y
,
px
,
py
都是
形式参数
。在
main
函数中传给
Swap1
的
num1
,num2 和传给 Swap2
函数的
&num1
,
&num2
是
实际参数
。
三· 函数的调用
3.1 传值调用
3.2 传址调用
3.3 练习
1.打印100-200之间的素数
//1.普通版本
int is_prime(int i)
{
int j = 0;
//用2-i-1的数来试除i
for (j = 2; j < i; j++)
{
if (i % j == 0)
{
return 0;
}
}
return 1;
}
int main()
{
int count = 0;//计数
int i = 0;
for (i = 100; i <= 200; i++)
{
//若为素数返回1否则返回0
if (is_prime(i))
{
printf("%d ", i);
count++;
}
}
printf("\n count = %d\n", count);
return 0;
}
//2.优化版本
#include <math.h>
int is_prime(int i)
{
int j = 0;
//用2-i-1的数来试除i
for (j = 2; j <= sqrt(i); j++)
{
if (i % j == 0)
{
return 0;
}
}
return 1;
}
int main()
{
int count = 0;//计数
int i = 0;
for (i = 100; i <= 200; i++)
{
//若为素数返回1否则返回0
if (is_prime(i))
{
printf("%d ", i);
count++;
}
}
printf("\n count = %d\n", count);
return 0;
}
其中优化版本使用了sqrt这个函数,读者可自行查阅,需引用头文件math.h
2.打印1000-2000之间的闰年
//闰年
//1.能被4整除但是不能被100整除的数
//2.能被400整除的数
int is_leap_year(int i)
{
if ((i % 4 == 0 && i % 100 != 0) || i % 400 == 0)
{
return 1;
}
else
{
return 0;
}
}
int main()
{
int count = 0;//计数
int year = 0;
for (year = 1000; year <= 2000; year++)
{
//若为闰年返回1否则返回0
if (is_leap_year(year))
{
printf("%d ", year);
count++;
}
}
printf("\n count = %d\n", count);
return 0;
}
3.写一个函数,实现一个整形有序数组的二分查找
int Binary_search(int arr[],int k, int sz)
{
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;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 7;//假设所需查找的数是7
int sz = sizeof(arr) / sizeof(arr[0]);
//若查找成功则返回下标否则返回-1
int ret = Binary_search(arr,k, sz);
if (ret != -1)
{
printf("下标为:%d\n", ret);
}
else
{
printf("没找到\n");
}
return 0;
}
其中需要注意的是,数组的大小sz必须在主函数中计算之后传递给查找函数,因为数组传参时传递的是数组首元素的地址并不是整个数组
4.写一个函数,每调用一次这个函数,就会将 num 的值增加1
//传址调用
void Add(int* p)
{
int z = *p;
(*p)++;
return z;
}
int main()
{
int num = 0;
Add(&num);//因为需要改变num的值,所以采用传址调用
printf("%d\n", num);
Add(&num);
printf("%d\n", num);
Add(&num);
printf("%d\n", num);
return 0;
}
//传值调用
int Add(int num)
{
return (++num);//注意这里是++num而不是num++
}
int main()
{
int num = 0;
num = Add(num);//每次返回的值用num接收
printf("%d\n", num);
num = Add(num);
printf("%d\n", num);
num = Add(num);
printf("%d\n", num);
return 0;
}
四·函数的嵌套调用和链式访问
4.1 嵌套调用
void new_line()
{
printf("hehe\n");
}
void three_line()
{
int i = 0;
for (i = 0; i < 3; i++)
{
new_line();
}
}
int main()
{
three_line();
return 0;
}
我们在主函数中调用three_line函数 ,在three_line函数中调用new_line函数,在函数中调用函数这个操作我们称为函数的嵌套调用,所以结果打印出来为三个hehe
注意:函数可以嵌套调用,但是不能嵌套定义,因为对于任何一个函数来说 ,地位都是相等的,不能再函数中去定义另一个函数
4.2 链式访问
示例1:
#include <string.h>
int main()
{
char arr[] = "abcdef";
printf("%d\n", strlen(arr));
return 0;
}
显然结果是6,strlen是一个计算字符串长度的函数,我们将strlen函数执行的结果放在printf函数中打印出来了,这便是函数的链式访问
示例2:
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
//结果是啥?
//注:printf函数的返回值是打印在屏幕上字符的个数
return 0;
}
其中printf函数的返回值是打印在屏幕上字符的个数,那结果读者自行理解
五·函数的声明和定义
5.1 函数的声明
先声明后使用。
函数的声明一般要放在头文件中的。
5.2 函数的定义
函数部分我们只剩下一个函数的递归了,由于函数的递归较难,我将单独写一篇博客进行讲解。希望大家可以今天函数的内容消化吸收,加油,感谢支持!!!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/89469.html