2026/08/02 C语言的函数

一、C语言的函数

C语言程序的基本单位是函数,C语言是面向过程的一门编程语言,采用“自顶向下”的设计思想,采用的方案是把一个大问题拆解为很多个小问题,每个小问题单独进行解决,每个小问题可能需要多条语句才能解决,为了提高效率,所以就把可以解决问题的多条语句构成一个块语句,C语言中把这种块语句就称为函数。

C语言标准在发布的同时也随之发布了标准C库,标准C库中提供了已经封装好的函数接口,目的也是方便用户提高开发效率,不过预先封装好的函数属于库函数。库函数根据发布者的不同,又分为标准库和第三方库,比如标准C库。

函数的本质就是一段可以重复使用的代码块,用户不想每次都复制这段代码,就可以把这段可以重复使用的代码封装为一个函数接口。

1.函数定义
在定义一个函数时应该声明一个标识符(用于表示函数名称)
定义函数时应该声明函数的类型(即返回值的类型),作为函数定义的一部分
函数的返回类型可以是一个对象类型或者是void,不允许是数组类型
如果标识符包含一个参数类型列表,则列表中每个参数都应该有一个标识符。也可以没有参数列表,参数列表中为空void即可。

//函数有参数列表,则应该在函数名称的()中写清楚每个参数的类型,以及每个参数的名称
函数类型 函数名称(参数1类型 参数1名称,参数2类型 参数2名称.........)
{

}

//函数的参数是可以可选的,如果没有参数,则需要在函数名称的()中填写void即可
函数类型 函数名称(void)
{

}

void在C语言标准中是一个关键字,含义具有空的意思,所以如果在参数列表中出现,则表示函数没有参数,同样,如果void是函数类型,则表示函数没有返回值。

函数的类型其实指的是函数的返回值的类型,C语言标准中规定函数类型可以是void或者是对象类型(基本数据类型 int char long float... + 复杂数据类型 struct union.... +指针)
但是函数的返回值类型不允许是数组!!!!

注意:如果函数有返回值类型,则函数内部的需要返回的数据的类型必须要和函数的返回值类型一致,则需要在函数内部调用return语句实现。

C语言中函数都是独立的个体,不允许在一个函数内部定义新的函数,但是允许在一个函数内部调用其他的函数!设计函数应该做到低耦合,高内聚!

如果函数的类型是一个指针类型,则表示该函数可以返回一个地址,就把这种函数称为指针函数。

2.函数调用
遵循一个“先定义,后使用”原则,由于C语言中程序都是以函数为单位,并且程序的入口是主函数main(),所以应该把用户自定义的函数定义在main()函数之前,然后在main()函数中进行调用。但是,有时用户可以在程序设计时是先在main()中调用了某个自定义函数,然后在main()函数后面定义了该函数,此时编译会报错,会提示:函数未定义,为了避免此类问题,C语言中也是支持“先声明,后定义”。

#include <stdio.h>

int add(int a,int b);

int main(int argc, char const *argv[])
{
	printf("add(1,2)=%d\n",add(1,2));

   	return 0;
}

int add(int a,int b)
{
	return a+b;
}

运行结果:

image

3.函数参数
把要处理的数据传递给函数需要在设计函数的时候说清楚函数需要传递的参数的类型以及参数名称,都是在定义函数的时候通过函数的参数列表传递。

函数的参数列表是在后缀运算符()里面进行填写,()中的参数只是一个函数的助记符, 只是为了描述需要传递给函数的参数,所以函数的参数一般称为形式参数,简称为形参。

而定义函数的时候函数参数列表中的形参是不占内存的,只是为了提醒用户参数的数量和类型!

用户在调用函数接口时,需要按照函数的参数列表来向函数提供对应的数据,数据的数量和数据的类型必须和形参一致。

当一个函数被调用之后,函数的形参才会得到对应的内存,并且函数的形参的内存只会在函数内部生效,当函数调用完成后,则函数形参的内存会被系统自动释放。

当用户调用一个函数时,如果函数有参数列表,则用户需要提供对应的数据给函数,而用户提供的数据的类型必须和函数参数类型一致,用户实际提供的数据被称为实际参数,简称为实参,而实参是必须存在的,实参的形式可以是表达式、常量、变量、地址........

参数的单向传递
只是把实参的值传递给函数作为参数,在函数内部对数值进行修改是不会影响外部实参的!实参的名称和函数形参的名称不需要一致,只需要确保实参的类型和函数形参的类型一致即可,如果类型不一致,则会出现数据精度异常。可以通过函数的返回值获取函数的处理结果,函数中调用return语句可以把结果返回给被调用的位置。

#include <stdio.h>

int add(int a,int b)
{
	a=30;
	b=40;
	int sum;
	sum = a + b;
	return sum;
}
int main(int argc, char const *argv[])
{
	int sum = 0;
	int c = 10;
	int d = 20;
	sum	= add(c,d);				//值是单向传递,c和d的值不会改变
	printf("c=%d\n",c);   		//c=10
	printf("d=%d\n",d);   		//d=20
	printf("sum=%d\n",sum);		//sum=70

   	return 0;
}

