C语言 - 函数

目录

函数基础

函数的概念

形参和实参

函数分类

从定义角度分类(即函数是谁实现的)

从参数角度分类

从返回值角度分类

函数使用

函数的定义

函数的声明

函数的调用

函数实践

不声明类型

函数的嵌套使用

函数的递归使用

数组作函数参数

局部变量和全局变量

全局变量

局部变量

内部函数和外部函数

外部函数

内部函数

内部函数和外部函数的区别

注意事项


函数基础

函数的概念

函数是c语言的功能单位,实现一个功能可以封装一个函数来实现。

定义函数的时候一切以功能为目的,根据功能去定函数的参数和返回值。

形参和实参

形参:形式参数,主要用于保存实参传递的值,本质跟实参没有任何关系,只是值的传递

实参:实际参数,本质就是在函数调用候将实参的值传递给形参

注意事项:实参可以是任意类型的数据。形参可以是变量,或缺省常量,不可以是表达式。

函数分类

从定义角度分类(即函数是谁实现的)

(1)库函数(c 库实现的,例如stdio.h)        用 < >

(2)自定义函数(编码人自己实现的函数) 用" "

(3)系统函数(操作系统实现的函数)

从参数角度分类

(1)有参函数

函数有形参,可以是一个,或者多个,参数的类型随便

完全取决于函数的功能

(2)无参函数

函数没有参数,在形参列表的位置写个void或什么都不写

从返回值角度分类

(1)带返回值的函数

在定义函数的时候,必须带着返回值类型,在函数体里,必须有return

如果没有返回值类型,默认返回整型。

char fun(形参列表)//定义了一个返回值为字符型的函数 
{ 
    函数体 
    return X;//X为字符型 
}

注意:如果把函数的返回值类型省略了,默认返回整型

在定义函数的时候,函数的返回值类型,到底是什么类型的,取决于函数的功能。

(2)不带返回值的函数

在定义函数的时候,函数名字前面加void

在函数里不需要return或者直接return ;什么都不返回就可以了

void fun(形参列表)
{ 
    函数体
    return ;//或者直接不写return
}

函数使用

函数的定义

用法格式

返回值类型 函数名(形参列表)
{ 
    函数体 - 函数的功能在函数体里实现 
}

函数的声明

有些情况下,如果不对函数进行声明,编译器在编译的时候,可能不认识这个函数,因为编译器在编译c程序的时候,从上往下编译的。而且函数的声明可以声明多次

  • 何时声明

(1)当主调函数和被调函数在同一个.c 文件中时:

       如果被调函数在上,主调函数在下,这种情况不需要声明

        如果被调函数在下,主调函数在上需要声明

(2)主调函数和被调函数不在同一个.c 文件中的时候一定要声明

  • 声明方法

(1)直接声明法

主调函数和被调函数在同一个.c 文件中的时,用直接声明法。

方法1:直接将被调用的函数的第一行拷贝过去后面加分号,即

返回值类型 函数名(形参列表);

方法2:可以将形参列表换为数据类型,即

返回值类型 函数名(数据类型);

示例:

#include<stdio.h> 
void GOOD(int a,int b); //直接声明法-1 
void GOOD(int,int);     //直接声明法-2 
int main()
{
    主体函数
} 
void GOOD(int a,int b) 
{
    函数体
}

(2)间接声明法

主调函数和被调函数不在同一个.c 文件中的时:

        1.将被调用的函数的第一行拷贝过去,后面加分号,前面加extern

        2.将函数的声明放在头文件中 .c 程序包含头文件

注意:要向头文件声明,借助头文件实现间接声明,向头文件声明时,要用" "(自定义函数)而非< >(库函数)

函数的调用

  • 调用方法

(1)带返回值的:

变量= 函数名(实参列表);

(2)不带返回值的

函数名(实参列表);
  • 代码案例

1.没有参数,没有返回值

直接写函数名,并且要在后面加括号

void fun(); 
int main() 
{
    fun();//函数的调用 
} 
void fun()
{
    语句块
}

2.有参数,没有返回值

需要在函数名右边括号中传入参数,参数可以是常量表达式,也可以是变量表达式

