C语言入门---函数

声明:以下文字仅自己个人总结并手打!!!不存在抄袭!!!

1.函数的声明与定义

1.1什么是函数的声明和定义?

函数的声明和定义是函数在使用前必须进行的两个步骤。 函数的声明是告诉编译器有一个函数的存在,它将在后面定义或实现。它包括函数的名称、返回类型和参数列表。例如,函数声明可以是 "int add(int a, int b)",告诉编译器有一个名为add的函数,它接受两个整数作为参数,并返回一个整数。 函数的定义则是函数的实际实现,它包括函数的功能代码和返回值。例如,函数定义可以是 "int add(int a, int b) { return a + b; }",实现了add函数的功能,并返回了两个参数的和。 需要注意的是,函数的声明和定义通常是分开的。在调用函数之前,必须先声明它,然后才能在需要的地方定义它。

如下;

#include <stdio.h>
//函数的定义
void success()
{
    printf("we will succeed");
}

int main() {
    success(); //调用success()
    return 0;
}
void success()
{
    printf("we will succeed");
}
函数的定义

注:如果函数的定义放在main函数的下面,需要在main函数提前声明,避免编译器出现警告!

#include <stdio.h>

void success();

int main() {
    success(); //调用success()
    return 0;
}
//函数的定义
void success()
{
    printf("we will succeed");
}

void success();对函数声明

两者输出结果:

1.2函数的嵌套调用

新建strive.c 和 strive.h

在头文件中定义 #include <stdio.h>  原因是main.c和strive.c 同时使用到了strive.h  这样在多个使用同一个头文件时,更加的方便。

可直接在main.c 和strive.c 直接调用strive.h 

