C 语言编程 — 函数

目录

前文列表

程序编译流程与 GCC 编译器
C 语言编程 — 基本语法
C 语言编程 — 基本数据类型
C 语言编程 — 变量与常量
C 语言编程 — 运算符
C 语言编程 — 逻辑控制语句

函数

函数大体上分为 3 类:

  1. 主函数:每个 C 程序都至少有一个 main()
  2. 内置函数:由 C 标准库提供。例如,strcat() 用来连接两个字符串,memcpy() 用来复制内存到另一个位置。
  3. 自定义函数

函数的声明

  • 函数声明就是告诉 C 编译器函数的名称、返回类型和参数以及如何调用函数。函数的实际主体可以单独定义,但当你在一个源文件中定义函数且在另一个源文件中调用函数时,函数声明是必需的。

函数声明包括以下几个部分:

return_type function_name(parameter list);

在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面两种都是有效的声明:

int max(int num1, int num2);
int max(int, int);

函数的定义

函数定义除了定义函数的名称、返回类型、形参列表之外,最终重要的是提供了函数的实际主体

C 程序中函数的结构如下:

return_type function_name(parameter list){
   body of the function
}
  • 返回类型:一个函数可以返回一个值,返回类型定义了函数返回的值的数据类型。有些函数不需要返回值,在这种情况下,return_type 是关键字 void
  • 函数名(函数标识符):函数名和参数列表一起构成了函数签名。
  • 形式参数:形参就像是占位符。当函数被调用时,向形参传递一个值,而这个值被称为实际参数。形参列表包括函数参数的类型、顺序、数量。形参是可选的,即形参列表可以为。
  • 函数主体:包含了一组定义函数执行任务的语句。
/* 函数返回两个数中较大的那个数 */
int max(int num1, int num2) 
{
   /* 局部变量声明 */
   int result;
 
   if (num1 > num2)
      result = num1;
   else
      result = num2;
 
   return result; 
}

函数的形参与实参

如果函数要使用参数,则必须声明接受参数值的变量。这些变量称为函数的形式参数。形式参数就像函数内的其他局部变量,在进入函数时被创建,退出函数时被销毁。当调用函数时,有两种向函数传递参数的方式:

  • 值传递:该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。
  • 引用传递:通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。

值传递

默认情况下,C 语言使用传值调用方法来传递参数。一般来说,这意味着函数内的代码不会改变用于调用函数的实际参数。

  • swap.c
/* 函数定义 */
void swap(int x, int y)
{
   int temp;

   temp = x; /* 保存 x 的值 */
   x = y;    /* 把 y 赋值给 x */
   y = temp; /* 把 temp 赋值给 y */
  
   return;
}
  • main.c
#include <stdio.h>
 
/* 函数声明 */
void swap(int x, int y);
 
int main ()
{
   /* 局部变量定义 */
   int a = 100;
   int b = 200;
 
   printf("交换前,a 的值: %d\n", a );
   printf("交换前,b 的值: %d\n", b );
 
   /* 调用函数来交换值 */
   swap(a, b);
 
   printf("交换后,a 的值: %d\n", a );
   printf("交换后,b 的值: %d\n", b );
 
   return 0;
}


引用传递

通过引用传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。传递指针可以让多个函数访问指针所引用的对象,而不用把对象声明为全局可访问。

  • swap.c
/* 函数定义 */
void swap(int *x, int *y)
{
   int temp;
   temp = *x;    /* 保存地址 x 的值 */
   *x = *y;      /* 把 y 赋值给 x */
   *y = temp;    /* 把 temp 赋值给 y */
  
   return;
}
  • main.c
#include <stdio.h>
 
/* 函数声明 */
void swap(int *x, int *y);
 
int main ()
{
   /* 局部变量定义 */
   int a = 100;
   int b = 200;
 
   printf("交换前,a 的值: %d\n", a );
   printf("交换前,b 的值: %d\n", b );
 
   /* 调用函数来交换值
    * &a 表示指向 a 的指针,即变量 a 的地址
    * &b 表示指向 b 的指针,即变量 b 的地址
   */
   swap(&a, &b);
 
   printf("交换后,a 的值: %d\n", a );
   printf("交换后,b 的值: %d\n", b );
 
   return 0;
}

可变长形参列表

有时候我们需要函数带有可变数量的参数,而不是预定义数量的参数。C 语言提供了 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏。

int func(int, ... ) {}
 
int main() {
   func(1);
   func(1, 2);
   func(1, 2, 3);
   return 0;
}
  • 定义一个函数,int 形参代表了要传递的可变参数的总数,... 可变长形参运算符标识函数的形参数量是可变的。
  • 在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
  • 使用 int 参数和 va_start 宏来初始化 va_list 变量为一个参数列表。宏 va_start 是在 stdarg.h 头文件中定义的。
  • 使用 va_arg 宏和 va_list 变量来访问参数列表中的每个项。
  • 使用宏 va_end 来清理赋予 va_list 变量的内存。
