C语言学习5-函数

一、变量作用域与生命周期

1.1 局部变量作用域规则

基本规则

  • 生命周期开始:变量定义处开始生效
  • 生命周期结束:所在代码块 } 结束时失效

代码示例

#include <stdio.h>

void demo_local_scope() {
    int outer = 100;  // outer 生效
    
    if (1) {
        int inner = 200;  // inner 生效
        printf("内部块: outer=%d, inner=%d\n", outer, inner);  // ✅ 都可以访问
    }  // inner 失效
    
    // printf("inner=%d\n", inner);  // ❌ 错误:inner 已失效
    printf("外部: outer=%d\n", outer);  // ✅ outer 仍然有效
}

int main() {
    demo_local_scope();
    return 0;
}

变量覆盖(Shadowing)示例

#include <stdio.h>

void demo_variable_shadowing() {
    int value = 10;  // 外层 value
    
    printf("外层 value = %d\n", value);  // 输出: 10
    
    {
        int value = 20;  // 内层 value(覆盖外层)
        printf("内层 value = %d\n", value);  // 输出: 20
        
        {
            int value = 30;  // 更内层 value
            printf("更内层 value = %d\n", value);  // 输出: 30
        }  // 30 失效,20 恢复可见
        
        printf("恢复内层 value = %d\n", value);  // 输出: 20
    }  // 20 失效,10 恢复可见
    
    printf("恢复外层 value = %d\n", value);  // 输出: 10
}

int main() {
    demo_variable_shadowing();
    return 0;
}

1.2 全局变量

全局变量特性

  • 定义在所有函数之外
  • 生命周期贯穿整个程序
  • 任何函数都可以访问和修改

全局变量示例

#include <stdio.h>

// 全局变量定义
int global_counter = 0;
const char* PROGRAM_NAME = "MyApp";

void increment_counter() {
    global_counter++;
    printf("[increment] 计数器: %d\n", global_counter);
}

void reset_counter() {
    global_counter = 0;
    printf("[reset] 计数器已重置\n");
}

void show_program_info() {
    printf("[info] 程序: %s, 计数器: %d\n", PROGRAM_NAME, global_counter);
}

int main() {
    printf("程序启动: %s\n", PROGRAM_NAME);
    
    show_program_info();  // 输出: 程序: MyApp, 计数器: 0
    
    global_counter = 5;
    increment_counter();  // 输出: [increment] 计数器: 6
    increment_counter();  // 输出: [increment] 计数器: 7
    
    reset_counter();      // 输出: [reset] 计数器已重置
    show_program_info();  // 输出: [info] 程序: MyApp, 计数器: 0
    
    return 0;
}

1.3 变量使用指南

选择原则

特性 局部变量 全局变量
作用域 限于代码块内 整个程序
生命周期 代码块执行期间 程序运行期间
内存管理 自动分配释放 始终存在
数据共享 困难 容易
代码维护 容易 困难

最佳实践

// ✅ 推荐:优先使用局部变量
void process_data() {
    int temp_result = calculate();  // 局部变量
    // 使用完后自动释放
}

// ✅ 必要时使用全局变量(如配置、状态)
int system_initialized = 0;        // 系统状态
const double PI = 3.14159;         // 常量配置

// ❌ 避免:滥用全局变量
// int temp_calculation_result;    // 不必要的全局变量

二、返回指针的函数

2.1 字符串处理函数示例

#include <stdio.h>
#include <ctype.h>

// 将字符串转换为大写,返回原字符串指针
char* to_uppercase(char *str) {
    char *original = str;  // 保存原始地址
    
    while (*str != '\0') {
        if (islower(*str)) {  // 使用标准库函数判断小写
            *str = toupper(*str);  // 转换为大写
        }
        str++;
    }
    
    return original;
}

// 安全的字符串反转函数
char* reverse_string(char *str) {
    if (str == NULL) {
        return NULL;  // 安全检查
    }
    
    char *start = str;
    char *end = str;
    
    // 找到字符串末尾
    while (*end != '\0') {
        end++;
    }
    end--;  // 指向最后一个字符
    
    // 前后交换
    while (start < end) {
        char temp = *start;
        *start = *end;
        *end = temp;
        
        start++;
        end--;
    }
    
    return str;
}

int main() {
    // ✅ 正确:字符数组(可修改)
    char text1[] = "hello world";
    printf("原始: %s\n", text1);
    printf("大写: %s\n", to_uppercase(text1));
    
    char text2[] = "abcdef";
    printf("反转: %s\n", reverse_string(text2));
    
    // ❌ 危险:字符串常量不可修改
    // char *text3 = "readonly";
    // to_uppercase(text3);  // 运行时错误!
    
    return 0;
}

2.2 返回指针的安全考虑

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

// ❌ 危险:返回局部变量的地址
char* dangerous_function() {
    char local_buffer[50] = "local data";
    return local_buffer;  // 错误!返回后将失效
}

// ✅ 安全:返回动态分配的内存
char* safe_dynamic_function() {
    char *buffer = malloc(100 * sizeof(char));
    if (buffer != NULL) {
        strcpy(buffer, "dynamic data");
    }
    return buffer;  // 调用者负责释放
}