void sum(int x,int y); 
int main() 
{ 
    sum(4,5);//方式1 
    int a=4,b=5;//方式2 
    sum(a,b); 
    return 0; 
} 
void sum(int x,int y) 
{ 
    int sum; 
    sum=x+y; 
    printf("%d\n",sum); 
}

3.有参数,有返回值

可以使用一个变量接收函数执行结果(返回值),或者直接使用也可以

int C(int m,int n);//声明C 
int main() 
{ 
    int H=C(432,17);//有参数,有返回值-方式1 
    printf("%d\n",H); 
    printf("%d\n",C(325,3));//有参数,有返回值-方式2 
    return 0; 
} 
int C(int m,int n) 
{ 
    int l; 
    l=m%n; 
    return l; 
}

4.没有参数,有返回值

int D();//声明D 
int main() 
{ 
    int U=D();//没有参数,有返回值 
    printf("%d\n",U);//方式1 
    printf("%d\n",D());//方式2 
    return 0; 
} 
int D() 
{ 
    int s=76,p=18; 
    return s*p; 
}

函数实践

不声明类型

即下面这段代码是可行的

#include<stdio.h> 
test(x,y); //返回值类型和参数类型不写,默认是int型 
int main() 
{ 
    test(5,6,7); //输出11 
    return 0; 
} 
void test(x,y) 
{ 
    int sum=x+y; 
    printf("%d\n",sum); 
}

函数的嵌套使用

虽然C语言的函数定义是互相平行、独立的,不能嵌套定义函数,但可以嵌套调用函数

案例:求四个数的最大数

函数的递归使用

直接或间接的地调用函数本身,称为函数的递归调用。C语言的特点之一就是允许函数的递归调用

案例:求n!

数组作函数参数

1.数组传参时,为了更好的使用和修改,一般不设置数组宽度

例如: void test(char str[])

案例:求10个数的平均值

2.给函数传数组的时候,没法一下将数组的内容作为整体传进去,只能传数组的地址,即只能传数组首元素的指针。将数组作为参数传递给函数

代码示例:

#include<stdio.h>
void f1(int m[]);//传一维数组_1
void f2(int *m);//传一维数组_2(常用)
void f3(int n[][4]);//二维传参_1
void f4(int (*n)[4]);//二维传参_2(常用)
void f5(char **x);//传指针数组  
int main()
{
    //传一维数组 
    int a[10]={1,2,3,4,5,6,7,8,9,10};
    f1(a);    f2(a);
    puts("***************************************");
    //传二维数组 
    int b[2][4]={1,3,5,7,2,4,6,8};
    f3(b);    f4(b);
    puts("***************************************");
    //传指针数组
    char *w[4]={"abcdef","hijklmn","jhkjbhjbhb","sklqkddi"};
    f5(&w);
    return 0;
}

void f1(int m[])//传一维数组_1
{
        printf("%d\t",m[2]);
        printf("%d\n",m[5]);
} 
void f2(int *m)//传一维数组_2(常用)
{
        printf("%d\t",m[1]);
        printf("%d\n",*(m+3));
}
void f3(int n[][4])//二维传参_1 
{
        printf("%d\t",n[1][3]);    
        printf("%d\n",n[0][3]);    
}
void f4(int (*n)[4])//二维传参_2(常用)
{
        printf("%d\t",n[0][2]);
        printf("%d\n",*(*(n+1)+1));    //p[x][y]<==> *(*(p + x) + y
}
void f5(char **x)//传指针数组 
{
        printf("%s\n",x[2]);
}

局部变量和全局变量

全局变量

  • 普通全局变量

普通全局变量在整个工程文件内都有效

int num=100;//num就是一个普通的全局变量 
  • 静态全局变量

静态全局变量只在定义的文件内有效

定义静态全局变量的时候,前面要用static修饰