疑问点:为什么这里的编译预处理命令(#include "strive.h")为什么是"" 而不是<>???

<>的意思是在c语言的系统文件中寻找,找不到报错

而" "表示先从自己的目录寻找后在系统文件寻找,找不到报错

2.在头文件中声明void success()

我们可以看出下方代码不用在main上方提前声明 也不会出现编译器警告现象! 

原因是 我们在头文件中声明了,之后在strive.c中添加了编译预处理命令(#include "strive.h")表明已经声明。

运行结果:

在头文件(strive.h)中 添加

int strive_succeed(int i);  //对strive_succed函数声明,有形参有返回值 返回值类型是int

之后在strive.c里面进行嵌套调用

//
// Created by HP on 2023/9/27.
//
#include "strive.h"
int strive_succeed(int i)
{
    printf("*******************\n");
    printf("strive_succeed %d\n",i);

}

//在这里调用 strive_succeed 就是嵌套调用
void success()
{
    printf("we will succeed\n");
    strive_succeed(6);
}

void success()
{
    printf("we will succeed\n");
    strive_succeed(6);
}
这里就是嵌套调用

嵌套调用图解: main调用了strive.c 和strivr.h    strive.c是调用了strive.h中的语句

1.3 C语言的编译和执行特点:

1. 分别编译:多个C文件可以分别进行编译,提高编译效率。由于是分别编译,每个C文件都可以独立进行编译,而不会影响其他文件。

2. 头文件拷贝:头文件包含在每个C文件中,在编译时直接拷贝到每个C文件中。头文件不参与编译,只是包含在C文件中。

3. 执行过程:程序从main函数开始执行,当main函数结束时,整个程序也就结束了。

1.4 嵌套调用定义

嵌套调用是程序中一个函数调用另一个函数的情况。在这种情况下,被调用的函数可以再调用其他函数,形成一层一层的调用关系,就像穿袜子一样,一层一层地套上。这种调用方式在程序中是允许的,但要注意,函数之间是互相平行的,不能在一个函数内部再定义另一个函数,这是不允许的。

1.5 函数声明与定义的差异

函数的定义是指函数功能的确立,包括函数名、函数值类型(返回值类型)、形参及其类型和函数体(下面对函数定义做出解释)例如,在C语言中,函数定义需要明确函数的功能、输入参数类型、返回值类型以及函数体中的具体实现。

差异:而函数声明则是不需要包含函数体的。它主要声明了函数返回值类型、函数名以及形参及其类型。在C语言中,函数声明只需要说明函数的返回值类型、函数名以及输入参数的类型即可,不需要写出函数的具体实现代码。 总之,函数声明与定义的主要区别在于是否包含函数体。函数定义包含了函数体,而函数声明则只是声明的函数的返回值类型、函数名以及输入参数的类型。

举个栗子
int(函数值类型) strive_succeed(函数名)(int(形参的类型) i)

{     }    形参体

1.6 什么是无参函数有参函数?

无参函数和有参函数是函数定义中的两种基本类型,它们的主要区别在于函数是否接受参数

无参函数是指在主调函数调用被调函数时,主调函数不向被调函数传递数据。无参函数通常用于执行特定的任务,如输出信息、获取系统时间等,也可以有返回值,但一般以没有返回值居多。无参函数的调用方式相对简单,只需要函数名和一对圆括号即可。

举个栗子
void sayHello() 
 {
  printf("Hello World!\n");
}

有参函数则是指在主调函数调用被调函数时,主调函数通过参数向被调函数传递数据。在一般情况下,有参函数在执行被调函数时会得到一个值并返回给主调函数使用。有参函数的使用需要注意在定义函数时,必须要指定形参的类型,同时实参与形参的类型要相同或赋值兼容。有参函数的调用方式需要在函数名后面的圆括号内传递参数,参数可以是常量、变量或表达式。

举个栗子:

​
​
int search(int arr[], int n, int key) 
{
  for (int i = 0; i < n; i++)
 {
    if (arr[i] == key) 
{
      return i;   //找到的元素在数组中的索引
    }

  }
  return -1;   // 如果没有找到元素则返回-1表示失败
}

​

​

2.函数递归调用

2.1为什么要使用递归?

使用递归的意义在于降低算法的难度,使得初学者也能够理解和实现。在快速排序等算法中,使用递归可以让代码更加简洁易懂。递归能够将问题分解成更小的子问题,降低问题的复杂度,使算法的实现更加直观和易于理解。

2.2递归例题演示(经典楼梯问题)

 假如有n给台阶,一次只能上1个或者2个台阶,走到第n个台阶有几种走法?代码如下:

#include <stdio.h>
int main() {
    int n;
    scanf("%d",&n);
    printf("step(%d)=%d",n,step(n));
    return 0;
}

 int step(int n){
     if(2==n ||  1==n)
     {
         return n;
     }
     return step(n-2)+step(n-1);

}
解析代码:n表示楼梯数   step表示一次走几步
#include <stdio.h>
int main() {
    int n;
    scanf("%d",&n);                 //scanf输入n(楼梯数)
    printf("step(%d)=%d",n,step(n));    //输出
    return 0;
}

 int step(int n){                  //设置类型
     if(2==n || 1==n)             //当n等于2或1时 输出n的值 即n=1 n=2
     {
         return n;                //输出n值
     }
     return step(n-2)+step(n-1);   //题目规则一次只能走一步或两步

}

2.3递归楼梯问题的核心(同递归的核心)

找公式

具体来说,要找到给定台阶数n和前一个台阶数n-1之间的关系。在这个例子中,通过递归的方式,将问题不断拆分成更小的子问题,直到到达基础情况(也就是结束条件)。 在实现递归时,首先要确定递归公式,即n*f(n-1)的关系。然后使用这个公式来实现递归函数。当传递到结束条件时,递归结束,然后返回结果。

3.全局变量和局部变量

3.1全局变量

3.1.1全局变量的定义

全局变量是在函数外部定义的变量,也被称为全称变量,它们在整个程序中都有效,从定义位置到文件尾部有效。

全局变量代码演示:通过代码可以看出succe_c和main函数同时使用了i 并成功运行了i的结果

#include <stdio.h>

int i=10;    //i是全局变量
void succeed_c(int c)
{
    printf("succeed_c=%d\n",i);
}
int main() {
    printf("main=%d\n",i);
    succeed_c(4);         //调用succeed_c
    return 0;
}

运行结果:

3.1.2不建议使用全局变量的原因

1. 全局变量在程序执行过程中会一直占用内存单元,不会释放掉。这会导致内存的浪费,特别是在程序执行结束后,这些全局变量仍然会占用内存空间。

2. 当局部变量与全局变量重名时,编译器会采用就近原则(看下图),即访问和修改的是局部变量的值,而非全局变量的值。这可能会导致程序的逻辑出错,影响代码的正确性。 因此,为了提高程序的可读性和可维护性,避免不必要的错误和内存浪费,不建议使用全局变量。

main函数下光标处 的int i=5根据就近原则 使得结果输出main=5 

3.2局部变量

3.2.1局部变量的定义

局部变量是在函数内部定义的变量,也被称为内部变量,它们只在其所在的函数内有效。这些变量只在该函数内部使用,在其他函数或主函数中无法直接使用。 形参也是局部变量的一种,它们在函数内部定义,并且只在该函数内部使用。形参的作用范围仅限于其所在的函数中,在函数外部无法直接使用。 需要注意的是,局部变量名不能和形参同名,否则会导致编译错误。同时,不同函数中可以定义相同的变量,但这些变量的作用范围仅限于各自所在的函数中,不会互相干扰。

3.2.2局部变量常见错误

1.局部变量时只能在同给括号使用,否则会出现报错!!!

这里只是举了一个基本例子还有比如for(),while()循环在()内命名的,在括号外不能使用。

2.形参也可做为局部变量,同就近原则。

3.2.3实参和形参是什么?他们的区别是什么?

1.实参是调用函数时提供的参数,用于在函数内部使用。实参可以是常量、变量、表达式等。

2.形参是定义函数时声明的参数,用于接收调用函数时传递的参数。形参必须指定类型,并且可以有多个。在函数内部,形参可以作为局部变量使用,也可以作为返回值返回给调用函数。 总之,实参和形参的主要区别在于它们的作用时间、作用范围和传递方式不同。

3.3全局变量和局部变量的区别

全局变量和局部变量的区别主要体现在作用域和存储位置

全局变量在整个程序中都可用,而局部变量仅在定义它的函数或循环等范围内可用。全局变量存储在全局数据区中,而局部变量存储在栈区。编译器在设计时规定,如果局部变量与全局变量重名,那么将采用就近原则,即实际获取和修改的都是局部变量的值。因此,不建议使用全局变量,以避免可能出现的逻辑错误。

(以上均个人理解)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值