_Generic關鍵字及其語法和應用(C11標準),C語言_Generic詳解

2020-07-16 10:04:24
對接觸過物件導向程式設計的程式設計師來講,相信各位對泛型程式設計並不陌生。在 C11 標準中,_Generic 關鍵字可以讓 C 語言也如同 C++ 等物件導向程式設計語言一樣,使其支援輕量級的泛型程式設計設計。

利用 _Generic 關鍵字,可以簡單地將一組具有不同型別卻有相同功能的函數抽象為一個統一的介面,語法形式如下:

generic-selection:
    _Generic (assignment-expression, generic-assoc-list)
generic-assoc-list:
    generic-association
    generic-assoc-list , generic-association
generic-association:
    type-name : assignment-expression
    default : assignment-expression

與 sizeof 與 typeof 類似,_Generic 中的 assignment-expression 只用於在編譯時獲得該表示式的型別,而不會對該表示式做執行時計算,如以下程式碼所示。
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#define getTypeName(x) _Generic((x), _Bool:"_Bool",
    char: "char", 
    signed char: "signed char", 
    unsigned char: "unsigned char", 
    short int: "short int", 
    unsigned short int: "unsigned short int", 
    int: "int", 
    unsigned int: "unsigned int", 
    long int: "long int", 
    unsigned long int: "unsigned long int", 
    long long int: "long long int", 
    unsigned long long int: "unsigned long long int", 
    float: "float", 
    double: "double", 
    long double: "long double", 
    char *: "pointer to char", 
    void *: "pointer to void", 
    int *: "pointer to int")
int main(void)
{
    char c = 'a';
    size_t s;
    ptrdiff_t p;
    intmax_t i;
    int arr[3] = { 0 };
    printf("s is '%s'n", getTypeName(s));
    printf("p is '%s'n", getTypeName(p));
    printf("i is '%s'n", getTypeName(i));
    printf("c is '%s'n", getTypeName(c));
    printf("arr is '%s'n", getTypeName(arr));
    printf("0x7FFFFFFF is '%s'n", getTypeName(0x7FFFFFFF));
    printf("0xFFFFFFFF is '%s'n", getTypeName(0xFFFFFFFF));
    printf("0x7FFFFFFFU is '%s'n", getTypeName(0x7FFFFFFFU));
}
執行結果為:
s is 'unsigned int'
p is 'int'
i is 'long long int'
c is 'char'
arr is 'pointer to int'
0x7FFFFFFF is 'int'
0xFFFFFFFF is 'unsigned int'
0x7FFFFFFFU is 'unsigned int'

除此之外,還必須保證 generic-association-list 中有與 assignment-expression 型別相同的 generic-association 與之對應,否則編譯就會報錯。例如,在上面程式碼的main函數中新增如下兩行程式碼:
float *fp=NULL;
printf("fp is '%s'n", getTypeName(fp));
很顯然,generic-assoc-list 中沒有 generic-association 與 fp 相匹配的型別,從而導致編譯出錯,如圖 1 所示。


圖 1 型別不匹配