1. 函数的概念

是一个完成某项任务的一小段代码,包括库函数和自定义函数

1.1库函数

库函数相关头文件点击查看 库函数需要包含头文件

1.2自定义函数

函数的语法形式

函数(函数的分类及声明和定义,练习题,作用域和生命周期的介绍,static和extern的详细介绍)_生命周期

形参只有在函数在被调用的过程中为了存放实参传递过来的值,才向内存申请空间,这个过程叫形参的实例化

函数(函数的分类及声明和定义,练习题,作用域和生命周期的介绍,static和extern的详细介绍)_源文件_02

VS中调试时F10,当进入形参F11,进到函数里里面 break时用于循环中的 形参是实参的一份临时拷贝

当return后面没有时,适合函数返回类是void的情况。 当return返回的值和函数返回类型不一致,系统自动将返回的值隐式转换成函数的返回类型。

链式访问

将一个函数的返回值作为另外一个函数的参数

练习一:将数组的值赋值为-1

法一:
void set_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		arr[i] = -1;
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[] = {1,2,3,4,5,6,12,14};
	int sz = sizeof(arr) / sizeof(arr[0]);
	set_arr(arr,sz);
	
	return 0;
}
法二:
//将数组中的数值赋值为-1
void set_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		arr[i] = -1;
	}
}
void print_arr(int arr[],int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ",arr[i]);

	}
	printf("\n");
}
int main()
{
	int arr[] = { 1,2,6,4,9,694,3,45 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	set_arr(arr, sz);
	print_arr(arr,sz);
	return 0;
}

形参如果是二维数组,行可以省略,列不能省略 数组传参时,形参不会创建新的数组,传过去的是原数组的地址,函数就是根据地址把里面的内容改了,这样内容就保留在这个地址里了,出了函数体,函数销毁了但是数组里的值变成改后的内容了

练习二:计算某年某月有多少天

//计算某年某月有多少天
int  run_ofyear(int y)
{
	if ((y % 400 == 0) ||((y % 4 == 0) && (y % 100 != 0)))
	{
		return 1;
	}
	else
		return 0;
}
void days_ofyear(int y, int  m)
{
	int days[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
	int r=run_ofyear(y);
	if (r==1&&m==2)
	{
		days[m] += 1;
	}
	printf("%d ", days[m]);
}

int main()
{
	int y = 0;
	int m = 0;
	printf("输入年份和月份:>");
	scanf("%d%*c%d", &y, &m);
	days_ofyear(y, m);
	return 0;
}

printf函数返回的是打印在屏幕上的字符的个数

函数(函数的分类及声明和定义,练习题,作用域和生命周期的介绍,static和extern的详细介绍)_全局变量_03

2. 函数的声明和定义

先声明,后使用

2.1 单个文件

练习一:判断一年是否是闰年

int  guss_y(int y)
{
	if (((y % 100 != 0) && (y % 4 == 0)) || (y % 400 == 0))
	{
		return 1;

	}
	else
		return 0;
}

int main()
{
	int y = 0;
	scanf("%d", &y);
	int r=guss_y(y);
	if (r == 1)
	{
		printf("是闰年\n");
	}
	else
		printf("非闰年\n");
	return 0;
}

2.2 多个文件

函数的声明类型的声明放在头文件(.h)中,函数的实现是放在源文件(.c)文件中。

3. 作用域

一个变量在哪里可以使用,那里就是他的作用域

3.1局部变量

局部变量的作用域是在变量所在的局部范围,大括号内部定义的变量

3.2全局变量

全局变量的作用域是整个工程,大括号外部定义的变量,全局变量具有外部链接属性,在这个源文件里定义的变量只要在其他源文件声明,就可以直接使用。全局变量具有外部链接属性,在这个源文件里定义的变量只要在其他源文件声明,就可以直接使用

4. 生命周期

生命周期指变量的创建到变量的销毁之间的时间段 局部变量的生命周期是进作用域开始,出作用域结束 全局变量的生命周期是整个程序的生命周期

5. static和extern

5.1 static

static(静态的)

  • 修饰局部变量,改变了变量的生命周期。函数默认具有外部连接属性,static修饰函数时,函数的外部链接属性变成内部。
  • 修饰全局变量时,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用,本质原因是全局变量默认时具有外部连接属性,全局变量默认是具有外部链接属性的,在外部的⽂件中想使用,只要适当的声明就可以使用;但是全局变量被static修饰之后,外部链接属性就变成了内部链接属性,只能在自己所在的源文件内部使⽤了,其他源文件,即使声明了,也是无法正常使用的。
  • static修饰全局变量后外部链接属性就变成内部链接属性(只能在自己所在的.c文件内部使用,其他源文件(.c)文件无法使用。
  • static修饰局部变量改变了变量的生命周期,生命周期的改变的本质是改变了变量的存储类型,本来一个局部变量是存储在内存的栈区的,但是被static修饰后存储到了静态区,存储在静态区的变量和全局变量是一样的,生命周期就和程序的生命周期一样了,只有程序结束,变量才销毁,内存才回收,但是作用域不变。

5.2extern

extern是用来修饰外部声明的 如果一个全局的符号在A文件中定义的,在B文件中想使用,就可以使用extern进行声明,然后使用

代码一:

void test()
{
	static int i = 0;
	i++;
	printf("%d ", i);
}
int main()
{
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		test();
	}
}

结果:

函数(函数的分类及声明和定义,练习题,作用域和生命周期的介绍,static和extern的详细介绍)_源文件_04

test函数中的局部变量i是每次进入test函数先创建变量(声明周期开始)并赋值为0,然后++,再打印,出函数的时候变量的生命周期将要结束(释放内存)

代码二:

void test()
{
	int i = 0;
	i++;
	printf("%d ", i);
}
int main()
{
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		test();
	}
	return 0;
}

结果:

函数(函数的分类及声明和定义,练习题,作用域和生命周期的介绍,static和extern的详细介绍)_生命周期_05

i的值有累加的效果,i直接上次累积的数值继续计算