關於不完全型別的認識

style__moon 發表於 2022-06-28

最近在使用libusb做USB驅動,編寫程式碼的時候發現了一段有趣的程式碼

typedef struct libusb_device libusb_device;

而這個結構體是沒有定義的,在我做出如下定義時編譯器會報錯: error C2079: “dev”使用未定義的 struct“CMW7300ISPTOOLDlg::OnBnClickedButton1::libusb_devic”

struct libusb_devic dev;

而定義為struct libusb_devic* dev;則不會報錯,這是為什麼呢?

網路搜尋看到說這個屬於不完全型別,typedef struct libusb_device libusb_device;這斷程式碼相當於申明瞭一個結構體libusb_device,但是沒有實際定義,類似於C語言的extern int A;在不使用申明的變數時,編譯器是不會報錯的,但是如果要使用的話,就需要先定義變數,比如要使用給A賦值2,就需要用int A = 2;直接使用A = 2編譯器就會報錯。同樣的對於不完全定義的結構體typedef struct libusb_device libusb_device;而言也是一樣的,不能直接定義struct libusb_devic dev;這樣定義的話編譯器不知道結構體的libusb_devic的大小,不知道開闢多少記憶體給變數,所以會報錯,但是如果用struct libusb_devic* dev;就沒問題了,因為這樣定義的是一個指標,對於編譯器而言,指標的大小是確定的(32位)4位元組。

這裡又出現一個新問題,這樣定義之後在使用這個結構體的時候還是需要先定義如下結構體:

struct libusb_devic

{

  若干變數;

}

那麼這個不完全型別的意義何在呢?後來發現主要是封裝時使用,當我們不希望別人訪問和修改結構體內容的時候,我們就可以將結構體的定義封裝成庫,那麼別人呼叫庫的時候只能使用不完全型別作為介面,這樣就沒法訪問結構體成員了,更不能修改結構體內容了。比如如下程式碼

ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx,
libusb_device ***list);

這就是libusb庫中的一個函式,其引數libusb_device ***list是個封裝在庫裡面的結構體,當我們要呼叫這個函式的時候,就需要先定義一個不完全變數struct libusb_device** device_list;,再呼叫函式libusb_get_device_list(NULL, &device_list);

這是我們是無法通過device_list->menber的方式訪問結構體成員的,整個結構體對於使用者來說就是一個黑匣子。