C語言如何支持C?
這個問題描述其實有點問題,因為C++重載有兩種場景:
函數重載運算符重載問題本身沒有指明到底是哪種場景。現在就兩個場景分別給出答案。
如何用C語言實現C++函數重載?根據筆者的經驗,共有3種方法可以實現:
用C語言實現一個C++編譯器的對應子集, 后者自然可以支持重載;用函數指針加上void指針類型參數強制類型轉換,可以實現函數重載;用宏加上可變參數,可以實現函數重載如何用C語言實現C++運算符重載?運算符在C語言中是保留字, 無法通過普通變通方法實現重載。只能用C語言實現一個C++編譯器的對應子集, 后者自然可以支持重載。
用函數指針加上void指針類型參數強制類型轉換,實現函數重載用一個例子來說明:
typedef void (*funcOverride)(void *param);
void runFuncOverride(funcOverride f, void *param) {
f(param);
}
void func_with_int_param(void *iParam) {
int i = *(int *)iParam;
printf("int_param function is called, param is %d\n", i);
}
void func_with_char_param(void *cParam) {
char c = *(char *)cParam;
printf("char_param function is called, param is %c\n", c);
}
int i = 1;
char c='2';
runFuncOverride(func_with_int_param, &i);
runFuncOverride(func_with_char_param, &c);
輸出結果為:
bint_param function is called, param is 1
char_param function is called, param is 2
這種方法有一個明顯的劣勢:
需要調用方事先指定函數指針掛接的實際調用的函數實體,即便是用變通的方式——將類型信息通過枚舉類型或者字符串類型作為參數傳遞,也無法完美消除這個劣勢。
根因是:運行時類型判斷并未收納于C語言標準規范中。
當然,一些C語言編譯器可能會提供內置函數來實現該特性,但畢竟不是標準,無法滿足跨平臺的需求。比方說gcc就提供了__builtin_types_compatible_p和typeof這兩個內置函數來做運行時類型判斷。
用宏加上可變參數,實現函數重載C語言支持可變參數,比方說prinf函數的原型如下:
int printf(const char *format, ...);
省略號表示參數為可變參數,而且C語言規定:省略號只能出現在函數形參的末尾,而且左邊必須有普通的形參。
需要注意的是:對于宏沒有上述限制。
C語言定義了一系列宏來完成可變參數函數參數的讀取和使用:宏va_start、va_arg和va_end。
在ANSI C標準下,這些宏定義在stdarg.h中:
void va_start(va_list ap, last);//取第一個可變參數;
type va_arg(va_list ap, type);//獲取當前位置參數值
void va_end(va_list ap);//將ap置為NULL
除此之外,還提供了一個非常有用的宏:__VA_ARGS__
這個宏直接引用可變參數列表。
有了上述前置知識,下面用一個例子來說明如何實現函數重載:
#define OneArgument(a) printf("One Argument func is called!\n")
#define TwoArguments(a, b) printf("Two Arguments func is called!\n")
#define MacroKernel(_1, _2, FUNC, ...) FUNC
#define Macro(...) MacroKernel(__VA_ARGS__, TwoArguments, OneArgument, ...)(__VA_ARGS__)
Macro(1);
Macro(2,3);
輸出結果為:
One Argument func is called!
Two Arguments func is called!
上述代碼估計有些小伙伴看了有點暈,現在做要點說明:
Macro宏定義展開其實是:調用MacroKernel宏展開的函數實體,調用參數為可變參數,由Macro宏括號中的內容定義。__VA_ARGS__其實引用的就是Macro宏定義括號中的可變參數列表。MacroKernel宏定義中的_1,_2都是占位符,沒有實際含義,作用是:保證在傳給Macro不同數目參數時,使得FUNC指向對應的函數實體:Macro參數數目為1時,FUNC指向OneArgument函數;參數數目為2時,FUNC指向TwoArguments函數。下面來看看實際的展開效果:
Macro(1)
=>MacroKernel(1, TwoArguments, OneArgument, ...)(1)
=>OneArgument(1)
Macro(2, 3)
=>MacroKernel(2, 3, TwoArguments, OneArgument, ...)(2, 3)
=>TwoArguments(2, 3)