巢狀函數定義在另一個函數內部,可存取外層函數的區域變數(詞法作用域)。這是函數式語言的核心特性(閉包),標準 C 不支援,但 GCC 作為擴展提供支援。
double foo(double a, double b) {
// 巢狀函數:可存取外層參數 a、b
double square(double z) { return z * z; }
return square(a) + square(b);
}
void bar(int *array, int offset, int size) {
// 內部函數捕獲 offset
int access(int *arr, int idx) {
return arr[idx + offset];
}
for (int i = 0; i < size; i++) {
// 使用 access(array, i)
}
}
__label__)
void bar(int *array, int offset, int size) {
__label__ failure; // 宣告區域標籤
int access(int *arr, int idx) {
if (idx > size) goto failure; // 跳轉到外層標籤
return arr[idx + offset];
}
for (int i = 0; i < size; i++) {
// ...
}
return 0;
failure:
return -1;
}
GCC 會在執行期於堆疊生成一小段機器碼,將外層幀指標綁定到巢狀函數入口,形成可攜帶環境的函數指標。
int (*make_adder(int x))(int) {
int adder(int y) { return x + y; }
return adder; // 返回攜帶 x 環境的函數指標
}
// 使用:
int (*add5)(int) = make_adder(5);
printf("%d\n", add5(3)); // 8
⚠️ 極度危險: 返回的函數指標綁定的是堆疊幀位址。外層函數返回後,堆疊幀失效,呼叫該指標屬於未定義行為(Use-after-scope)。僅在外層函數生命週期內使用才安全。
-z execstack),現代系統預設關閉(NX bit),可能導致 SIGSEGV。| 需求 | 標準 C 方案 |
|---|---|
| 狀態封裝 | 結構體 + 函數指標(物件導向模擬) |
| 回調函數 | void (*cb)(void *ctx) + void *context |
| 閉包語意 | 手動建立環境結構體,傳遞給函數指標 |