C語言學習5-函數(shù)

一、變量作用域與生命周期

1.1 局部變量作用域規(guī)則

基本規(guī)則

  • 生命周期開始:變量定義處開始生效
  • 生命周期結(jié)束:所在代碼塊 } 結(jié)束時失效

代碼示例

#include <stdio.h>

void demo_local_scope() {
    int outer = 100;  // outer 生效
    
    if (1) {
        int inner = 200;  // inner 生效
        printf("內(nèi)部塊: 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;  // 內(nèi)層 value(覆蓋外層)
        printf("內(nèi)層 value = %d\n", value);  // 輸出: 20
        
        {
            int value = 30;  // 更內(nèi)層 value
            printf("更內(nèi)層 value = %d\n", value);  // 輸出: 30
        }  // 30 失效,20 恢復可見
        
        printf("恢復內(nèi)層 value = %d\n", value);  // 輸出: 20
    }  // 20 失效,10 恢復可見
    
    printf("恢復外層 value = %d\n", value);  // 輸出: 10
}

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

1.2 全局變量

全局變量特性

  • 定義在所有函數(shù)之外
  • 生命周期貫穿整個程序
  • 任何函數(shù)都可以訪問和修改

全局變量示例

#include <stdio.h>

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

void increment_counter() {
    global_counter++;
    printf("[increment] 計數(shù)器: %d\n", global_counter);
}

void reset_counter() {
    global_counter = 0;
    printf("[reset] 計數(shù)器已重置\n");
}

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

int main() {
    printf("程序啟動: %s\n", PROGRAM_NAME);
    
    show_program_info();  // 輸出: 程序: MyApp, 計數(shù)器: 0
    
    global_counter = 5;
    increment_counter();  // 輸出: [increment] 計數(shù)器: 6
    increment_counter();  // 輸出: [increment] 計數(shù)器: 7
    
    reset_counter();      // 輸出: [reset] 計數(shù)器已重置
    show_program_info();  // 輸出: [info] 程序: MyApp, 計數(shù)器: 0
    
    return 0;
}

1.3 變量使用指南

選擇原則

特性 局部變量 全局變量
作用域 限于代碼塊內(nèi) 整個程序
生命周期 代碼塊執(zhí)行期間 程序運行期間
內(nèi)存管理 自動分配釋放 始終存在
數(shù)據(jù)共享 困難 容易
代碼維護 容易 困難

最佳實踐

// ? 推薦:優(yōu)先使用局部變量
void process_data() {
    int temp_result = calculate();  // 局部變量
    // 使用完后自動釋放
}

// ? 必要時使用全局變量(如配置、狀態(tài))
int system_initialized = 0;        // 系統(tǒng)狀態(tài)
const double PI = 3.14159;         // 常量配置

// ? 避免:濫用全局變量
// int temp_calculation_result;    // 不必要的全局變量

二、返回指針的函數(shù)

2.1 字符串處理函數(shù)示例

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

// 將字符串轉(zhuǎn)換為大寫,返回原字符串指針
char* to_uppercase(char *str) {
    char *original = str;  // 保存原始地址
    
    while (*str != '\0') {
        if (islower(*str)) {  // 使用標準庫函數(shù)判斷小寫
            *str = toupper(*str);  // 轉(zhuǎn)換為大寫
        }
        str++;
    }
    
    return original;
}

// 安全的字符串反轉(zhuǎn)函數(shù)
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() {
    // ? 正確:字符數(shù)組(可修改)
    char text1[] = "hello world";
    printf("原始: %s\n", text1);
    printf("大寫: %s\n", to_uppercase(text1));
    
    char text2[] = "abcdef";
    printf("反轉(zhuǎn): %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;  // 錯誤!返回后將失效
}

// ? 安全:返回動態(tài)分配的內(nèi)存
char* safe_dynamic_function() {
    char *buffer = malloc(100 * sizeof(char));
    if (buffer != NULL) {
        strcpy(buffer, "dynamic data");
    }
    return buffer;  // 調(diào)用者負責釋放
}

// ? 安全:返回靜態(tài)變量的地址
char* safe_static_function() {
    static char static_buffer[100] = "static data";
    return static_buffer;  // 靜態(tài)變量生命周期長
}

// ? 安全:返回輸入?yún)?shù)的指針
char* process_and_return(char *input) {
    // 處理輸入數(shù)據(jù)...
    return input;  // 返回原始指針
}

void demo_safe_pointers() {
    // 使用動態(tài)內(nèi)存
    char *dynamic_result = safe_dynamic_function();
    if (dynamic_result != NULL) {
        printf("動態(tài): %s\n", dynamic_result);
        free(dynamic_result);  // 必須釋放!
    }
    
    // 使用靜態(tài)變量
    char *static_result = safe_static_function();
    printf("靜態(tài): %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;
}

三、函數(shù)指針

3.1 函數(shù)指針基礎

為什么需要函數(shù)指針?

  • 函數(shù)在內(nèi)存中有確定的入口地址
  • 函數(shù)名就是該地址的符號表示
  • 指針可以存儲任何內(nèi)存地址,包括函數(shù)地址

3.2 函數(shù)指針的定義與使用

#include <stdio.h>

// 基礎數(shù)學運算函數(shù)
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() {
    // 函數(shù)指針的多種定義方式
    int (*func_ptr1)(int, int) = add;           // 推薦:明確參數(shù)
    int (*func_ptr2)(int, int) = &subtract;     // 使用取地址符
    int (*func_ptr3)() = multiply;              // 省略參數(shù)(不推薦)
    
    // 調(diào)用方式的等價性
    int result1 = func_ptr1(10, 5);            // 直接調(diào)用
    int result2 = (*func_ptr2)(10, 5);         // 解引用調(diào)用
    int result3 = func_ptr3(10, 5);            // 省略參數(shù)調(diào)用
    
    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 函數(shù)指針作為參數(shù)(回調(diào)函數(shù))

#include <stdio.h>

// 數(shù)學運算函數(shù)
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; }

// 通用的計算函數(shù) - 接受函數(shù)指針作為參數(shù)
void calculate_with_callback(const char *operation_name,
                           int (*math_operation)(int, int),
                           int x, int y) {
    if (math_operation == NULL) {
        printf("錯誤: 無效的操作函數(shù)\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("計算結(jié)果: %d\n", result);
    }
}

void demo_callback_functions() {
    int a = 12, b = 4;
    
    printf("=== 回調(diào)函數(shù)演示 ===\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 函數(shù)指針數(shù)組

#include <stdio.h>

// 系統(tǒng)操作函數(shù)
void startup() { printf("?? 系統(tǒng)啟動中...\n"); }
void shutdown() { printf("?? 系統(tǒng)關(guān)閉中...\n"); }
void diagnostics() { printf("?? 運行診斷檢查...\n"); }
void maintenance() { printf("???  執(zhí)行維護任務...\n"); }

void demo_function_pointer_array() {
    // 函數(shù)指針數(shù)組
    void (*system_commands[])() = {
        startup,
        diagnostics, 
        maintenance,
        shutdown
    };
    
    const char *command_names[] = {
        "啟動系統(tǒng)",
        "運行診斷", 
        "執(zhí)行維護",
        "關(guān)閉系統(tǒng)"
    };
    
    int command_count = sizeof(system_commands) / sizeof(system_commands[0]);
    
    printf("=== 系統(tǒng)命令菜單 ===\n");
    for (int i = 0; i < command_count; i++) {
        printf("%d. %s\n", i + 1, command_names[i]);
    }
    
    printf("\n=== 執(zhí)行命令序列 ===\n");
    for (int i = 0; i < command_count; i++) {
        printf("執(zhí)行: %s\n", command_names[i]);
        system_commands[i]();  // 通過函數(shù)指針調(diào)用
        printf("---\n");
    }
}

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

四、高級函數(shù)指針用法

4.1 使用 typedef 簡化函數(shù)指針

#include <stdio.h>

// 定義函數(shù)指針類型
typedef int (*MathFunction)(int, int);
typedef void (*Formatter)(const char*, int);

// 數(shù)學函數(shù)
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }

// 格式化函數(shù)
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);
}

// 使用類型別名的函數(shù)
int calculate_with_format(int a, int b, 
                         MathFunction operation,
                         Formatter formatter) {
    int result = operation(a, b);
    formatter("計算結(jié)果", result);
    return result;
}

void demo_typedef_functions() {
    printf("=== 使用 typedef 的函數(shù)指針 ===\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("直接調(diào)用", 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 返回負數(shù)
}

int descending_order(int a, int b) {
    return b - a;  // 降序:a > b 返回負數(shù)
}

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;  // 按絕對值排序
}

// 通用的排序函數(shù)(模擬)
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");
    
    // 這里可以實現(xià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("原始數(shù)組: ");
    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 函數(shù)指針聲明語法

// ? 正確的函數(shù)指針聲明
int (*function_pointer)(int, int);    // 指向函數(shù)的指針
function_pointer = add;               // 賦值

// ? 常見的錯誤聲明
int *function_pointer(int, int);      // 這是函數(shù)聲明,返回 int*

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

5.2 安全使用指南

#include <stdio.h>

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

// 安全的函數(shù)指針調(diào)用
void safe_function_call(int (*func)(int, int), int a, int b) {
    if (func == NULL) {
        printf("錯誤: 函數(shù)指針為空\n");
        return;
    }
    
    int result = func(a, b);
    printf("安全調(diào)用結(jié)果: %d\n", result);
}

// 帶錯誤處理的函數(shù)指針數(shù)組
typedef void (*CommandFunction)();

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

int main() {
    // 安全調(diào)用演示
    safe_function_call(add, 5, 3);      // 正常調(diào)用
    safe_function_call(NULL, 5, 3);     // 安全處理空指針
    
    // 函數(shù)指針數(shù)組安全使用
    CommandFunction commands[] = {add, NULL, subtract};
    int command_count = sizeof(commands) / sizeof(commands[0]);
    validate_and_execute(commands, command_count);
    
    return 0;
}

六、總結(jié)

6.1 核心要點回顧

  1. 變量作用域

    • 局部變量:代碼塊內(nèi)有效
    • 全局變量:整個程序有效
    • 優(yōu)先使用局部變量
  2. 返回指針的函數(shù)

    • 確保返回的指針指向有效內(nèi)存
    • 避免返回局部變量的地址
    • 動態(tài)內(nèi)存需要調(diào)用者釋放
  3. 函數(shù)指針

    • 實現(xiàn)回調(diào)機制和策略模式
    • 使用 typedef 提高可讀性
    • 總是檢查函數(shù)指針是否為空

6.2 最佳實踐

  • 作用域管理:最小化變量作用范圍
  • 內(nèi)存安全:謹慎處理返回的指針
  • 代碼設計:利用函數(shù)指針實現(xiàn)靈活架構(gòu)
  • 錯誤處理:驗證函數(shù)指針的有效性

掌握這些高級函數(shù)特性,將顯著提升你的 C 語言編程能力和代碼設計水平!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容