C语言函数基础篇章

目录

  一、 C语言中的函数

1.1初步认识函数

1.2函数的存在意义

1.3函数的分类

1.4如何定义一个函数

1.5函数的调用

1.6函数的声明

1.7函数调用的过程

1.8数组作为函数的参数

1.9函数例子

二、变量的存储方式

2.1局部变量和全局变量

2.2变量的存储方式

2.3static修饰的局部变量

2.4extern修饰的全局变量

2.4.1同一源文件下扩展全局变量的作用范围

2.4.2不同源文件下扩展全局变量的作用范围


  一、 C语言中的函数

1.1初步认识函数

    在学习C语言函数这部分的内容之前,我们认知中的函数是数学上的一种自变量和因变量之间的对应法则,并且我们接触的函数大多是单值函数(一个自变量对应一个因变量)。实际上C语言的函数功能上和我们数学上认知的单值函数也很类似,C语言函数通过输入一个或者多个值,然后得到一个结果,这个结果可以是我们前面学习的整型数字,浮点数字,也可以是一种其他的效应,比如后面我会举的一个例子:通过冒泡法来实现数字的排序。总之C语言中的函数就是实现一种特定功能的东西。一个C程序通常由多个模块组成,可以理解为多个函数组成,我们通常把一个或者多个函数放到同一个项目下的不同源文件当中,由多个源文件构成一个C程序。

1.2函数的存在意义

     学函数之前我们的程序都是一个.c文件实现一个功能,当我们设计一个大程序的时候我们如果仍然用一个源文件来解决时,写出来的代码就会变得非常长,阅读和维护程序变得十分困难,如果程序当中需要多次实现某一功能时则需要多次编写这部分的代码,而且调试过程中寻找代码错误时也会变得更加麻烦,这给我们设计和调试程序带来很大的不便。同时一个大程序通常是需要多人合作完成的,想要高效地完成庞大的程序,代码的模块化是必不可少的,代码的模块化使代码的移植性变高,使得代码变得更加简洁明了,提高代码的可读性。而代码的模块化就离不开函数,函数的存在让代码实现模块化,使设计程序变得更加灵活。

1.3函数的分类

   根据函数的来源,我们可以把函数分成库函数和用户自己写的函数,例如我们之前用的printf函数,scanf函数,这些就是库函数。根据函数的功能可以分为有值返回和没有值返回。

1.4如何定义一个函数

int sum(int a, int b)
{
	int c = a + b;
	return c;
}


void print_arry(int a[],int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}

在这里我们定义了两个函数,下面我通过这两个例子来详细地介绍函数是如何定义的

一个函数的定义:函数的返回类型 函数的名字(类型 形式参数1名字,类型 形式参数2名字)

{

  函数体内部(实现特定功能的代码)

 }

一般来说函数的名字可以根据函数的功能来取。

   上图中,函数一的返回类型是整型int;它的名字是sum;a和b是形式参数名字,都是int类型;函数体部分的代码实现的功能是将a和b的和返回到main函数当中,这是一个有返回类型的函数,需要有return语句,其格式为:return 变量; return语句中的变量的类型要和函数定义时的类型相同,这个表达式的作用是把变量的值返回到main函数。

   函数二不需要返回值,这个函数的返回类型那部分用void来代替;它的名字是print_arry;a和n是形式参数的名字,形参a的类型是指向int类型的指针,形参b的类型是整型int;函数体部分代码的功能是打印一个数组的所有值。

  如果函数是不需要形式参数的,那圆括号里面可以写void或者省略不写,可以把a,n,b这三个形式参数理解为只能在函数体里面使用的局部变量。

1.5函数的调用

  函数调用的格式为 :函数名字(实参1,实参2);或者函数名字();

int sum(int a, int b)
{
	int c = a + b;
	return c;
}
#include <stdio.h>
int main()
{
	printf("%d", sum(2, 3));
	return 0;
}
//5
//D:\code\code  2023\打印菱形\x64\Debug\打印菱形.exe(进程 18260)已退出,代码为 0。
//按任意键关闭此窗口. . .

在这个例子中,我们先是定义了一个函数,然后在main函数当中调用这个函数,如果这个函数是有返回类型的,那调用这个函数的结果就是函数的返回值,我们可以看到最后调用函数的结果是5符合我们的预期,函数在使用之前要定义或者声明。函数调用是把这两个实参的值传递到相对应的形式参数当中,函数调用时的实参类型要和函数定义的形参的类型相同;同时实参一定是一个数值,它可以是一个整型地址,一个常量,一个表达式,一个有数值的变量。

1.6函数的声明

如果函数的定义没有放到main函数之前,我们编译的时候就会报错,这个时候我们只需要在使用之前对函数进行声明就行了;

函数声明格式:函数返回类型 函数名称(参数类型 参数1名字,参数类型 参数2名字);

#include <stdio.h>
int main()
{
	int sum(int a, int b);
	printf("%d", sum(2, 3));
	return 0;
}
int sum(int a, int b)
{
	int c = a + b;
	return c;
}

实际上函数声明的时候可以不说明形式参数的名称。

1.7函数调用的过程

   函数定义的形参,或者函数体里面定义的变量称为局部变量,它们的内存单元分配是动态的,只有在函数被调用的时候,这些局部变量才会被分配内存,形参被分配内存后接收实参的值,在函数体内进行运算,注意,形式参数只是获得实参的值,如果传递的不是地址,那么它在函数体内的运算是不会影响实参的值的大小的。函数结束后,这些局部变量所占用的内存就会被释放,因此它们是动态分配内存的,与之相对的是静态分配内存,这种内存分配是从程序开始直到程序结束,全局变量的内存分配就是这种。