#include <stdio.h>
#include <stdarg.h>

double Avg(int num, ...) {
    va_list valist;
    double sum = 0.0;
    int i;

    /* 初始化 valist,数量为 num 个 */
    va_start(valist, num);
    for (i = 0; i < num; i++) {
        /* 访问所有赋给 valist 的参数 */
        sum += va_arg(valist, int);
    }
    /* 清理为 valist 保留的内存 */
    va_end(valist);

    return sum / num;

}

int main() {
    printf("Average of 2, 3, 4, 5 = %f\n", Avg(4, 2, 3, 4, 5));
    printf("Average of 5, 10, 15 = %f\n", Avg(3, 5, 10, 15));
    return 0;
}

运行:

$ ./main
Average of 2, 3, 4, 5 = 3.500000
Average of 5, 10, 15 = 10.000000

函数的调用

定义 C 函数时,会定义函数做什么,然后通过调用函数来完成已定义的任务。当程序调用函数时,程序控制权会转移给被调用的函数。被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束括号时,会把程序控制权交还给主程序。

调用函数时,传递所需参数,如果函数返回一个值,则可以存储返回值。

#include <stdio.h>
 
/* 函数声明 */
int max(int num1, int num2);
 
int main ()
{
   /* 局部变量定义 */
   int a = 100;
   int b = 200;
   int ret;
 
   /* 调用函数来获取最大值 */
   ret = max(a, b);
 
   printf( "Max value is : %d\n", ret );
 
   return 0;
}
 
/* 函数返回两个数中较大的那个数 */
int max(int num1, int num2) 
{
   /* 局部变量声明 */
   int result;
 
   if (num1 > num2)
      result = num1;
   else
      result = num2;
 
   return result; 
}

函数的指针

函数名本质就是一个地址,也就是一个指针。函数指针是指向函数的指针类型变量,函数指针可以像一般函数一样,用于调用函数、传递参数。也就是说,函数指针其实可以看做就是一个函数的 “别名”。

声明一个函数指针:

typedef int (*fun_ptr)(int, int);

e.g.

#include <stdio.h>
 
int max(int x, int y){
    return x > y ? x : y;
}
 
int main(void){
    /* 定义并初始化一个函数指针 */
    int (*p)(int, int) = &max;  // 可以省略地址运算符 &
    int a, b, c, d;
 
    printf("Input three numbers:");
    scanf("%d %d %d", &a, &b, &c);

    d = p(p(a, b), c); 
    printf("MAX: %d\n", d);
    
    return 0;
}

回调函数

回调函数就是将函数指针作为实参传入到某个函数,即:回调函数就是一个通过函数指针调用的函数。

#include <stdlib.h>
#include <stdio.h>

void populate_array(int *array, size_t arraySize, int (*getNextValue)(void)) {

    size_t i;

    for(i = 0; i < arraySize; i++) {
        array[i] = getNextValue();
    }
}

int getNextRandomValue(void) {
    return rand();
}

int main() {
    int i;
    int myarray[10];

    populate_array(myarray, 10, &getNextRandomValue);  // & 可以省略,因为函数名的本质就是一个指针

    for(i = 0; i < 10; i++) {
        printf("%d ", myarray[i]);
    }

    printf("\n");
    return 0;
}

递归函数

递归,指的是在函数的定义中调用函数自身

在这里插入图片描述

void recursion()
{
   statements;
   ... ... ...
   recursion(); /* 函数调用自身 */
   ... ... ...
}
 
int main()
{
   recursion();
}

但在使用递归时,一定要注意编写退出条件,否则会进入死循环。递归函数在解决许多数学问题上起了至关重要的作用,比如计算一个数的阶乘、生成斐波那契数列,等等。

数的阶乘

#include <stdio.h>
 
double factorial(unsigned int i) {
   if(i <= 1)
   {
      return 1;
   }
   return i * factorial(i - 1);
}

int main() {
    int i = 15;
    printf("%d 的阶乘为 %f\n", i, factorial(i));
    return 0;
}

斐波那契数列

#include <stdio.h>
 
int fibonaci(int i) {
   if (i == 0) {
      return 0;
   }
   if (i == 1) {
      return 1;
   }
   return fibonaci(i-1) + fibonaci(i-2);
}
 
int main() {
    int i;
    for (i = 0; i < 10; i++) {
       printf("%d\t\n", fibonaci(i));
    }
    return 0;
}
发布了500 篇原创文章 · 获赞 1352 · 访问量 188万+

猜你喜欢

转载自blog.csdn.net/Jmilk/article/details/105279885