函数---(定义,头文件,函数的调用机制, 函数的注意事项和细节讨论, 函数传递的两种方式)(变量的作用域)(static关键字)

函数的定义

基本语法:

调用函数和引用变量一样,都需要 “先定义后使用”, 定义函数是对函数功能的确立,其一般格式为:
返回值类型 函数名 (形参)
{
声明部分;
语句部分;
}

1)形参列表:表示函数的输入;
2) 函数中的语句:表示为了实现某一功能代码块;
3) 函数可以有返回值,也可以没有, 如果没有返回值,返回类型 声明为 void 。

代码演示:

//定义一个实现两数求和功能的函数
int sum(int x,int y){
int z;
z=x+y;
return z;
}

头文件

基本概念:

  1. 头文件是 扩展名为 .h 的文件,包含了 C 函数声明和宏定义,被多 个源文件中引用共享。有两种类型的头文件:程序员编写的头文件和 C 标准库自带的头文件;

  2. 在程序中要使用头文件,需要使用 C 预处理指令 #include 来引用它。前面我们已经看过 stdio.h 头文件,它是C 标准库自带的头文件。

  3. #include 叫做文件包含命令,用来引入对应的头文件(.h 文件)。#include 也是 C 语言预处理命令的一种。#include的处理过程很简单,就是将头文件的内容插入到该命令所在的位置,从而把头文件和当前源文件连接成一个源文件,这与复制粘贴的效果相同。但是我们不会直接在源文件中复制头文件的内容,因为这么做很容易出错,特别在程序是由多个源文件组成的时候。

  4. 建议把所有的常量、宏、系统全局变量和函数原型写在头文件中,在需要的时候随时引用这些头文件。

说明:头文件快速入门-C 程序相互调用函数,我们将 cal 声明到文件 myFun.h , 在 myFun.c 中定义 cal 函数,
当其它文件需要使用到 myFun.h 声明 的函数时,可以#include 该头文件,就可以使用了。

头文件的注意事项和细节说明

  1. 引用头文件相当于复制头文件的内容
  2. 源文件的名字 可以不和头文件一样,但是为了好管理,一般头文件名和源文件名一样.
  3. C 语言中 include <> 与 include “” 的区别
    include <>:引用的是编译器的类库路径里面的头文件,用于引用系统头文件。
    include “”:引用的是程序目录的相对路径中的头文件,如果在程序目录没有找到引用的头文件则到编译器的
    类库路径的目录下找该头文件,用于引用用户头文件。
    所以:
  • 用 引用 系统头文件,两种形式都会可以,include <> 效率高
  • 引用 用户头文件,只能使用 include “”
  1. 一个 #include 命令只能包含一个头文件,多个头文件需要多个 #include 命令
  2. 同一个头文件如果被多次引入,多次引入的效果和一次引入的效果相同,因为头文件在代码层面有防止重复引入的机制 [举例]
  3. 在一个被包含的文件(.c)中又可以包含另一个文件头文件(.h)
  4. 不管是标准头文件,还是自定义头文件,都只能包含变量和函数的声明,不能包含定义,否则在多次引入时会引起重复定义错误(!!!)

函数的调用机制

函数调用规则;
1)当调用(执行)一个函数时,就会开辟一个独立的空间(栈)。
2)每给栈相互独立。
3)当函数执行完毕后,会返回到调用函数的位置,继续执行后面的步骤。
4)如果函数有返回值,则将返回值赋给接收的变量。
5)当一个函数返回后,该函数对应的栈空间就会销毁。

函数的递归调用:

介绍:一个函数在函数体内又调用了本身,我们称为递归调用

函数递归需要遵守的重要原则:

  1. 执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)
  2. 函数的局部变量是独立的,不会相互影响
  3. 递归必须向退出递归的条件逼近,否则就是无限递归
  4. 当一个函数执行完毕,或者遇到 return,就会返回,遵守谁调用,就将结果返回给谁。

代码演示:

//斐波那切数列
//请使用递归的方式,求出  1,1,2,3,5,8,13...
//给你一个整数 n,求出它的斐波那契数是多少?
//分析:规律为当n为1与n为2时,斐波那契数为1;除此之外,整数为n时斐波那锲数为前两项数之和
#include<stdio.h>
#include<stdlib.h>

int fbn(int n){
if(n==1||n==2){
   return 1;
}
    else{
        return fbn(n-1)+fbn(n-2);
    }
}
void main(){
int res=fbn(7);
printf("res=%d",res);
}

在这里插入代码片//求函数值
//已知 f(1)=3; f(n) = 2*f(n-1)+1;
//请使用递归的思想编程,求出 f(n)的值?
#include<stdio.h>
#include<stdlib.h>

int f(int n){
if(n==1){
    return 3;
    }
      else{
        return 2*f(n-1);
}
}

void main(){
int qz=f(2);
printf("%d",qz);
}
结果为6

//题 3:猴子吃桃子问题
//有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。
//当到第十天时,想再吃时(还没吃),发现只有 1 个桃子了。问题:最初共多少个桃子?
//分析:第十天为1个,第n天的为(ts(n+1)+1)*2

int ts(int n){
if(n==10){
    return 1;
}
else{
    return (ts(n+1)+1)*2;
}
}
void main(){
    int sum=ts(7);
    printf("%d",sum);
}
结果为22