1.8数组作为函数的参数

  在传递参数的时候(只包括整型数值的传递),我们可以把整型数组元素的值,变量的值,常量等整型数值传递给函数的形参,这种情况下的形参的类型是int,而当我们把数组名当实参传给函数的时候我们需要注意形参的类型,我们知道数组名是数组首元素的地址,这相当于把地址传递过去了,这个时候函数的形参类型应该是指针,根据数组的类型确定该指针的类型。我们先来看一维数组名字作为函数实参的情况

void bubble_sort(int a[], int b)
{
	int i = 0;
	for (b = b; b > 1; b--)
	{
		for (i = 0; i + 1 < b; i++)
		{
			if (a[i] < a[i + 1])
			{
				swap(a + i, a + i + 1);
			}
		}
	}
}
int main()
{
	int a[10];
	int b = sizeof(a) / sizeof(a[0]);
	bubble_sort(a, b);
	return 0;
}

   这个bubble_sort函数是冒泡排序函数,我们可以看到第一个实参是一维整型数组a的名字,函数形参的类型是指向int的指针,可以写成int *a或者int a[],这两种写法是等效的。

我们再来看看二维数组名称作为实参的时候

#include <stdio.h>
double subject_student_score_average(int student_score[][5], int a)
{
	int i = 0, j = a - 1, sum = 0;
	double subject_score_average = 0;
	for (i = 0; i < 10; i++)
	{
		sum += student_score[i][j];
	}

	subject_score_average = sum / 10.0;
	return subject_score_average;
}
int main()
{
	int student_score[10][5];
	int i = 0;
	for (i = 0; i < 50; i++)
	{
		scanf("%d", &student_score[0][i]);
	}
	printf("第一科的平均值是%.1f\n", subject_student_score_average(student_score, 1));
	printf("第二科的平均值是%.1f\n", subject_student_score_average(student_score, 2));
	printf("第三科的平均值是%.1f\n", subject_student_score_average(student_score, 3));
	printf("第四科的平均值是%.1f\n", subject_student_score_average(student_score, 4));
	printf("第五科的平均值是%.1f\n", subject_student_score_average(student_score, 5));
	return 0;
}

   这个例子当中调用subject_student_score_average的时候第一个实参是二维整型数组student_score的名称,它是指向二维整型数组首元素的地址,函数对应的第一个形式参数应该是指向int [5]类型的指针,可以写成int student_score[][5]或者int (*student_score)[5],这两种写法是等效的,加括号是因为下标运算符[]的优先级高于指针运算符*

1.9函数例子

#include <stdio.h>
//交换函数 
void swap(int* a, int* b)
{
	int tem = *a;
	*a = *b;
	*b = tem;
}
//冒泡法函数 
void bubble_sort(int a[], int b)
{
	int i = 0;
	for (b = b; b > 1; b--)
	{
		for (i = 0; i + 1 < b; i++)
		{
			if (a[i] < a[i + 1])
			{
				swap(a + i, a + i + 1);
			}
		}
	}
}
//数组输入函数 
void scanf_(int a[], int b)
{
	int i = 0;
	for (i = 0; i < b; i++)
	{
		scanf("%d", &a[i]);
	}
}
//打数组函数 
void print_arry(int a[], int b)
{
	int i = 0;
	for (i = 0; i < b; i++)
	{
		printf("%d ", a[i]);
	}
}
int main()
{
	int a[10];
	int i = 0;
	int b = sizeof(a) / sizeof(a[0]);
	scanf_(a, b);
	bubble_sort(a, b);
	print_arry(a, b);
	return 0;
}


//98 96 95 93 78 71 78 65 45 34
//98 96 95 93 78 78 71 65 45 34

我们可以看到冒泡排序函数是没有返回数据到主函数里面的,但是它实现了数组排序的功能。

二、变量的存储方式

2.1局部变量和全局变量

   局部变量是指在函数内部定义的变量,局部变量只能在本函数的范围内使用;全局变量是在函数外定义的变量,全局变量的使用范围时从开始定义到本源文件结束,一般来说我们在定义全局变量的时候习惯上将其名字的第一个字母用大写来表示。

2.2变量的存储方式

  变量的存储方式分为静态存储和动态存储,静态存储是程序从运行到结束这个过程,变量分配固定的内存单元,整个过程都是存在的,全局变量是属于静态存储。动态存储是在需要的时候才临时给变量分配内存单元,比如函数的调用,一旦调用完函数就会释放该内存单元,该内存单元不再属于调用函数时的局部变量,局部变量的存储方式是动态存储。因此程序的数据有一部分是存放到静态存储区,有一部分是放到动态存储区

2.3static修饰的局部变量

  在函数中定义变量的时候,如果我们不特别声明,我们定义的变量都是属于局部变量,是动态分配内存空间,如果我们在函数体中定义变量的时候加上static,这个时候声明的变量是静态局部变量,是静态分配内存的,函数调用后其内存单元不会被释放,该变量保存了上次调用函数时的值。如果定义变量的时候赋予初值,则在下一次调用函数时不会再赋值,只赋一次值,若定义时没有赋初值,系统默认是0或\0

2.4extern修饰的全局变量

2.4.1同一源文件下扩展全局变量的作用范围

  如果全局变量是在main函数后面定义的,此时全局变量在main函数内是无效的,如果我们想在main函数当中使用它,我们只需要在使用前加上:extern 类型 变量名字;就可以在main函数当中使用

2.4.2不同源文件下扩展全局变量的作用范围

  一个程序通常包括很多模块,由多个源文件组成,假设我们在A.c文件中定义了全局变量C,同时我们又想在B.c中使用该全局变量,如果我们在B.c文件当中再次对变量C进行定义,在程序链接的时候会出现重复定义,正确的做法是在B.c使用变量C前加上:extern C;

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学不会也要努力学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值