文章目录
1. 函数的声明、定义 与 调用
函数的定义是对函数整体功能的确定,即完整地定义一个函数;C语言的函数可以嵌套调用,但是不能嵌套定义(即在函数中再定义函数,这种做法是不可行的)。
而函数的声明则相对简略些,具体包括把函数的名字、函数类型及形参的类型、个数和顺序通知编译系统,以便在调用该函数时编译系统能正确识别函数并检查调用是否合法。
;
在做函数声明的时候,要尽可能避免做出隐式声明。隐式声明即省略一些可以省略的声明信息,使其使用默认值。如:函数如果不显式地声明返回值的类型,那么它默认返回整型。隐式声明不是好的习惯。
主函数可以调用其他子函数。
子函数之间也可以互相调用。
但是子函数不可以调用主函数。
直接定义一个子函数,然后在主函数中调用它。代码示例如下。
#include <stdio.h>
void print_hello(){
printf("%s\n","hello");
}
int main() {
print_hello();
return 0;
}
作为规范的写法,函数必须先被声明或者定义,后被调用。而不能先调用后定义,不然会产生警告。
当调用未被定义的子函数的时候,默认返回的是int
类型。如果先调用,后定义子函数,且子函数的返回值类型不为int
,则会发生类型不匹配的警告。
虽然也可以先声明,再调用,最后再定义。但是不建议这样写。一般建议主函数写在最后。
下边便是,合理但是不推荐的写法的示例:
#include <stdio.h>
//先声明
void print_hello();
int main() {
print_hello(); //调用
return 0;
}
// 定义
void print_hello(){
printf("%s\n","hello");
}
2.多文件写法
对于较大的程序项目,通常将程序内容分别放在若干源文件中,再由若干源程序文件组成一个 C 程序。
这样处理便于分别编写、分别编译,进而提高调试效率。一个源程序文件可以为多个C 程序共用。
下边做一个简单示例。首先创建一个项目文件夹,以test
为例。
这里以VScode
为工具为例。多文件写法具体包含三部分:
2.1 头文件(xxx.h)
在test文件夹内,新建一个后缀为.h
的文件,即头文件。这里以func.h
为例。
假设我想要定义的是两个函数,分别是print_hello()
和print_sum()
。
则需要在func.h
中声明这两个函数。
如果这两个函数有需要引用的头文件,则也应在func.h
中引用。
具体代码示例如下:
#include <stdio.h>
void print_hello();
int print_sum(int a,int b);
2.2 定义函数的源文件(xxx.c)
头文件中只做了函数的声明,并未做函数的定义。函数的定义应体现在若干个(.c
)源文件中。
以一个源文件func.c
为例,
在func.c
中定义print_hello()
和print_sum()
的相关代码示例如下:
#include "func.h"
void print_hello(){
printf("%s\n","hello");
}
int print_sum(int a,int b){
int result;
result = a+b;
printf("%d\n",result);
return result;
}
因为我们在这里引用了头文件"func.h"
,而头文件中也引用了已经引用<stdio.h>,所以在func.c
中不需要再重复引用,只需要引用"func.h"
即可。
统一将每个源文件要引用的头文件都写在一个固定的头文件(func.h)中,然后每个源文件 引用该固定的头文件。这样写的好处是,降低了代码的冗余度,也有利于每个.c
源文件之间都能够实现交叉引用。而不是在需要的时候再手动一个一个引用。
2.3 主函数文件(main.c)
主函数文件,同命名为main.c
。C程序的执行是从main函数开始的,也结束于main函数。
主文件也是需要引用头文件的,且需要确认头文件func.h
存在,且位于与主程序文件相同的目录下。
#include "func.h"
int main(){
print_hello();
print_sum(100, 200);
return 0;
}
2.4 程序执行
编辑好后的三个文件在VSCode
中如下:
要执行程序,可以通过命令函来实现。首先通过命令行进入到test
文件夹下,
然后执行以下命令,即可得到一个可执行的程序文件:
gcc main.c func.c -o program
得到exe程序后,可以双击执行(双击执行可能不会有任何显示效果)。
也可以继续在命令行中执行,以查看程序输出效果。
program.exe
程序输出结果:
hello
300
3.函数的递归调用
递归,即函数在满足或不满足一定约束条件的情况下,函数自身调用自身的使用方法。
以定义一个求阶乘的函数为例。代码示例如下:
# include <stdio.h>
int fun(int n){
if (1==n){
return 1;
}
return n*fun(n-1);
}
int main(){
int num;
scanf("%d",&num);
printf("f(%d)=%d",num,fun(num));
return 0;
}
输入;
10
输出:
f(10)=3628800
这是递归的基本用法,使用递归的核心便是找公式,尤其是当面对较为复杂的问题的时候。
n=1 f(1)=?
n=2 f(2)=?
n=3 f(3)=?
…
n=x f(x)=?
下边给出一个例子,以一个上台阶的问题为例。
假设台阶一共有n阶,每一步可以走1阶,也可以走两个台阶,不可以走更多。问一共有多少种上台阶的方法。
代码实现如下:
# include <stdio.h>
int fun(int n){
if (1==n||2==n){
return n;
}
return fun(n-1)+fun(n-2);
}
int main(){
int num;
scanf("%d",&num);
printf("fun(%d)=%d",num,fun(num));
return 0;
}
输入;
5
输出:
fun(10)=8
4.全局变量
数据传递的方式有三种:
①参数
②返回值
③全局变量
全局变量定义在函数之外,也称外部变量,全程变量。一般不建议使用。全局变量可以在全局使用,在定义的函数内也可以使用。
下边定义全局变量i
示例如下。
# include <stdio.h>
int i=100;
void print(){
printf("i=%d\n", i);
}
int main(){
print();
}
程序输出结果:
i=100
如果在某个函数中修改全局变量,以主函数为例:
# include <stdio.h>
int i=100;
void print(){
printf("i=%d\n", i);
}
int main(){
int i=10;
print();
}
程序输出结果依然是100,而不是10.
i=100
这是因为,全局变量是存储在数据段中的,而函数内的局部变量是存储在栈空间内的。主函数中定义的变量i存在与栈空间内,而print
函数中的数据存储于另一个栈空间内。因为print
函数对应的栈空间内没有定义局部变量i
,所以访问的是全局变量中的变量i
。
;
如果是在子函数print
中定义了变量i=10
,则在print
函数中打印的i
则为10。此时此处访问的i
则为print
函数对应的栈空间内的变量i
。
局部变量只在其当前作用于内有效,即其所在的大括号内。局部变量也是有层级的,即多层大括号嵌套的时候。内层大括号内没有定义某局部变量时,可以在这里访问外层大括号内定义的该局部变量的。但是外层大括号不能访问内层大括号内定义的变量。(如,for
循环括号内定义的变量在该for
循环外不可用。)
;
一般要避免全局变量与局部变量同名。或者避免使用全局变量。