前言与概述
笔者打算通过两篇文章详细介绍C语言函数的相关知识,本文章将会介绍C语言函数定义、C语言函数分类、函数参数、函数调用。下一篇文章将会介绍函数声明、链式访问,并详细介绍函数递归。笔者不才,如有错误,欢迎各位编程大佬在评论区批评指正。
C语言函数定义
维基百科中将C语言函数定义为子程序。
一个较大的程序通常由多个子程序组成。子程序有一条或多条语句块组成,主要负责完成特定的任务,不同的子程序之间具有相应的独立性。
函数通常有输入参数和返回值,提供对过程的封装和细节的隐藏,这些代码通常集成为软件库。
C语言函数分类
在C语言中,函数主要分为库函数和自定义函数。
库函数
库函数是C语言本身提供的函数,集成了编程中常用的函数,主要用
于提高代码的可移植性和提高代码的开发效率。库函数的数量有限,需要初学者单独学习。下面将提供学习C语言库函数的常用网站。
1.MSDN(Micrsoft Developer Network)
2.cplusplus网址:cplusplus.com
3.cppreference网址:cppreference.com
注:除了MSDN可以下载离线版本,其它两个网站的服务器不在中国大陆,加载速度慢,属于正常现象。
库函数分类
函数分类 | 示例函数 | 函数作用 |
IO函数(标准输入输出函数) | printf() scanf() | 输出内容到控制台、从控制台读取内容 |
字符操作函数 | toupper() | 将小写字母转换为大写字母 |
字符串操作函数 | strcmp() | 用于比较两个字符串的大小 |
时间\日期函数 | time(0) | 用于获取当前时间的时间戳 |
数学函数 | sqrt() | 用于开平方 |
内存操作函数 | memset() | 修改内存的值 |
其他函数 | system() | 用于对系统进行相关操作 |
库函数使用示例
strcpy()函数
strcpy()函数是字符串函数,通过数组地址将一个字符串拷贝至目标字符串数组中。返回值是目标字符串。头文件是string.h 拷贝内容包括原字符串的结尾标志符\0
语法结构:strcpy(目标字符串数组地址,原字符串数组地址);
示例代码:
//strcpy()函数
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = { 0 };
char arr2[] = "hello world !!!";
strcpy(arr1, arr2);
printf("%s", arr1);
return 0;
}
运行结果:
memset()函数
memset()函数通过地址,将目标内存起始的前n个字节的内容修改为指定的内容。返回值是目标内存的值。头文件是memory.h
语法结构:memset(目标地址,修改的字符,无符号整型数字)
示例代码:
//memset()函数
#include <stdio.h>
#include <memory.h>
int main()
{
char room[] = "hello world !!!";
memset(room, '*', 5);
printf("%s", room);
return 0;
}
运行结果:
自定义函数
虽然库函数功能丰富、使用方便,但是不能满足一些特殊的功能需求。于是,我们可以通过自定义函数,来实现想要的目的。
自定义函数和库函数一样,都需要函数返回类型、函数名、函数参数,只不过这些需要程序员自己设计。
函数组成
ret_type fun_name(para1,para2)
{
Statement;
}
ret_type:函数返回类型
fun_name:函数名称
Statement:函数语句块
para1、para2:函数参数
用函数求两个整型数字的最大值
写一个函数用于求出两个整数的最大值
示例代码:
//写一个函数求两个整数的最大值
#include <stdio.h>
//设计函数
int get_max(int x, int y)
{
int max = 0;
if (x > y)
{
max = x;
}
else
{
max = y;
}
return max;
}
int main()
{
int a, b;
printf("please enter two numbers on the there:\n");
scanf("%d %d", &a, &b);
//函数调用
int bigger = get_max(a, b);
printf("The bigger number is %d", bigger);
return 0;
}
运行结果:
代码分析:
程序从main函数进入,创建变量a和变量b。输出提示语句后,以整型数值的形式读取用户输入的两个值,并赋予变量a和变量b。创建变量bigger并将变量a和变量b(两个实参)的值传递给函数get_max。进入get_max函数,创建两个整形类型的形参x和y,并分别得到两个实参的值。创建整形变量max并初始化值为0。进入条件判断,如果变量x的值大于变量y的值,将变量x的值赋予变量max,否则,将变量y的值赋予变量max。最后返回变量max的值。变量bigger得到get_max函数返回的值。输出提示语句,返回0,程序结束。
补充:
①:当进入get_max函数中,变量x、变量y和变量max创建,离开get_max函数,这些变量会自动销毁。
②:只有实参的类型与相应位置的形参类型一致并且实参的数量和形参的数量相等时,才可以顺利的完成传参。
写一个函数交换两个变量的值
算法:
交换两个变量的值不可以写成a = b;b= a;因为a = b就改变了a的值,使a的值与b的值相等。当b = a就等于把b的值再赋予b。所以需要一个变量(假设为temp)先保存变量a的值。再将变量b的值赋予a、变量temp的值赋予变量b。为了更好的帮助理解,举一个生活中交换酱油和啤酒的例子。
如果直接交换酱油和啤酒得到的是啤酒和酱油的混合物
当我们拿一个空杯子,把啤酒全倒进杯子里,啤酒瓶里没有酒。接着把酱油瓶里的酱油全倒进啤酒瓶,啤酒瓶里就装满了酱油。酱油瓶就空空如也。把杯子里的酒倒进酱油瓶,酱油瓶里就装满了啤酒。从而,实现交换酱油和啤酒。
下面,我们按照这个逻辑写一个函数去实现功能。
示例代码:
//变量交换
#include <stdio.h>
void change(int x, int y)
{
int temp = 0;
temp = x;
x = y;
y = temp;
}
int main()
{
int a, b;
printf("please enter two numbers on the there:\n");
scanf("%d %d", &a, &b);
printf("交换前:a = %d b = %d\n", a, b);
change(a, b);
printf("交换后:a = %d b = %d\n", a, b);
return 0;
}
运行结果:
从运行结果可以看出:变量a和变量b的值并没有发生交换。这是为什么呢?
当把变量a和变量b的值作为实参,传给change函数的两个形参(变量x、变量y)。这两个变量得到了两个实参的值。同时也在内存中分配了地址(但这两个地址不同于变量a、b的地址)。也就是说变量x、变量y与变量a、变量b是相互独立的空间,对变量x、y做的任何操作与变量a、b无直接关系。
在change函数中,变量x和变量y的值发生交换,但变量a和b的值并没有因此发生改变。
为了实现变量交换,就需要传址调用。我们可以将变量a和变量b的地址作为实参传给change函数。当然change函数的两个形参也需要以指针变量的类型接收传来的两个值。change函数的两个形参分别储存着变量a和变量b的地址。于是,在函数中,就可以通过解引用指针来间接的改变变量a和变量b的值。
示例代码:
//传址调用
#include <stdio.h>
void change(int* pa, int* pb)
{
int z = 0;
z = *pa;
*pa = *pb;
*pb = z;
}
int main()
{
int a, b;
printf("please enter two numbers on the there:\n");
scanf("%d %d", &a, &b);
printf("交换前:a = %d b = %d\n", a, b);
change(&a, &b);
printf("交换后:a = %d b = %d\n", a, b);
return 0;
}
运行结果:
代码分析:
程序运行从main函数进入,scanf从控制台中读取用户输入的两个值,并分别赋予变量a和变量b。&a和&b读取变量a和变量b的地址,并将其作为实参传给change函数。change函数定义了pa和pb两个整形的指针变量用于储存变量a和变量b的地址。change函数的返回值类型是void,表示空类型,没有返回值,也不需要返回值。在change函数中,定义整形变量z并初始化值为0。*pa解引用指针变量pa。z = *pa;通过指针变量pa中储存变量a的地址,找到变量a并将变量a的值赋予变量z。*pa = *pb;通过指针变量pb储存的地址,找到变量b,并将变量b的值赋予指针变量pa指向的变量a。*pb = z;将变量z的值赋予指针变量pb指向的变量b。从而实现改变变量a和变量b的值。
函数参数
函数参数分为实际参数和形式参数。
实际参数:真实传给函数的参数,实际参数可以是常量、变量、表达式、函数,无论是何种类型,当函数调用时,都必须要有明确的值,以便把值传给函数。
代码演示:
int main()
{
int a, b;
scanf("%d %d", &a, &b);
//实参的值是变量
int bigger = get_max(a, b);
//实参的值是常量
bigger = get_max(4, 5);
//实参的值是表达式
bigger = get_max(2 + 3, 6);
//实参的值是函数
bigger = get_max(get_max(4, 10), 8);
printf("The bigger number is %d", bigger);
return 0;
}
注意:当实参中含有表达式或函数,会优先把实参中表达式的值或函数的返回值求出,以便将实参的值传递给形参。
形式参数:函数名后面括号内的变量,形式参数只有在函数被调用的时候,才会被实例化(在内存分配空间)。当函数完成调用后,形式参数会自动销毁,因此形式参数只存在于函数中。
自定义函数组成
函数调用
函数调用方式分为传值调用和传址调用。
传值调用:函数中形参和实参分别占用不同的内存块,对形参的任何修改不影响实参的值。
传址调用:通过在函数外部创建变量的内存地址,传递给函数形参的一种函数调用方式。这种函数调用方式可以使函数内部与外部变量建立真正的联系,也就是可以在函数内部直接操作函数外部的变量。
如果不需要修改实参的值,适用于传值调用。
当需要修改实参的值,适用于传址调用。
实战演练:
写一个函数输出100-200之间的素数
总体思路:
判断素数的标准:只能被1和它自身整除的数就是素数。因为要输出100-200之间的素数,所以需要for循环产生这些数。每产生一个数,就将值作为实参传给函数。因为只需要判断,所以适用于传值调用。在函数内部,定义一个flag变量用于判断这个数是不是素数。初始值为1,默认是素数。为了实现判断是否为素数,可以对从2到小于这个数之间的所有数进行取模%,如果取模的结果是0(说明这个数能被除了1和它本身以外的其它数整除),那么将变量flag的值改为0(表示这个数不是素数),并且使用break语句,跳出for循环。如果整个for循环语句结束,变量flag的值依然是1,就说明没有一个数能被整除。返回变量flag的值。在主函数体内,设置变量result用于接收返回值。定义条件语句,如果变量result的值等于1,那么用printf()输出这个数。
示例代码:
//写一个函数输出100-200之间所有的素数
#include <stdio.h>
int prime(int x)
{
int flag = 1;
int i = 1;
for (i = 2; i < x; i++)
{
if (x % i == 0)
{
flag = 0;
}
}
return flag;
}
int main()
{
int result = 0;
int number = 0;
for (number = 100; number <= 200; number++)
{
result = prime(number);
if (result == 1)
{
printf("%d ",number);
}
}
return 0;
}
运行结果:
每调用一个函数,变量number的值加一
总体思路:当调用函数,变量number的值会修改,所以适用于传址调用。我们可以把变量number的地址作为实参传给函数,并在自定义函数体内部通过解引用指针变量修改变量number的值。因为不需要返回值,所以可以使用void类型(空类型)。
代码示例:
//写一个函数,每当调用函数,就把变量number的值加一
#include <stdio.h>
void add(int* pa)
{
*pa = *pa + 1;
}
int main()
{
int number = 0;
printf("please enter a number on the there:\n");
scanf("%d", &number);
printf("没调用变量number的值是%d\n", number);
add(&number);
printf("第一次调用变量number的值是%d\n", number);
add(&number);
printf("第二次调用变量number的值是%d\n", number);
return 0;
}
运行结果:
标签:函数,int,max,number,C语言,实参,变量 From: https://blog.csdn.net/2401_84689376/article/details/141202081