C的動態型別檢查

fango發表於2013-07-07

C的動態型別檢查

這次美國之行的一大收穫就是終於正視了Objective-C而不再只是當它是不順手的工具了。這主要是有機會拜讀了Brad Cox的《Objective-Oriented Programming, an evolutionary approach》。今天就談談它第91頁提到的那個極少C程式設計師甚至C編譯器作者知道的一個極為餛飩的語法:

struct MYSTRUCT someVariables;
someVariable = (*((struct MYSTRUCT *)(*)() _msg))(someObject, …);

C語言是靜態型別檢查的編譯語言.例如下面程式定義和使用了兩個型別:浮點和定點。

#include <stdio.h>

typedef struct {
    float x, y;
} FloatingPoint;

typedef struct {
    int x, y;
    int scale;
} FixedPoint;

int main() {
    FloatingPoint flo;
    FixedPoint fix;
    printf("(%f,%f)\n", flo.x, flo.y);
    printf("(%f, %f)\n", 1.0*fix.x/fix.scale, 1.0*fix.y/fix.scale);
}

不同型別間的轉換需要cast,否則編譯時會被警告。後門還是有的,就是void *型別。我們宣告id是此void *型別,加入一個使用它的迷你函式:

typedef void *id;

id thePoint(id thePoint) {
    return thePoint;
}

這樣在main裡我們可以使用明確的型別轉換,不會被警告:

id flid = thePoint(&flo);
printf("(%f,%f)\n", ((FloatingPoint *)flid)->x, ((FloatingPoint *)flid)->y);

但型別錯了也沒了警示報告:

FloatingPoint *flip = thePoint(&fix);
printf("(%f,%f)\n", flip->x, flip->y);

我們試著改寫為間接的函式指標型別,當然也沒有編譯預警:

id (*thePointPointer)(id);
thePointPointer = &thePoint;

flip = thePointPointer(&fix);
printf("(%f,%f)\n", flip->x, flip->y);

但如果我們加入明確型別,就會看到預期的警示,雖然是有些囉嗦:

FloatingPoint *(*theFloatingPointPointer)(FloatingPoint *);
theFloatingPointPointer = &thePoint;

flip = theFloatingPointPointer(&fix);
printf("(%f,%f)\n", flip->x, flip->y);

但這很容易解決,寫在一行就行了:

FloatingPoint *flop = ((FloatingPoint *(*)(FloatingPoint *))&thePoint)(&fix);

我想這就是書中提到的意思。Objective-C是超級的C語言,這些餛飩都被精心的包裝成了簡潔的語法。帶型別檢查的動態語言 —— 我喜歡。

相關文章