今天下了Apple最新的XCode4.6,里面包含了最新的Apple LLVM4.2編譯器,其中令人十分欣喜的是C語言編譯器語言選項中多了C11以及gnu11。
之前在Apple LLVM4.0開始就已經支持了C11標准中一個比較重要的語法特性——泛型機制(詳細可參考這篇博文:
C11標准的泛型機制
)。而現在,既然已經徹底推出了GNU11規范,那么我們來簡單地探究一下,此次Apple LLVM4.2支持了哪些C11標准中的語法特性。
我這里列出了5大語法特性:
1、數據對齊(Alignment)
2、泛型表達式(Type-generic expressions)
3、原子數據類型(Atomics)
4、靜態斷言:(static assertion)
5、匿名結構與聯合(Anonymous structures and unions)——其實這個語法特性在GNU擴展語法中已經被GCC編譯器所支持。
下面提供一些樣例代碼來介紹這些特性以及如何使用這些特性:
// // main.c // CTest // // Created by zenny_chen on 12-12-1. // Copyright (c) 2012年 zenny_chen. All rights reserved. // #include <stdio.h> #include <stdalign.h> #include <stddef.h> // no max_align_t #include <string.h> //#include <threads.h> not defined //#include <uchar.h> not defined //#include <stdatomic.h> not defined #define Zenny_IsEqual4BasicTypes(a, b) _Generic((a), \ const char*: strcmp(a, b) == 0, \ char*: strcmp(a, b) == 0, \ const char[sizeof(a)]: strcmp(a, b) == 0, \ char[sizeof(a)]: strcmp(a, b) == 0, \ default: (a) == (b)) static volatile _Atomic(int) a = 100; int main(int argc, const char * argv[]) { // 1. Alignment char _Alignas(long double) buf1[30] = { [0] = 10, [2] = -128, [29] = 127 }; char _Alignas(32) buf2[7] = { [0] = 10, [2] = -128 }; printf("The address of buf1 is: 0x%.16lX\n", (size_t)buf1); // 16-byte aligned printf("The address of buf1 is: 0x%.16lX\n", (size_t)buf2); // 32-byte aligned // 在C++的<cstddef>中定義了max_align_t為long double,但在C的<stddef.h>頭文件中沒有定義 printf("The max align size is: %lu\n", _Alignof(long double)); // 2. Type-generic expressions printf("1 == 2? %d\n", Zenny_IsEqual4BasicTypes(1, 2)); const char words[] = "Hi"; printf("Hi == words? %d\n", Zenny_IsEqual4BasicTypes("Hi", words)); // Use char[3] printf("words == Hi? %d\n", Zenny_IsEqual4BasicTypes(words, "Hi")); // Use const char[3] // 3. Atomics _Atomic(int) volatile *p = &a; ++(*p); printf("The value is: %d\n", a); // 4. _Static_assert: _Static_assert會在編譯時做斷言處理,因此第一個參數必須是常量表達式 _Static_assert(sizeof(void) == 1, "sizeof(void) !=1 and that is not expected!"); // 5. Anonymous structures and unions struct T { int tag; union { int i; float f; }; }; struct T t = {.tag = 100, .f = -100.25f}; printf("The improper value is: %d\n", t.tag + t.i); printf("The suitable value is: %f\n", t.tag + t.f); return 0; }
在Apple LLVM4.2中沒被實現的C11標准有:
1、與<threads.h>相關的特性,包括不支持_Thread_local關鍵字。
2、與<uchar.h>相關的特性,包括不支持char16_t以及char32_t。
3、不支持_Noreturn函數指示符。
我同時也在Ubuntu下用了GCC4.7.2。該編譯器支持了<uchar.h>以及_Noreturn,但是對_Generic尚未支持。
而在最新的LLVM Clang中看到了Clang C編譯器又增加了一個非常有趣的特性——函數重載。這個重載方式與限制跟C++中的一樣。只需通過__attribute__((overloadable))來指定即可。下面看一下示例代碼:
static void __attribute__((overloadable)) MyFunc(float x) { puts("This is a float function"); } static int __attribute__((overloadable)) MyFunc(int x) { puts("This is an integer function"); return x; } int main(void) { MyFunc(1.0f); MyFunc(1); }
關於LLVM Clang更多信息可以參考此鏈接:http://clang.llvm.org/docs/index.html
這里會放最新LLVM Clang版本的手冊以及各類文檔。
而到了Apple LLVM 5.0之后,對C11標准的UTF-8、UTF-16以及UTF32編碼格式都有了非常好的支持提升。C11標准中,字符串前綴u8表示以UTF-8編碼的字符串,u前綴表示UTF-16編碼的字符串,U前綴表示UTF-32編碼的字符串。其中,UTF-8編碼是變長編碼格式,一個字符所占字節數從1到6字節不等。而UTF-16是定長編碼,一個字符總是2個字節。UTF-32也是定長編碼,一個字符總是4個字節。比如以下代碼:
const char *utf8Str = u8"哈羅"; unichar utf16c = u'你'; unsigned utf32c = U'好'; const unichar *utf16Str = u",世界!"; printf("UTF-16 char size is: %lu\n", sizeof(u'你')); // 2個字節 printf("UTF-32 char size is: %lu\n", sizeof(U'好')); // 4個字節 NSString *str = [NSString stringWithCString:utf8Str encoding:NSUTF8StringEncoding]; NSLog(@"str = %@", str); str = [NSString stringWithFormat:@"%C%C%S", utf16c, (unichar)utf32c, utf16Str]; NSLog(@"str = %@", str);
這里需要注意,u8前綴只能跟字符串字面量,而不能跟字符字面量。而u以及U既可以跟字符字面量也可以跟字符串字面量。
不過,在Apple LLVM5.0以及Apple LLVM 5.1中,尚未引入<uchar.h>標准庫。但是它已經定義了unichar這個類型,它實際上是unsigned short類型,正好能對上UTF-16字符。而對於UTF-32編碼,由於不管是iOS還是OS X都沒有開始很好支持,因此僅僅是語法上支持而已。這里用unsigned來存UTF-32編碼字符。