// ✅ 安全:返回静态变量的地址
char* safe_static_function() {
    static char static_buffer[100] = "static data";
    return static_buffer;  // 静态变量生命周期长
}

// ✅ 安全:返回输入参数的指针
char* process_and_return(char *input) {
    // 处理输入数据...
    return input;  // 返回原始指针
}

void demo_safe_pointers() {
    // 使用动态内存
    char *dynamic_result = safe_dynamic_function();
    if (dynamic_result != NULL) {
        printf("动态: %s\n", dynamic_result);
        free(dynamic_result);  // 必须释放!
    }
    
    // 使用静态变量
    char *static_result = safe_static_function();
    printf("静态: %s\n", static_result);  // 无需释放
    
    // 处理并返回
    char input[] = "input data";
    char *processed = process_and_return(input);
    printf("处理: %s\n", processed);
}

int main() {
    demo_safe_pointers();
    return 0;
}

三、函数指针

3.1 函数指针基础

为什么需要函数指针?

  • 函数在内存中有确定的入口地址
  • 函数名就是该地址的符号表示
  • 指针可以存储任何内存地址,包括函数地址

3.2 函数指针的定义与使用

#include <stdio.h>

// 基础数学运算函数
int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

int multiply(int a, int b) {
    return a * b;
}

int divide(int a, int b) {
    return (b != 0) ? a / b : 0;
}

void demo_basic_function_pointer() {
    // 函数指针的多种定义方式
    int (*func_ptr1)(int, int) = add;           // 推荐:明确参数
    int (*func_ptr2)(int, int) = &subtract;     // 使用取地址符
    int (*func_ptr3)() = multiply;              // 省略参数(不推荐)
    
    // 调用方式的等价性
    int result1 = func_ptr1(10, 5);            // 直接调用
    int result2 = (*func_ptr2)(10, 5);         // 解引用调用
    int result3 = func_ptr3(10, 5);            // 省略参数调用
    
    printf("10 + 5 = %d\n", result1);    // 15
    printf("10 - 5 = %d\n", result2);    // 5
    printf("10 × 5 = %d\n", result3);    // 50
}

int main() {
    demo_basic_function_pointer();
    return 0;
}

3.3 函数指针作为参数(回调函数)

#include <stdio.h>

// 数学运算函数
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }

// 通用的计算函数 - 接受函数指针作为参数
void calculate_with_callback(const char *operation_name,
                           int (*math_operation)(int, int),
                           int x, int y) {
    if (math_operation == NULL) {
        printf("错误: 无效的操作函数\n");
        return;
    }
    
    int result = math_operation(x, y);
    
    // 确定操作符
    char operator;
    if (math_operation == add) operator = '+';
    else if (math_operation == subtract) operator = '-';
    else if (math_operation == multiply) operator = '*';
    else operator = '?';
    
    printf("%s: %d %c %d = %d\n", operation_name, x, operator, y, result);
}

// 更通用的版本
void universal_calculator(int a, int b, int (*operation)(int, int)) {
    if (operation != NULL) {
        int result = operation(a, b);
        printf("计算结果: %d\n", result);
    }
}

void demo_callback_functions() {
    int a = 12, b = 4;
    
    printf("=== 回调函数演示 ===\n");
    calculate_with_callback("加法运算", add, a, b);
    calculate_with_callback("减法运算", subtract, a, b);
    calculate_with_callback("乘法运算", multiply, a, b);
    
    printf("\n=== 通用计算器 ===\n");
    universal_calculator(a, b, add);
    universal_calculator(a, b, subtract);
    universal_calculator(a, b, multiply);
}

int main() {
    demo_callback_functions();
    return 0;
}

3.4 函数指针数组

#include <stdio.h>

// 系统操作函数
void startup() { printf("🔧 系统启动中...\n"); }
void shutdown() { printf("🛑 系统关闭中...\n"); }
void diagnostics() { printf("🔍 运行诊断检查...\n"); }
void maintenance() { printf("🛠️  执行维护任务...\n"); }

void demo_function_pointer_array() {
    // 函数指针数组
    void (*system_commands[])() = {
        startup,
        diagnostics, 
        maintenance,
        shutdown
    };
    
    const char *command_names[] = {
        "启动系统",
        "运行诊断", 
        "执行维护",
        "关闭系统"
    };
    
    int command_count = sizeof(system_commands) / sizeof(system_commands[0]);
    
    printf("=== 系统命令菜单 ===\n");
    for (int i = 0; i < command_count; i++) {
        printf("%d. %s\n", i + 1, command_names[i]);
    }
    
    printf("\n=== 执行命令序列 ===\n");
    for (int i = 0; i < command_count; i++) {
        printf("执行: %s\n", command_names[i]);
        system_commands[i]();  // 通过函数指针调用
        printf("---\n");
    }
}

int main() {
    demo_function_pointer_array();
    return 0;
}

四、高级函数指针用法

4.1 使用 typedef 简化函数指针

#include <stdio.h>

// 定义函数指针类型
typedef int (*MathFunction)(int, int);
typedef void (*Formatter)(const char*, int);

