當前位置: 首頁> 最新文章列表> 在FFI::cdef 中忘記定義struct 會發生什麼?

在FFI::cdef 中忘記定義struct 會發生什麼?

gitbox 2025-05-28

PHP 的FFI(Foreign Function Interface)擴展讓我們可以直接在PHP 中調用C 語言的函數和數據結構,在處理性能敏感或者底層交互的場景非常有用。但是一旦你深入使用它,就會發現一些隱藏的“地雷”,比如——在FFI::cdef()裡聲明結構體時,如果你忘了寫struct會發生什麼?

先看一個最基本的例子

假設我們有一個簡單的C 結構體:

 typedef struct {
    int x;
    int y;
} Point;

我們用PHP 的FFI::cdef()聲明:

 $ffi = FFI::cdef("
    typedef struct {
        int x;
        int y;
    } Point;
");

然後可以這樣使用:

 $point = $ffi->new("Point");
$point->x = 10;
$point->y = 20;
echo "Point: ({$point->x}, {$point->y})\n";

一切運行正常。但現在問題來了:如果你忘了寫struct ,會發生什麼?

忘寫struct的陷阱

C 語言中, struct PointPoint是兩個不同的名字空間。除非你用了typedef ,否則Point並不是一個有效的類型名。而在PHP 的FFI::cdef()中,情況略有不同——它的解析規則和C 編譯器略有差異,容錯性低。

比如你寫了這樣一段聲明:

 $ffi = FFI::cdef("
    typedef struct Point {
        int x;
        int y;
    } Point;
");

$pt = $ffi->new("Point");

這段代碼是沒問題的。但如果你粗心把typedef部分寫漏了,比如這樣:

 $ffi = FFI::cdef("
    struct Point {
        int x;
        int y;
    };
");

$pt = $ffi->new("Point"); // 出錯

這裡$ffi->new("Point")會拋出異常,提示找不到類型“Point”。因為此時“Point”是struct Point ,而不是一個單獨的類型別名。

正確的調用應該是:

 $pt = $ffi->new("struct Point");

這就是很多人掉進去的坑:以為定義了結構體名字“Point”,其實你只定義了“struct Point”,沒做typedef 的話不能直接用Point。

如何排查這個問題?

如果你看到類似下面的錯誤:

 FFI\Exception: undefined C type 'Point'

那你首先要檢查你在cdef()中到底是如何聲明的。是不是只寫了struct Point而沒有typedef?如果是這樣,就只能用struct Point來引用。

一個調試技巧是,把你的cdef()聲明打印出來檢查結構體有沒有typedef,例如:

 echo $ffi->cdef;

雖然這個方法不總是適用(尤其在某些版本的PHP 中),但能幫助你確認類型是否正確註冊。

建議做法:一律typedef

為了避免混亂,建議你在所有結構體聲明中都帶上typedef。例如:

 typedef struct {
    int x;
    int y;
} Point;

這樣你在PHP 中就能愉快地寫$ffi->new("Point") ,不用再糾結是否該加struct前綴。

舉個更複雜的例子:結構體嵌套

假設你定義了一個結構體裡嵌套另一個結構體:

 typedef struct {
    int x;
    int y;
} Point;

typedef struct {
    Point start;
    Point end;
} Line;

PHP 中的FFI::cdef()應該這樣寫:

 $ffi = FFI::cdef("
    typedef struct {
        int x;
        int y;
    } Point;

    typedef struct {
        Point start;
        Point end;
    } Line;
");

然後你可以像這樣使用:

 $line = $ffi->new("Line");
$line->start->x = 1;
$line->start->y = 2;
$line->end->x = 10;
$line->end->y = 20;

echo "Start: ({$line->start->x}, {$line->start->y})\n";
echo "End: ({$line->end->x}, {$line->end->y})\n";

如果你在聲明中漏了typedef ,比如Point沒typedef,那麼Line的定義就會報錯,因為它找不到Point類型。

小結

在使用PHP FFI 調用C 的結構體時,以下幾點務必注意:

  1. 結構體未typedef 的話,類型名必須加struct前綴

  2. 推薦所有結構體都使用typedef聲明,避免命名混淆

  3. 調試時遇到undefined type 錯誤,先查cdef 中的聲明是否一致

通過這些規範使用方式,可以減少大量莫名其妙的類型錯誤,讓你在用PHP 玩C 接口的時候少踩坑、少掉髮。

更多關於PHP FFI 的示例可以參考https://gitbox.net/docs/php-ffi-examples