C语言函数详解

函数

  • c语言 面向过程(函数)
    • 定义
    • 返回值
    • 函数调用:
  • 函数声明:
    • 局部变量:
    • 全局变量:
    • static局部变量(静态变量)
    • const变量
    • static全局变量
  • 递归函数: 自己(间接或直接)调用自己.
  • 函数指针:
  • main 函数
    • 变长参数

c语言 面向过程(函数)

函数:把一部分特殊功能的代码封装成的过程模块.
作用:方便c语言的调用,并且函数是实现过程的一个部分
c语言的基本单位—函数.

定义

格式: 返回值类型 函数(参数列表,参数列表,…){ 函数体 }
注意:参数列表不是必须的,可以不传入参数.
函数中不可以再定义函数,但是可以调用函数.
函数参数:(类似于-定义变量)
形参:在定义或声明函数时的参数叫形参,形式参数调用前不占用内存空间,在实际调用时才开辟变量的内存空间.形参保存在栈.所以可以理解成局部变量.
实参:也叫做实际参数,占用内存空间,在函数调用的时候使用的参数.(所有形参都是实参的拷贝)

返回值

函数的返回值是返回给调用者,告诉调用者结果。
返回值值和返回值类型有关系,和return后面的类型无关,并且只可以返回一个返回值,不可以返回多个。
如果没有返回值,必须写void返回值类型,函数遇到return就结束。

函数调用:

格式: 返回值变量 = 函数名(实际参数);
函数分类:

  1. 库函数 自定义函数
  2. 带参函数 无参函数
  3. 数学函数 abs-求绝对值 pow-x的y次方 atof
    目录函数 进程函数 诊断函数 操作函数

函数声明:

函数在使用前,需要告诉编译器,函数的形参,和返回值,
函数的声明就是直接写函数头,加分号.
例如:int add(int ,int );
上例中,函数参数只有形参类型,没有形参名,这种方式叫哑元.

#include 
double er(double , int arr[10]);  \\返回值为double的er函数声明
//函数的声明以及定义
void fun(int m)                          
{
	printf("拿着%d块钱,酱油花了4块钱,剩下%d块钱\n", m, m - 4);
	printf("拿着钱往回走,需要给我结果了....\n");
	m = 200;
	m = max(10, 20, 30);
}

/*
	写一个函数,用来得到三个数中的最大值
*/
int max(int x, int y, int z)
{
	if (x > y)
	{
		if (x > z)
		{
			return x;
		}
		else
		{
			return z;
		}
	}
	else
	{
		if (y > z)
		{
			return y;
		}
		else
		{
			return z;
		}
	}
}

/*
	函数: int add(int x,int y);
	作用: 计算两个函数的和
	参数: x 第一个加数
	       y 第二个加数
    返回值: 返回两个数的和
*/
int add(int x, int y)
{
	return x + y;
}

int main()
{  
	int x = 10;
	int y = add(10,20);                               //add的函数调用
	printf("x + y= %d\n", add(100, 20));
	y = max(10, 20, 30);                           //函数求最大值的调用
	//er(10, 20);
	printf("y = %d\n", y);
	return 0;
}
//函数er的定义
double er(double x, int arr)
{
	return 0.0;
}

C语言函数详解_第1张图片
插入一些变量的知识

局部变量:

作用域:在局部,在大括号范围之内,超过范围其他地方不可识别
生命周期:定义时开始到函数大括号结束

全局变量:

作用域:在任何地方都可以识别和使用
生命周期:程序运行到程序结束

static局部变量(静态变量)

静态变量—长生不老药.
样式: static 类型 变量名 = 初始化值;
生命周期:不会因为函数结束而释放内存空间.从第一次调用函数时的变量定义,直到程序结束为止.
作用域:作用域还是在局部,不会发生改变
注意:只申请一次内存空间,只会初始化一次.

const变量

const变量是常变量.不可以修改的变量.两者都需要初始化
const修饰的局部变量生命周期和作用域不发生改变.
很多时候会定义const形参,说明形参是不可以修改的.

static全局变量

生命周期:不变,还是从程序开始到程序结束
作用域:限定在当前文件中.只可以在当前文件中使用.c_day15.c的文件中使用.

  • 小杂碎知识点:
    在函数中 通过指针参数修改实参存储内存空间的值
    在函数中 通过二级指针参数修改实参一级指针的指向
    在函数中,数组当做形参是,实际上拷贝的是数组首地址,不会拷贝整个数组内容.所以传入数组时一般都会传入第二个参数–数组大小
#include 
#include 
static int x = 123;              //static全局变量
void fun(const int xx)        //形参就是局部变量
{
	static int danny = 12;//只申请一次内存空间,只会初始化一次
	//xx = 12;
	printf("danny = %d\n", danny++);
	return ;
}

//拷贝了x和y的地址,没有拷贝x和y的值.通过地址修改了内存空间
void pfun(int* xx,int* xy)
{
	int temp = *xx;
	*xx = *xy;
	*xy = temp;
	printf("内部 x = %d,y = %d\n",*xx,*xy);
}

void pMalloc(int** p)
{
	*p = (int*)malloc(sizeof(int));
}

//指针函数.一个普通函数,返回指针.
int* pReturn(int* p)
{
	p = (int*)malloc(sizeof(int));
	return p;
}