函数的注意事项和细节讨论:

1)函数的形参列表可以是多个;
2)c语言传递参数可以是值传递,也可以传递指针(也叫引用传递);
3)函数名的命名规则和标识符的命名规则相同;
4)函数中的变量是局部的,函数外不会生效;
5)基本数据类型默认是值传递,即进行值拷贝,在函数内修改,不会影响到原来的值;
6) 如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果上看类似引用(即传递指针) ;
7)c语言不支持重载;

代码练习:

//请编写一个函数 swap(int *n1, int *n2) 可以交换 n1 和 n2 的值
//说明
//1. 函数名为 swap
//2. 形参是两个指针类型 int*
void swap(int *n1, int *n2) {
int temp = *n1; //表示将 n1 这个指针指向的变量的值赋给 temp
*n1 = *n2;   // 表示将 n2 这个指针指向的变量的值赋给 n1 这个指针指向的变量
*n2 = temp;  //表示将 temp 值赋给 n2 这个指针指向的变量
}
void main() {
int n1 = 1;
int n2 = 2;
swap(&n1, &n2);
printf("main n1=%d n2=%d", n1, n2);// n1= 2 n2 = 1
getchar();
}

函数传递的两种方式

基本介绍:
我们在讲解函数注意事项和使用细节时,已经讲过 C 语言传递参数可以是值传递(pass by value),也可以传递指针(a pointer passed by value)也叫传递地址或者 引用传递。

两种传递方式

  1. 值传递
  2. 引用传递(传递指针、地址)
    其实,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的数据大小,数据越大,效率越低。

值传递和引用传递使用特点:
在这里插入图片描述
5) 如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量(*指针)。
从效果上看类似引用 【案例演示: 画出示意图】, 比如修改结构体的属性.

变量的作用域

基本说明

1、函数内部定义(声明)变量称为局部变量,作用域仅限函数内部。
2、代码块的中的局部变量(比如 if ,for)中的,作用域仅限代码块。
3、在所有函数外部定义的变量称为全局变量,它作用域为整个程序。
4、函数的参数,形式参数,会被看做是局部变量,当它与全局变量重名时,会优先使用局部变量(编译器使用就近原则)。

变量的初始化(包括局部变量和全局变量)

1、局部变量系统不会默认初始化,所以必须对局部变量初始化后才可以使用,否则程序会异常退出。
2、全局变量系统会默认初始化如下所示;
在这里插入图片描述

3、正确地初始化变量是一个良好的编程习惯,否则有时候程序可能会产生意想不到的结果,因为未初始化的变量会导致一些在内存位置中已经可用的垃圾值。

作用域的注意事项和细节

  1. 全局变量(Global Variable)保存在内存的全局存储区中,占用静态的存储单元,它的作用域默认是整个程序,也就是所有的代码文件,包括源文件(.c 文件)和头文件(.h 文件)。
  2. 局部变量(Local Variable)保存在栈中,函数被调用时才动态地为变量分配存储单元,它的作用域仅限于函数内部。【内存布局分析 】
  3. C 语言规定, 只能从小的作用域向大的作用域中去寻找变量,而不能反过来,使用更小的作用域中的变量
  4. 在 同一个作用域,变量名不能重复,在 不同的作用域,变量名可以重复,使用时编译器采用就近原则.
  5. 由{ } 包围的代码块也拥有独立的作用域。
#include<stdio.h>
#include<stdlib.h>
double price=200.0; //全局变量
void test01(){
printf("%.2f\n",price);
}

void test02(){
price=250;
printf("%.2f\n",price);
}

void main(){
printf("main price=%.2f\n",price);
test01();
test02();
test01();
system("pause");
}


static关键字

介绍:static 关键字在 c 语言中比较常用,使用恰当能够大大提高程序的模块化特性,有利于扩展和维护。
1、变量使用static修饰

【1】局部变量使用static修饰

1)局部使用static修饰后称为静态局部变量;

2)静态局部变量如果在声明时没有给赋初值,系统会默认初始化为0

3)静态局部变量存储在静态存储区,不存储在栈中了,并且只被初始化一次:

代码演示:

在这里插入代码片static int n; //n就是静态局部变量,默认初始值为0
fn();
 fn_static();
//printf("\n %d",n);
}

void fn(){
int n=10;//普通变量,每次执行都会初始化,n在栈区
printf("%d\n",n);//n==10
n++;
printf("n++=%d\n",n);
}

void fn_static(){
static int n=10;  //静态局部变量量,放在静态存储区,全局性质空间,不会执行初始化
printf("static n=%d\n",n);//n==10
n++; //n==11
printf("\n%d",n);
}


【2】、全局变量使用static修饰

  1. 普通全局变量对整个工程可见,其他文件可以使用 extern 外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量),静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响 [案例]
  2. 定义不需要与其他文件共享的全局变量时,加上 static 关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用;

2、函数使用static修饰

  1. 函数的使用方式与全局变量类似, 在函数的返回类型前加上 static ,就是静态函数;
  2. 非静态函数可以在另一个文件中通过 extern 引用 【案例】
  3. 静态函数只 能在声明它的文件中可见,其他文件不能引用该函数[案例]
  4. 不同的文件可以使用相同名字的静态函数,互不影响[案例]
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值