运行结果:
image

参数的双向传递
如果不打算调用return语句,则可以选择把实参的地址作为参数传递给函数内部,这样函数内部对地址中的数据进行修改,则函数外部的实参地址下的值也会变化,只不过此时函数参数类型应该是指针才可以。

#include <stdio.h>

void add(int *a,int *b)
{
	*a=30;
	*b=40;
}
int main(int argc, char const *argv[])
{
	int sum = 0;
	int c = 10;
	int d = 20;
	printf("c=%d\n",c);   		//c=10
	printf("d=%d\n",d);   		//d=20
	add(&c,&d);					//传递的参数是地址,c和d的值会被修改
	printf("c=%d\n",c);   		//c=30
	printf("d=%d\n",d);   		//d=40

   	return 0;
}

运行结果:
image

变量的生命周期
对于生命周期是指变量的生命周期,也就是变量从得到内存到释放内存的时间就是变量的生命周期,程序中变量如果按照存储单元的属性分类,可以分为变量和常量,也可以按照生命周期进行划分,可以分为全局变量和局部变量。

(1)局部变量:在函数内部定义的变量或者在某个复合语句中定义的变量都称为局部变量!
(2)全局变量:在所有的函数外部(在所有复合语句外部)定义的变量就被称为全局变量!

变量的作用范围
作用范围指的是定义的变量的作用域,也就是变量的有效使用范围,对于全局变量而言,作用域是针对整个程序,所以程序中任何一个函数都有访问权限。对于局部变量而言。作用域只针对当前局部变量的复合语句有效。
当全局变量的名称和局部变量名称相同时,则应该遵循“就近原则”,也就是应该优先使用同一个作用域内的变量,如果该作用域中没有该变量,则可以扩大作用域。

数组传递
有时用户需要连续处理多个相同类型的数据,可以选择把多个类型相同的数据构造为一个数组,然后把数组作为参数传递给函数,本质就是把数组的地址传递过去,此时分为两种方案:

#include <stdio.h>

void func(int buf[5]) //数组作为函数参数传递时,实际上传递的是指针,数组名会被转换为指向数组首元素的指针
{

}
int main(int argc, char const *argv[])
{
	int arr[5] = {1, 2, 3, 4, 5};
	func(arr);  //传递数组名,实际上传递的是指向数组首元素的指针

   	return 0;
}
#include <stdio.h>

void func(int *buf)  //函数参数是一个指针,指向一个整数类型的数组
{

}
int main(int argc, char const *argv[])
{
	int arr[5] = {1, 2, 3, 4, 5};
	func(arr);  //传递数组名,实际上传递的是指向数组首元素的指针

   	return 0;
}

如果打算把数组作为参数传递给函数,则应该连同数组的长度一同作为参数传递给函数,而数组长度应该使用sizeof进行计算。

#include <stdio.h>

void func(int *buf, int size) 
{
	for (int i = 0; i < size; i++) 			//下标方式遍历数组
	{
		printf("buf[%d]=%d \n", i, buf[i]);
	}

	for (int i = 0; i < size; i++) 			//指针方式遍历数组
	{
		printf("*(buf+%d)=%d \n", i, *(buf + i));
	}
}
int main(int argc, char const *argv[])
{
	int arr[5] = {1, 2, 3, 4, 5};
	func(arr, sizeof(arr) / sizeof(arr[0]));  

   	return 0;
}

运行结果:
image

如果用户打算在一个函数中定义一个数组用来存储已经处理好的数据,但是C语言中规定不允许返回一个数组类型,也不可以把数组的地址(指针)作为参数返回(返回的是局部变量的地址,而局部变量的生命周期是针对函数的),会出现段错误,返回的地址已经没有访问权限。

解决方案:
(1)可以选择把函数内部的数组定义为全局变量,此时程序中任意函数都可以访问,并且数组内存是在程序终止后才会被释放。
(2)可以选择把函数内部的局部变量的生命周期延长,此时需要使用C语言中的存储类修饰符,就是C语言关键字之一的static关键字,static具有静态的含义,可以把局部变量的生命周期进行延长。
从内存角度分析:如果在函数内部定义一个局部变量,则系统会从内存分区中的栈空间中分配一块内存给局部变量,栈空间是由系统自动管理,所以当函数调用结束时系统会自动释放该局部变量的内存。
如果函数中定义的局部变量使用static关键字进行修饰,则系统会从全局数据区分配内存空间给该局部变量,全局数据区的生命周期是跟随程序的,不会因为函数结束而释放。
static除了可以修饰局部变量外,也可以用于修饰函数,如果一个函数在定义的时候使用static关键字进行修饰,则可以限制函数的作用域为文件内部有效。

#include <stdio.h>

char *func(void) 
{
	static char buf[10] = "Hello";
	return buf;
}
int main(int argc, char const *argv[])
{
	char *p = NULL;
	p = func();
	printf("%s\n", p);
	return 0;
}

运行结果:
image

posted @ 2026-08-02 22:24  !城北徐公.!  阅读(8)  评论(0)    收藏  举报