// 数学函数
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }

// 格式化函数
void pretty_format(const char* label, int value) {
    printf("✨ %s: %d\n", label, value);
}

void simple_format(const char* label, int value) {
    printf("%s = %d\n", label, value);
}

// 使用类型别名的函数
int calculate_with_format(int a, int b, 
                         MathFunction operation,
                         Formatter formatter) {
    int result = operation(a, b);
    formatter("计算结果", result);
    return result;
}

void demo_typedef_functions() {
    printf("=== 使用 typedef 的函数指针 ===\n");
    
    calculate_with_format(15, 3, add, pretty_format);
    calculate_with_format(15, 3, subtract, simple_format);
    
    // 也可以直接使用类型别名定义变量
    MathFunction func = multiply;
    Formatter fmt = simple_format;
    
    int result = func(5, 4);
    fmt("直接调用", result);
}

int main() {
    demo_typedef_functions();
    return 0;
}

4.2 实际应用:策略模式

#include <stdio.h>

// 定义排序策略类型
typedef int (*CompareStrategy)(int, int);

// 不同的比较策略
int ascending_order(int a, int b) {
    return a - b;  // 升序:a < b 返回负数
}

int descending_order(int a, int b) {
    return b - a;  // 降序:a > b 返回负数
}

int absolute_order(int a, int b) {
    int abs_a = (a < 0) ? -a : a;
    int abs_b = (b < 0) ? -b : b;
    return abs_a - abs_b;  // 按绝对值排序
}

// 通用的排序函数(模拟)
void sort_with_strategy(int arr[], int size, CompareStrategy strategy) {
    printf("使用");
    if (strategy == ascending_order) printf("升序");
    else if (strategy == descending_order) printf("降序");
    else if (strategy == absolute_order) printf("绝对值");
    printf("策略排序\n");
    
    // 这里可以实现实际的排序算法
    // 使用 strategy(a, b) 进行比较
    for (int i = 0; i < size - 1; i++) {
        for (int j = i + 1; j < size; j++) {
            if (strategy(arr[i], arr[j]) > 0) {
                // 交换元素
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }
}

void print_array(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

void demo_strategy_pattern() {
    int numbers[] = {3, -1, 4, -2, 5};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    
    printf("原始数组: ");
    print_array(numbers, size);
    
    printf("\n升序排序: ");
    sort_with_strategy(numbers, size, ascending_order);
    print_array(numbers, size);
    
    printf("降序排序: ");
    sort_with_strategy(numbers, size, descending_order);
    print_array(numbers, size);
    
    printf("绝对值排序: ");
    sort_with_strategy(numbers, size, absolute_order);
    print_array(numbers, size);
}

int main() {
    demo_strategy_pattern();
    return 0;
}

五、重要注意事项

5.1 函数指针声明语法

// ✅ 正确的函数指针声明
int (*function_pointer)(int, int);    // 指向函数的指针
function_pointer = add;               // 赋值

// ❌ 常见的错误声明
int *function_pointer(int, int);      // 这是函数声明,返回 int*

// ✅ 使用 typedef 更清晰
typedef int (*MathOp)(int, int);
MathOp operation = add;

5.2 安全使用指南

#include <stdio.h>

int add(int a, int b) { return a + b; }

// 安全的函数指针调用
void safe_function_call(int (*func)(int, int), int a, int b) {
    if (func == NULL) {
        printf("错误: 函数指针为空\n");
        return;
    }
    
    int result = func(a, b);
    printf("安全调用结果: %d\n", result);
}

// 带错误处理的函数指针数组
typedef void (*CommandFunction)();

void validate_and_execute(CommandFunction commands[], int count) {
    for (int i = 0; i < count; i++) {
        if (commands[i] != NULL) {
            printf("执行命令 %d: ", i + 1);
            commands[i]();
        } else {
            printf("命令 %d: <空函数>\n", i + 1);
        }
    }
}

int main() {
    // 安全调用演示
    safe_function_call(add, 5, 3);      // 正常调用
    safe_function_call(NULL, 5, 3);     // 安全处理空指针
    
    // 函数指针数组安全使用
    CommandFunction commands[] = {add, NULL, subtract};
    int command_count = sizeof(commands) / sizeof(commands[0]);
    validate_and_execute(commands, command_count);
    
    return 0;
}

六、总结

6.1 核心要点回顾

  1. 变量作用域

    • 局部变量:代码块内有效
    • 全局变量:整个程序有效
    • 优先使用局部变量
  2. 返回指针的函数

    • 确保返回的指针指向有效内存
    • 避免返回局部变量的地址
    • 动态内存需要调用者释放
  3. 函数指针

    • 实现回调机制和策略模式
    • 使用 typedef 提高可读性
    • 总是检查函数指针是否为空

6.2 最佳实践

  • 作用域管理:最小化变量作用范围
  • 内存安全:谨慎处理返回的指针
  • 代码设计:利用函数指针实现灵活架构
  • 错误处理:验证函数指针的有效性

掌握这些高级函数特性,将显著提升你的 C 语言编程能力和代码设计水平!

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容