在C语言中,函数是一组执行一个任务的诸多语句的组合。就像数学中的函数一样,输入自变量(参数),就可以得到因变量(输出)。
Table of Contents
函数长啥样?
声明与定义函数
函数声明告诉编译器函数的名称、返回类型和参数。函数定义提供了函数的实际主体。
声明函数:
- 函数声明格式:返回类型 函数名(参数列表); 例如:int max(int a, int b);
- 参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明:int max(int, int);
- 函数声明一般在源文件的最顶部(头文件和宏定义之后、main函数之前);
- 当出现在一个源文件中定义函数而需要在另一个函数中调用函数时,函数申明是必需的。例如,在头文件中声明函数,在具体文件中定义函数。
定义函数:
- 函数由一个函数头和一个函数主体组成。其中,函数头的格式为“返回类型 函数名(参数列表)”,函数主体由“{}”扩起来的部分。
- 返回类型:一个函数可以返回一个值(需要指明类型,如int,double),也可以不返回(即为void)。
- 函数名称:这是函数的实际名称。函数名和参数列表一起构成了函数签名。函数的命名需参照编程范式中的一些原则。
- 参数:参数就像是占位符。参数列表包括函数参数的类型、顺序、数量。也可以不含有参数。
- 函数主体:函数主体包含一组定义函数执行任务的语句。
申明、定义函数:
申明、定义函数
#include <stdio.h>
int max(int, int); //函数声明,只需要注明类型
int min(int a, int b); //函数声明,详细注明类型和变量名
//函数定义
int min(int a, int b)
{
//实现
return b;
}
int main()
{
//调用
max(1, 2);
min(1, 2);
return 0;
}
//已经申明过了的函数,可以在本文件其他函数体以外的任何地方定义。
int max(int a, int b)
{
//实现
return a;
}
函数调用
理解函数调用:
- C语言中的一个函数,就如同数学中的一个函数一样(映射关系:一个输入对应一个输出);
- 把一个大功能的实现分割成许多个小功能组件(即一个个地函数),再一一调用这些组件(函数);
- 以做饭吃饭为例,以往饿了的话,需要自己买菜、煮饭、炒菜,然后才能吃到饭;而今你可以直接调用买菜做法的功能,直接就能够吃到饭了。
函数调用示意图:
函数调用中的参数问题:
- 传值调用:又叫副本机制,把调用函数时的传入的值称为实参,把执行该函数时传入的参数值称为形参。副本机制的含义是:复制实参的值给形参,形参无论怎样变化,对实参都毫无影响。
- 引用调用:通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。
函数调用中的内存情况:
main函数中的各种变量、常量存放情况:
main函数中malloc、realloc的内存:
子函数1在栈区开辟的内存,子函数1和2均可以使用:
子函数2在全局区开辟的内存,子函数1和main函数均可以使用:
main函数的参数
一般来说,C语言的main函数的写法应该是:
#include <stdio.h>
int main(int args, char* argv[])
{
//....
return 0;
}
其中,args参数表示传递的参数个数,argv参数表示一个指针数组,存储每个args个参数的具体内容。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main(int args, char *argv[]) //args是给main函数传参的个数,argv是指具体的参数是啥
{
printf("args=%d\n", args);
for (int i = 0; i < args; i++)
{
printf("%s\n", argv[i]);
}
system("pause");
return 0;
}
/*直接运行的结果:
args=1 //参数的个数为1个
F:\ProgramDesign\VisualStudio2017\KyleC\Debug\KyleC.exe //输出程序的绝对路径
*/
可变参数函数
什么是可变参函数?
- 一般情况下,一个参数,只会接受参数列表个数的参数个数。例如,函数“int max(int a, int b) {...}”,只能接受两个int型的参数。
- 那么想要实现在上述例子中,随机输入多个参数,应该怎么办呢?
stdarg.h是C语言中C标准函数库的头文件,stdarg是由standard(标准) arguments(参数)简化而来,主要目的为让函数能够接收可变参数。
实例
#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h> //有很多宏,我们可以处理可变参数的函数
#include<string.h>
double add(int num, ...) //...代表可变的参数,至少定义一个num(参数有多少个呢?)
{
double last = 0.0;
va_list argp; //创建一个char类型的指针,存储地址;转到定义是:typedef char * va_list;
va_start(argp, num); //从这里开始读取参数,读取num个参数,并把读取到的东西的地址放在argp;
int i;
for (i = 0; i<num; i++)
{
double temp = va_arg(argp, double); //读取参数,并以double类型来读取;
printf("\n%f", temp); //输出每次读取的东西;
last += temp;
}
va_end(argp); //结束读取;
return last;
}
void go(int num, ...)
{
va_list argp; //定义指针,用于存储地址;
va_start(argp, num); //num代表是有num个参数;
int i;
for (i = 0; i<num; i++)
{
char str[50]; //保存读取的字符串;
strcpy(str, va_arg(argp, char*));//按照字符串的参数读取一个参数,并且以char* 的类型去读,复制到str;
printf("%s", str);
system(str);
}
va_end(argp); //结束调用;
}
int main()
{
/* int x=12;
double result=0;
result=add(x,1.2,2.3,3.4,4.5,5.6,6.7,7.8,8.9,9.10,10.11,1000.0,100000.0); //刚开始的第一个数:x表示的数要与后面的参数的个数相同;
printf("\n%f",result);*/
putchar('\n');
go(6, "start notepad", "tasklist", "start mspaint", "start write", "title kyle", "color a");
//同时打开所有的东东;
}