一、變量作用域與生命周期
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 核心要點回顧
-
變量作用域:
- 局部變量:代碼塊內(nèi)有效
- 全局變量:整個程序有效
- 優(yōu)先使用局部變量
-
返回指針的函數(shù):
- 確保返回的指針指向有效內(nèi)存
- 避免返回局部變量的地址
- 動態(tài)內(nèi)存需要調(diào)用者釋放
-
函數(shù)指針:
- 實現(xiàn)回調(diào)機制和策略模式
- 使用 typedef 提高可讀性
- 總是檢查函數(shù)指針是否為空
6.2 最佳實踐
- 作用域管理:最小化變量作用范圍
- 內(nèi)存安全:謹慎處理返回的指針
- 代碼設計:利用函數(shù)指針實現(xiàn)靈活架構(gòu)
- 錯誤處理:驗證函數(shù)指針的有效性
掌握這些高級函數(shù)特性,將顯著提升你的 C 語言編程能力和代碼設計水平!