c_snake_game

第 5 课:函数 🔧

函数是可重复使用的代码块。本课将学习如何编写和使用函数。


5.1 为什么需要函数?

想象你要做 10 次同样的计算。你会复制粘贴代码 10 次吗?❌

函数的好处:


5.2 函数的结构

返回类型 函数名 (参数列表) {
    // 函数体
    // 执行的代码
    
    return 返回值;  // 如果返回类型不是 void
}

示例:计算平方

// 函数定义
int square(int x) {
    return x * x;
}

// 函数调用
int result = square(5);  // result = 25

5.3 函数的组成部分

    int      add       (int a, int b)
                     
  返回类型  函数名    参数列表
                      {
                          return a + b;  // 函数体
                      }

详细说明

部分 说明 示例
返回类型 函数返回值的类型 int, float, void
函数名 函数的名字 calculate_score
参数列表 传入函数的值 (int x, int y)
函数体 实际执行的代码 { ... }
返回值 函数执行的结果 return result;

5.4 函数的声明与定义

声明(在头文件中)

// snake.h
int snake_get_length(snake_t* snake);
void snake_move(snake_t* snake);

定义(在源文件中)

// snake.c
#include "snake.h"

int snake_get_length(snake_t* snake) {
    return snake->length;
}

void snake_move(snake_t* snake) {
    // 移动蛇的逻辑
}

5.5 贪吃蛇中的函数示例

示例 1:无参数无返回值

void clear_screen(void) {
    printf("\033[2J\033[H");  // 清屏转义序列
}

示例 2:有参数无返回值

void draw_char(int x, int y, char ch) {
    printf("\033[%d;%dH%c", y, x, ch);
}

示例 3:有参数有返回值

int calculate_score(int food_eaten, int level) {
    return food_eaten * 10 * level;
}

示例 4:多个参数

bool check_collision(int snake_x, int snake_y, 
                     int wall_x, int wall_y) {
    return (snake_x == wall_x && snake_y == wall_y);
}

5.6 函数调用方式

// 1. 直接使用返回值
int result = square(5);

// 2. 作为其他函数的参数
printf("结果是:%d\n", square(5));

// 3. 忽略返回值(void 函数)
clear_screen();

// 4. 在条件语句中
if (check_collision(x, y, wall_x, wall_y)) {
    game_over();
}

5.7 作用域规则

int global_var = 100;  // 全局变量,任何地方都能访问

void function1(void) {
    int local_var = 50;  // 局部变量,只在函数内有效
    printf("%d\n", global_var);  // ✅ 可以访问
    printf("%d\n", local_var);   // ✅ 可以访问
}

void function2(void) {
    printf("%d\n", global_var);  // ✅ 可以访问
    printf("%d\n", local_var);   // ❌ 错误!local_var 不存在于此
}

作用域规则总结

┌─────────────────────────────────────┐
│  全局作用域                          │
│  int global_var;                     │
│  ┌─────────────────────────────┐    │
│  │  函数 1 局部作用域            │    │
│  │  int local1;                │    │
│  └─────────────────────────────┘    │
│  ┌─────────────────────────────┐    │
│  │  函数 2 局部作用域            │    │
│  │  int local2;                │    │
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘

5.8 递归函数(简介)

函数可以调用自己,这叫递归:

// 计算阶乘:5! = 5 × 4 × 3 × 2 × 1 = 120
int factorial(int n) {
    if (n <= 1) {
        return 1;  // 基础情况
    }
    return n * factorial(n - 1);  // 递归调用
}

int main(void) {
    int result = factorial(5);  // result = 120
    printf("5! = %d\n", result);
    return 0;
}

5.9 实战:重构贪吃蛇代码

重构前(所有代码在 main 中)

int main(void) {
    // 初始化
    int score = 0;
    int snake_x = 10, snake_y = 10;
    
    // 游戏循环
    while (1) {
        // 处理输入
        // 更新蛇位置
        snake_x += 1;
        
        // 检查碰撞
        if (snake_x >= 80) {
            printf("Game Over!\n");
            break;
        }
        
        // 绘制
        printf("Snake at (%d, %d)\n", snake_x, snake_y);
    }
    
    return 0;
}

重构后(使用函数)

void init_game(game_t* game) {
    game->score = 0;
    game->snake_x = 10;
    game->snake_y = 10;
}

void update_snake(snake_t* snake) {
    snake->x += 1;
}

bool check_wall_collision(snake_t* snake, int width) {
    return snake->x >= width;
}

void render_game(game_t* game) {
    printf("Snake at (%d, %d)\n", game->snake_x, game->snake_y);
}

int main(void) {
    game_t game;
    init_game(&game);
    
    while (1) {
        update_snake(&game.snake);
        
        if (check_wall_collision(&game.snake, 80)) {
            printf("Game Over!\n");
            break;
        }
        
        render_game(&game);
    }
    
    return 0;
}

✅ 本课检查清单


📝 作业

  1. 编写一个函数 int max(int a, int b),返回两个数中的较大值

  2. 编写一个函数 void print_box(int width, int height),打印指定大小的矩形框

  3. 将你的贪吃蛇代码中的功能分解成函数:

    • init_game() - 初始化游戏
    • handle_input() - 处理输入
    • update_game() - 更新游戏状态
    • render_game() - 绘制画面

下一课:结构体