static int x=100;//x就是一个静态全局变量 
  • 全局变量小结
  1. 生命周期:程序运行的整个过程,一直存在,直到程序结束。第一次调用函数的时候,开辟空间赋值,函数结束后,不释放,以后再调用函数的时候,就不再为其开辟空间,也不赋初值,用的是以前的那个变量。
  2. 作用域:普通全局变量在整个工程文件内都有效。静态全局变量只在定义的文件内有效
  3. 在函数外部定义的变量,即在main函数和子函数外部定义的变量叫做全局变量
  4. 定义全局变量时,如果不赋初值它的值默认为0,即如果全局变量没有进行初始化,则系统自动将其初始化为0

全局变量跨文件使用之前需要用extern声明,而且不能进行赋值操作

局部变量

  • 普通局部变量

生命周期:

在函数(复合语句)调用之前局部变量不占用空间,调用函数(复合语句)的时候才为局部变量开辟空间,函数(复合语句)结束时,局部变量也紧接着释放。

  • 静态局部变量

定义静态局部变量的时候,前面加static修饰

生命周期:

第一次调用函数(复合语句)时开辟空间赋值,函数(复合语句)结束后,不释放,以后再调用函数时就不再为其开辟空间,也不赋初值,用的还是以前的那个变量,直到程序结束。

  • 局部变量小结
  1. 在函数(复合语句)内部定义的变量叫做局部变量
  2. 生命周期:普通局部变量在定义它的局部范围内有效,每次使用完后释放,下次使用时会重新初始化。静态局部变量程序仅分配一次内存,函数返回后,该变量不会消失,下次使用时继续使用此变量
  3. 普通局部变量不进行初始化,默认是随机值(有些编译器会报错)。静态局部变量不进行初始化,默认是0
  4. 作用域:局部变量只能在定义的函数(复合语句)内部使用,生命周期相对较短

内部函数和外部函数

外部函数

普通定义的函数,都是外部函数。即外部函数可以在程序的任何一个文件中调用

在分文件编程中,只需要将函数的实现过程写在指定的.c文件中,然后将其声明写在指定的.h文件中,其他文件只要包含了头文件,就可以使用外部函数

内部函数

内部函数也称之为静态函数,就是用static修饰的函数,即返回值类型前面加static修饰的函数被称为内部函数

static void fun()//内部(静态)函数 
{
    函数体
}

static 限定了函数的作用范围,只在所定义的.c文件中有效

内部函数和外部函数的区别

外部函数,不需要修饰,在所有地方都可以调用

内部函数,需要用static修饰,只能在所定义的.c文件中的函数调用

注意事项

  1. 函数名字是标识符,所以需要满足标识符的命名规则
  2. 函数传参时即可传变量也可传常量
  3. 有返回值的函数,最终返回的是变量所存储的数据的值,而不是变量
  4. 不同的形参之间要用逗号隔开。形参可以有,也可以没有,也可以有多个。但即使没有,函数名字后面也必须加括号。而且函数体上下位置必须有大括号
  5. 如果要返回函数执行的结果,即函数有返回值,则return后面跟的变量或者值的类型,必须与返回值类型保持一致
  6. 函数的定义不能嵌套,即不能在一个函数体内定义另外一个函数。所有的函数的定义是平行的,相同的函数名只能出现一次
  7. C语言函数的返回值和形参是可以不写的,不写的情况下默认是int类型。
  8. C语言下函数调用时不会对实参个数进行检测。
  9. C语言下void型函数可以写返回值。
  10. 函数的形参为数组时相当于指针,例如 void test(int a[20]) 函数中的参数相当于int*a,所占大小为四字节
  11. 函数的形参为数组时可以不指定数组个数,例如 void test(int num[])
  12. 在同一作用范围内,不允许变量重名。但作用范围不同的可以重名。

  13. 局部范围内,重名的全局变量不起作用。(即向上就近原则)

    int num = 100;     //全局变量 
    int main() 
    { 
        printf("%d\n",num);// 1 
        int num = 999;     //局部变量 
        printf("%d\n",num);// 2 
        return 0; 
    }

    代码分析:第一个num是全局变量,存放在静态全局区;第二个num是局部变量,存放在栈区。所以两个num虽然变量名相同,但不是在同一作用范围内,所以可以重名。而其中的向上就近原则指的是——相同的变量名在使用的时候以上方最近的那个为准,例如第一个printf输出100,第二个printf输出999

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值