//数组当做函数参数时,
int Pfun(int arr[],int size)
{
	arr[1] = 123;
	printf("sizeof(arr) =%d\n", sizeof(arr));
	arr[size - 1] = 10;
	return 0;
}


int main()
{
	int danny = 12;
	fun(10);
	fun(10);
	printf("x = %d", danny);

	int x = 12;
	int y = 123;
	int* px = &x;
	int* py = &y;
	pfun(px, py);
	printf("外面x = %d, y = %d", x, y);

	int* pNull = NULL;
	pMalloc(&pNull);
	*pNull = 1234; 

	int *p = NULL;
	int *pp = pReturn(p);
	*pp = 123;

	int arr[10] = { 1, 2, 3, 4, 54, 5, 6 };
	Pfun(arr,sizeof(arr)/sizeof(arr[0]));

	return 0;
}

递归函数: 自己(间接或直接)调用自己.

弊端: 内存消耗比较多.每次函数调用都会压栈,消耗内存空间
好处:可读性好,代码简洁.
递归满足两个条件:

  1. 必须有返回(结束条件)
  2. 一定要有通配公式. 1 1 2 3 5
#include 
#include 
//递归函数实例
int Fun(int x){ //参数压栈
	printf("第%d次调用fun函数\n", x);
	if (x < 5)
	{
		Fun(x+1);
	}
	printf("函数%d次返回\n", x);
	return 0;
}
//斐波那契数列的第n项1 1 2 3 5
int fplq(int x)
{
	if (x == 1 || x == 2)
	{
		return 1;
	}
	else
	{
		return fplq(x - 1) + fplq(x - 2);
	}

}

int main()
{
	Fun(1);                           //调用递归示例函数,查看递归的执行顺序
	printf("x = %d",fplq(5));
	return 0;
}

函数指针:

函数指针:一个指针指向于函数
格式:返回值类型 (*指针名)(参数列表);
typedef 返回值类型 (*指针名)(参数列表); typedef 取别名 定义的是指针类型

注:一个函数名确定了函数的地址

main 函数

main 函数结束后的调用:atexit()
先注册函数,在main函数结束后,会直接调用注册后的函数
注册:告诉才做系统有这样的一个函数,需要调用
当main函数结束后,操作系统自动调用注册的函数,叫回调
void (__clrcall* _Function)(void x) 这是一个函数指针 没有返回值的类型

main函数的参数 注: 有三个参数 第三个参数为环境变量值

    格式:int main(int argv,char *argc[])
			 参数 :
			 int argv  说明函数参数的个数  
			 char* argc[]参数列表,参数的实际内容,字符串数组    char*   就是字符串

main函数参数使用的三种方式:

  1. 直接拖拽文件到exe上,会把文件路径当作参数
  2. 在项目–>属性–>配置属性–>命令参数,直接设置,参数用空格隔开
  3. 在cmd中(控制台命令窗口),拖拽exe到cmd,然后空格添加参数

变长参数

函数中的变长参数:函数的参数不确定,数量是可变的。
格式:返回值 函数名(第一个参数,…)
C语言中通过3个语句来处理变长参数
va_start(ap,v) //宏–>理解成函数 在头文件stdarg中
作用:做初始化处理,告诉编译器有变长参数来了
第一个参数ap:可变长参数列表的地址,相当于第一个参数,实际上是va_list变量
第二个参数v:确定的参数,一般为函数的第一个参数,一般就是count

va_arg(ap,t)
作用:得到变长参数的内容
第一个参数ap:实际就是va_list变量
第二个参数t:传入参数的类型

va_end(ap)
作用:结束变长参数,告诉编译器变长参数的处理结束了
第一个参数ap:实际就是va_list变量
va_list 是char*型

注:

  1. 必须有一个参数,不可以所有的参数都…代替,可以有两个或多个确定的参数,参数类型不确定。
  2. 一般第一个参数是可变长参数的个数。
#include 
#include 
#include 

typedef int (*pFun)(int x, int y, int z);  //函数指针   typedef  取别名 实际上别名为pFun   为函数指针类型

typedef int limitfly;

int f(int a, int b, int c)
{
	return a + b + c;
}
int fun1()
{
	printf("danny 老师在开车,此车四平八稳。");
}
int fun2()
{
	printf("danny 老师即将开车,大家尽快上车。");
}
//计算所有数据的和   count 形参的个数
int add(int count, ...)
{
	va_list vl;     //用来存储所有的参数
	int sum;
	va_strat(vl,count);   //初始化
	for (int i = 0; i < count; i++)
	{
		int temp = va_arg(vl, int);
		sum += temp;
	}
	va_end(vl);
	return sum;
}

int main(int argv,char *argc[])
{
	int(*arr)[10];                           //数组指针
	pFun px = f;                           //函数指针的使用   这里的px是函数指针  指向的是函数f  类型不一样的无法指向
	int m = px(1, 2, 3);

	printf("argv=%d\n", argv);
	printf("argc[0]=%s\n", argc[0]);

	int num = add(4,20,50,60,80);
	printf("num=%d\n", num);

	atexit(fun1);// 此处的fun1和fun2就是注册函数
	atexit(fun2);  //函数结束后   会以压栈的方式(即先进后出的原则)先后打印fun2和fun1

	return 0;
}

你可能感兴趣的:(C语言学习)