一、longjmp ()/setjmp () 函數詳解
1. 核心概念
- 非局部跳轉:不同於
goto(僅能在當前函數內跳轉),longjmp()可跳轉到之前通過setjmp()標記的位置,即使跨多個函數調用層級。 - jmp_buf 緩衝區:存儲程序執行上下文(寄存器、棧指針等),作為跳轉的 “錨點”。
2. 函數原型
c
運行
#include <setjmp.h>
// 標記跳轉點,返回值:
// - 首次調用:返回 0;
// - 被 longjmp() 觸發跳轉時:返回 longjmp() 的第二個參數(非 0)
int setjmp(jmp_buf env);
// 跳轉到 setjmp() 標記的位置,恢復 env 存儲的上下文
// val:傳遞給 setjmp() 的返回值(必須非 0,若傳 0,setjmp() 實際返回 1)
void longjmp(jmp_buf env, int val);
3. 工作原理
4. 示例代碼(錯誤處理場景)
c
運行
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
jmp_buf jump_env; // 全局/靜態緩衝區(需保證跳轉時有效)
void risky_operation(int code) {
if (code < 0) {
// 發生錯誤,跳回 setjmp 標記的位置,返回值 1
longjmp(jump_env, 1);
} else if (code > 100) {
// 另一種錯誤,返回值 2
longjmp(jump_env, 2);
}
printf("Operation success! Code: %d\n", code);
}
int main() {
// 標記跳轉點,首次返回 0
int ret = setjmp(jump_env);
switch (ret) {
case 0: // 首次執行,正常邏輯
printf("Start operation...\n");
risky_operation(-5); // 觸發錯誤1
// risky_operation(101); // 觸發錯誤2
// risky_operation(50); // 正常執行
break;
case 1: // 錯誤1處理
printf("Error: code is negative\n");
exit(1);
case 2: // 錯誤2處理
printf("Error: code exceeds 100\n");
exit(1);
default:
printf("Unknown error\n");
exit(1);
}
return 0;
}
plaintext
Start operation...
Error: code is negative
5. 關鍵注意事項
- 緩衝區有效性:
jmp_buf需保證在longjmp()調用時未被銷燬(如全局 / 靜態變量,或棧上變量但跳轉後棧未被覆蓋),否則行為未定義; - 禁止跳轉的場景:
- 不能從信號處理函數中跳轉到非信號處理的代碼(可能導致資源泄漏);
- 不能跳轉到已返回的函數(棧幀已釋放,訪問無效內存);
- 資源泄漏風險:跳轉時會跳過局部變量的析構(C++)、
free()/close()等操作,需手動清理資源; - C++ 兼容性:C++ 中不推薦使用(建議用異常
try/catch),因為longjmp()不會調用對象的析構函數,可能導致內存泄漏或對象狀態異常。
6. 適用場景
- 嵌入式系統 / 內核態代碼(無異常機制時的錯誤恢復);
- 簡化多層嵌套函數的錯誤返回(避免逐層返回錯誤碼);
- 實現協程 / 上下文切換(基礎原理)。
二、qsort () 函數詳解
1. 核心特點
- 通用性:支持 int、char、結構體等任意類型數組;
- 原地排序:無需額外內存(快速排序的特性);
- 不穩定排序:相等元素的相對位置可能改變;
- 回調驅動:比較邏輯由用户自定義的函數決定。
2. 函數原型
c
運行
#include <stdlib.h>
// 對數組進行快速排序
// base:指向待排序數組的首地址
// nitems:數組元素個數
// size:單個元素的字節大小(如 int 是 4,char 是 1)
// compar:比較函數指針,定義元素的排序規則
void qsort(void *base, size_t nitems, size_t size,
int (*compar)(const void *, const void *));
3. 比較函數規則
- 入參:兩個待比較元素的指針(
const void*需強制轉換為具體類型); - 返回值:
< 0:第一個元素 < 第二個元素(升序時放前面);= 0:兩個元素相等;> 0:第一個元素 > 第二個元素(升序時放後面)。
4. 示例代碼
示例 1:排序 int 數組(升序 / 降序)
c
運行
#include <stdio.h>
#include <stdlib.h>
// 升序比較函數:int 類型
int compare_int_asc(const void *a, const void *b) {
// 轉換為 int* 並取值
int val1 = *(const int*)a;
int val2 = *(const int*)b;
// 簡化寫法:return val1 - val2;(注意溢出風險)
if (val1 < val2) return -1;
if (val1 > val2) return 1;
return 0;
}
// 降序比較函數:int 類型
int compare_int_desc(const void *a, const void *b) {
return compare_int_asc(b, a); // 交換參數即可
}
int main() {
int arr[] = {5, 2, 9, 1, 5, 6};
size_t n = sizeof(arr) / sizeof(arr[0]);
// 升序排序
qsort(arr, n, sizeof(int), compare_int_asc);
printf("Ascending: ");
for (size_t i = 0; i < n; i++) printf("%d ", arr[i]);
printf("\n");
// 降序排序
qsort(arr, n, sizeof(int), compare_int_desc);
printf("Descending: ");
for (size_t i = 0; i < n; i++) printf("%d ", arr[i]);
printf("\n");
return 0;
}
plaintext
Ascending: 1 2 5 5 6 9
Descending: 9 6 5 5 2 1
示例 2:排序結構體數組(按字符串 / 數值)
c
運行
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定義結構體
typedef struct {
char name[20];
int score;
} Student;
// 按分數升序比較
int compare_student_score(const void *a, const void *b) {
const Student *s1 = (const Student*)a;
const Student *s2 = (const Student*)b;
return s1->score - s2->score;
}
// 按名字字典序比較
int compare_student_name(const void *a, const void *b) {
const Student *s1 = (const Student*)a;
const Student *s2 = (const Student*)b;
// strcmp 返回值符合 qsort 比較規則
return strcmp(s1->name, s2->name);
}
int main() {
Student students[] = {
{"Alice", 85},
{"Bob", 92},
{"Charlie", 78},
{"David", 92}
};
size_t n = sizeof(students) / sizeof(students[0]);
// 按分數排序
qsort(students, n, sizeof(Student), compare_student_score);
printf("Sort by score:\n");
for (size_t i = 0; i < n; i++) {
printf("%s: %d\n", students[i].name, students[i].score);
}
// 按名字排序
qsort(students, n, sizeof(Student), compare_student_name);
printf("\nSort by name:\n");
for (size_t i = 0; i < n; i++) {
printf("%s: %d\n", students[i].name, students[i].score);
}
return 0;
}
plaintext
Sort by score:
Charlie: 78
Alice: 85
Bob: 92
David: 92
Sort by name:
Alice: 85
Bob: 92
Charlie: 78
David: 92
5. 關鍵注意事項
- 類型轉換:
void*必須強制轉換為對應類型的指針,否則會導致內存訪問錯誤; - 溢出風險:比較 int 時,
return a - b在a很大、b很小(如a=INT_MAX, b=-1)時會溢出,建議用if-else或(a > b) - (a < b); - 穩定性:
qsort()是快速排序實現,不穩定(相等元素可能交換位置),若需穩定排序需手動實現(如歸併排序); - 性能:平均時間複雜度 O (n log n),最壞 O (n²)(但標準庫通常做了優化,如結合插入排序);
- 空指針檢查:若
base為 NULL 或nitems=0,qsort()無操作(行為安全)。
6. 適用場景
- 快速實現任意類型數組的排序(無需手動編寫排序算法);
- 批量數據處理(如日誌排序、統計數據排序);
- 配合
bsearch()(二分查找函數)使用,先排序後查找,效率更高。
三、總結
|
函數 / 特性
|
核心用途
|
關鍵注意事項
|
|
setjmp/longjmp
|
非局部跳轉、錯誤恢復
|
避免資源泄漏,C++ 中優先用異常
|
|
qsort
|
通用數組排序
|
正確編寫比較函數,注意類型轉換和溢出
|