C11 标准新特性
C11標準是C語言標準的第三版(2011年由ISO/IEC發布),前一個標準版本是C99標準。相比C99,C11有哪些變化呢
1、 對齊處理
alignof(T)返回T的對齊方式,aligned_alloc()以指定字節和對齊方式分配內存,頭文件<stdalign.h>定義了這些內容。
alignof( 類型標識 )
返回 std::size_t 類型值。
返回由類型標識所指示的類型的任何實例所要求的對齊字節數,該類型可以為完整類型、數組類型或者引用類型。
若類型為引用類型,則運算符返回被引用類型的對齊;若類型為數組類型,則返回元素類型的對齊要求。
sizeof和alignof對于一般的數據類型返回值是相同的,但是對于下面情況特別:
struct Foo {int a;float b;char c; };alignof(Foo) //值為4,對齊長度 sizeof(Foo) //結構體的總大小:12void *aligned_alloc( size_t alignment, size_t size );
分配 size 字節未初始化的存儲空間,按照 alignment 指定對齊。 size 參數必須是 alignment 的整數倍。
aligned_alloc 是線程安全的:它表現得如同只訪問通過其參數可見的內存區域,而非任何靜態存儲。
令 free 或 realloc 歸還一塊內存區域的先前調用,同步于令 aligned_alloc 分配相同或部分相同的內存區域的調用。此同步出現于任何解分配函數所做的內存訪問之后,和任何通過 aligned_alloc 所做的內存訪問之前。所有操作每塊特定內存區域的分配及解分配函數擁有單獨全序。
傳遞不是 alignment 整數倍的 size ,或傳遞實現不支持的 alignment ,會令函數失敗并返回空指針(出版的 C11 指定此為未定義行為,這已經為 DR 460 所更正)
2、 _Noreturn
_Noreturn是個函數修飾符,位置在函數返回類型的前面,聲明函數無返回值,有點類似于gcc的attribute((noreturn)),后者在聲明語句尾部。
_Noreturn function_declaration
_Noreturn 關鍵詞出現于函數聲明中,指定函數不會由于執行到 return 語句或抵達函數體結尾而返回(可通過執行 longjmp 返回)。若聲明 _Noreturn 的函數返回,則行為未定義。若編譯器能檢測此錯誤,則推薦編譯器診斷。
_Noreturn 指定符可以在同一函數聲明中出現多于一次,行為與只出現一次相同。
此指定符通常通過便利宏 noreturn 使用,該宏于頭文件 stdnoreturn.h 提供。
示例:
#include <stdlib.h> #include <stdio.h> #include <stdnoreturn.h>// 在 i <= 0 時導致未定義行為 // 在 i > 0 時退出 noreturn void stop_now(int i) // 或 _Noreturn void stop_now(int i) {if (i > 0) exit(i); }int main(void) {puts("Preparing to stop...");stop_now(2);puts("This code is never executed."); }輸出:
Preparing to stop...3、 _Generic
_Generic支持輕量級范型編程,可以把一組具有不同類型而卻有相同功能的函數抽象為一個接口。
_Generic ( controlling-expression , association-list )
其中 association-list 是逗號分隔的關聯列表,每個關聯擁有語法:
type-name : expression
default : expression
_Generic. 這個關鍵字類似與switch語法 :_Generic( ‘a’, char: 1, int: 2, long: 3, default: 0) 輸出為2 (字符在C中為整型).
4、 _Static_assert()
_Static_assert(),靜態斷言,在編譯時刻進行,斷言表達式必須是在編譯時期可以計算的表達式,而普通的assert()在運行時刻斷言。
_Static_assert ( 表達式 , 消息 ) (C11 起)
表達式 - 任何整數常量表達式
消息 - 任何字符串字面量
示例:
#include <assert.h> int main(void) {// 測試數學是否正常工作static_assert(2 + 2 == 4, "Whoa dude!"); // 或 _Static_assert(...// 這會在編譯時產生錯誤。static_assert(sizeof(int) < sizeof(char),"this program requires that int is less than char"); }5、安全版本的幾個函數
gets_s()取代了gets(),原因是后者這個I/O函數的實際緩沖區大小不確定,以至于發生常見的緩沖區溢出攻擊,類似的函數還有其它的。
char *gets( char *str );
從 stdin 讀入 str 所指向的字符數組,直到發現換行符或出現文件尾。在讀入數組的最后一個字符后立即寫入空字符。換行符被舍棄,但不會存儲于緩沖區中。
char *gets_s( char *str, rsize_t n );
從 stdin 讀取字符直到發現換行符或出現文件尾。至多寫入 n-1 個字符到 str 所指向的數組,并始終寫入空終止字符(除非 str 是空指針)。若發現換行符,則忽略它并且不將它計入寫入緩沖區的字符數。
在運行時檢測下列錯誤,并調用當前安裝的制約處理函數:
6、 fopen()新模式
fopen()增加了新的創建、打開模式“x”,在文件鎖中比較常用。
以x結尾的模式為獨占模式,文件已存在或者無法創建(一般是路徑不正確)都會導致fopen失敗.文件以操作系統支持的獨占模式打開.[C11]
7、 匿名結構體、聯合體。
在 C 語言中,可以在結構體中聲明某個聯合體(或結構體)而不用指出它的名字,如此之后就可以像使用結構體成員一樣直接使用其中聯合體(或結構體)的成員。
示例:
#include <stdio.h> struct person { char *name; char gender; int age; int weight; struct { int area_code; long phone_number; }; }; int main(void) { struct person jim = {"jim", 'F', 28, 65, {21, 58545566}}; printf("%d\n", jim.area_code); }如果不使用匿名結構體,則上述例子對應的代碼如下:
#include <stdio.h> struct phone { int area_code; long phone_number; }; struct person { char *name; char gender; int age; int weight; struct phone office; }; int main(void) { struct person jim = {"jim", 'F', 28, 65, {21, 58545566}}; printf("%d\n", jim.office.area_code); }對比上述兩個例子可以看出:
使用匿名結構體,結構體對象 jim 可以通過 jim.area_code 直接訪問匿名結構體成員變量 area_code,代碼相對比較簡潔
反之則必須通過 jim.office.area_code 來訪問結構體成員變量
8、 多線程
頭文件<threads.h>定義了創建和管理線程的函數,新的存儲類修飾符_Thread_local限定了變量不能在多線程之間共享。
_Thread_local 指示線程存儲期。它不能用于函數聲明。若將它用在對象聲明上,則它必須在同一對象的每次聲明上都存在。若將它用在塊作用域聲明上,則必須與 static 或 extern 之一組合以決定鏈接。
線程存儲期。存儲期是創建對象的線程的整個執行過程,在啟動線程時初始化存儲于對象的值。每個線程擁有其自身的相異對象。若執行訪問此對象的表達式的線程,不是執行其初始化的線程,則行為是實現定義的。所有聲明為 _Thread_local 的對象擁有此存儲期。
9、 _Atomic類型修飾符和頭文件<stdatomic.h>。
語法
_Atomic ( type-name ) (1) (C11 起)
_Atomic type-name (2) (C11 起)
用作類型限定符;指代 type-name 的原子版本。在此作用中,它可以與 const 、 volatile 及 restrict 混合使用。盡管不同于其他限定符, type-name 的原子版本可能擁有不同的大小、對齊以及對象表示。
_Atomic const int * p1; // p 是指向 _Atomic const int 的指針 const atomic_int * p2; // 同上 const _Atomic(int) * p3; // 同上原子類型的對象是僅有的免除數據競爭的對象,即它們可以被兩個線程共時修改,或先被一個修改再被另一個讀取。.
每個原子對象都擁有關聯于其自身的修改順序,即對該對象的完整修改順序。若從某個線程的視角來看,對于某原子對象M的修改 A 發生先于同一原子對象 M 的修改 B ,則在 M 的修改順序中 A 的出現先于 B 。
注意即使每個原子對象都有其自身的修改順序,它卻不是全序;不同線程可能會觀測到相異原子對象有相異的修改順序。
對于所有原子運算,保證有四種連貫:
寫寫連貫:若原子對象 M 的修改操作 A 發生先于 M 的修改操作 B ,則 M 的修改順序中 A 出現早于 B 。 讀讀連貫:若原子對象 M 的值計算 A 發生先于 M 的值計算 B ,且從 M 上的副效應X求得 A 值,則 B 所計算得的值要么是 X 所存儲的值,要么是 M 上的副效應 Y 所存儲的值,其中 Y 在 M 的修改順序中出現后于 X 。 讀寫連貫:若原子對象 M 的值計算 A 發生先于 M 上的操作 B ,則從 M 上的副效應X求得 A 值,這里 X 在 M 的修改順序中出現先于 B 。 寫讀連貫:若在原子對象 M 上的副效應 X 發生先于 M 的值計算 B ,則求值 B 從 X,或從在 M 的修改順序中出現后于 X 的副效應 Y 求得其值。一些原子運算亦是同步操作:它們可以擁有附加的釋放語義、獲取語義,或順序一致語義。見 memory_order 。
內建的自增減運算符和復合賦值運算符是擁有完全序列一致順序(如同用 memory_order_seq_cst )的讀-修改-寫操作。若想要更不嚴格的同步語義,則可以用標準庫函數替代。
原子屬性僅對左值表達式有意義。左值到右值轉換(其模仿從原子區域到CPU寄存器的內存讀取)會把原子性及其他限定符剝去。
示例:
#include <stdio.h> #include <threads.h> #include <stdatomic.h>atomic_int acnt; int cnt;int f(void* thr_data) {for(int n = 0; n < 1000; ++n) {++cnt;++acnt;// 對于此例,寬松內存順序是足夠的,例如// atomic_fetch_add_explicit(&acnt, 1, memory_order_relaxed);}return 0; }int main(void) {thrd_t thr[10];for(int n = 0; n < 10; ++n)thrd_create(&thr[n], f, NULL);for(int n = 0; n < 10; ++n)thrd_join(thr[n], NULL);printf("The atomic counter is %u\n", acnt);printf("The non-atomic counter is %u\n", cnt); }可能的輸出:
The atomic counter is 10000 The non-atomic counter is 864410、改進的Unicode支持和頭文件<uchar.h>。
語法
” s-char-sequence ” (1)
u8 ” s-char-sequence ” (2) (C11 起)
u ” s-char-sequence ” (3) (C11 起)
U ” s-char-sequence ” (4) (C11 起)
L ” s-char-sequence ” (5)
1) 字符串字面量:字面量類型為 char[] ,用執行字符集從 s-char-sequence 中的下個字符初始化數組中的每個字符。
2) UTF-8 字符串字面量:字面量類型為 char[] ,用 UTF-8 編碼,從 s-char-sequence 中的下個多字節字符初始化字符數組中的每個字符。
3) 16 位寬字符串字面量:字面量類型為 char16_t[] ,如同在實現定義的本地環境中通過執行 mbrtoc16 一般初始化數組中的每個 char16_t 元素。
4) 32 位寬字符串字面量:字面量類型為 char32_t[] ,如同在實現定義的本地環境中通過執行 mbrtoc32 一般初始化數組中的每個 char32_t 元素。
5) 寬字符串字面量:字面量類型為 wchar_t[] ,如同在實現定義的本地環境中通過執行 mbstowcs 一般初始化數組中的每個 wchar_t 元素。
示例:
#include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <uchar.h> #include <locale.h> int main(void) {char s1[] = "a貓?"; // 或 "a\u732B\U0001F34C"char s2[] = u8"a貓?";char16_t s3[] = u"a貓?";char32_t s4[] = U"a貓?";wchar_t s5[] = L"a貓?";setlocale(LC_ALL, "en_US.utf8");printf(" \"%s\" is a char[%zu] holding { ", s1, sizeof s1 / sizeof *s1);for(size_t n = 0; n < sizeof s1 / sizeof *s1; ++n) printf("%#x ", +(unsigned char)s1[n]); puts(" }");printf("u8\"%s\" is a char[%zu] holding { ", s2, sizeof s2 / sizeof *s2);for(size_t n = 0; n < sizeof s2 / sizeof *s2; ++n) printf("%#x ", +(unsigned char)s2[n]); puts(" }");printf(" u\"a貓?\" is a char16_t[%zu] holding { ", sizeof s3 / sizeof *s3);for(size_t n = 0; n < sizeof s3 / sizeof *s3; ++n) printf("%#x ", s3[n]); puts(" }");printf(" U\"a貓?\" is a char32_t[%zu] holding { ", sizeof s4 / sizeof *s4);for(size_t n = 0; n < sizeof s4 / sizeof *s4; ++n) printf("%#x ", s4[n]); puts(" }");printf(" L\"%ls\" is a wchar_t[%zu] holding { ", s5, sizeof s5 / sizeof *s5);for(size_t n = 0; n < sizeof s5 / sizeof *s5; ++n) printf("%#x ", (unsigned)s5[n]); puts(" }"); }可能的輸出:
"a貓?" is a char[9] holding { 0x61 0xe7 0x8c 0xab 0xf0 0x9f 0x8d 0x8c 0 } u8"a貓?" is a char[9] holding { 0x61 0xe7 0x8c 0xab 0xf0 0x9f 0x8d 0x8c 0 }u"a貓?" is a char16_t[5] holding { 0x61 0x732b 0xd83c 0xdf4c 0 }U"a貓?" is a char32_t[4] holding { 0x61 0x732b 0x1f34c 0 }L"a貓?" is a wchar_t[4] holding { 0x61 0x732b 0x1f34c 0 }11、quick_exit()
又一種終止程序的方式,當exit()失敗時用以終止程序。
quick_exit
C
程序支持工具
定義于頭文件
#include <stdlib.h> #include <stdio.h>void f1(void) {puts("pushed first");fflush(stdout); }void f2(void) {puts("pushed second"); }int main(void) {at_quick_exit(f1);at_quick_exit(f2);quick_exit(0); }輸出:
pushed second pushed first12、復數宏,浮點數宏。
頭文件 <tgmath.h> 包含頭文件 <math.h> 及 <complex.h> ,并定義了幾種泛型宏。這些宏會根據參數類型決定要調用的實際函數。
示例:
#include <stdio.h> #include <complex.h> #include <tgmath.h>int main(void) {double complex z1 = I * I; // 虛數單位平方printf("I * I = %.1f%+.1fi\n", creal(z1), cimag(z1));double complex z2 = pow(I, 2); // 虛數單位平方printf("pow(I, 2) = %.1f%+.1fi\n", creal(z2), cimag(z2));double PI = acos(-1);double complex z3 = exp(I * PI); // 歐拉公式printf("exp(I*PI) = %.1f%+.1fi\n", creal(z3), cimag(z3));double complex z4 = 1+2*I, z5 = 1-2*I; // 共軛printf("(1+2i)*(1-2i) = %.1f%+.1fi\n", creal(z4*z5), cimag(z4*z5)); }輸出:
I * I = -1.0+0.0i pow(I, 2) = -1.0+0.0i exp(I*PI) = -1.0+0.0i (1+2i)*(1-2i) = 5.0+0.0i13、time.h新增timespec結構體,時間單位為納秒,原來的timeval結構體時間單位為毫秒。
struct timespec 定義:
typedef long time_t; #ifndef _TIMESPEC #define _TIMESPEC struct timespec { time_t tv_sec; // seconds long tv_nsec; // and nanoseconds }; #endifstruct timespec有兩個成員,一個是秒,一個是納秒, 所以最高精確度是納秒。
一般由函數int clock_gettime(clockid_t, struct timespec *)獲取特定時鐘的時間,常用如下4種時鐘:
CLOCK_REALTIME 統當前時間,從1970年1.1日算起
CLOCK_MONOTONIC 系統的啟動時間,不能被設置
CLOCK_PROCESS_CPUTIME_ID 本進程運行時間
CLOCK_THREAD_CPUTIME_ID 本線程運行時間
struct tm *localtime(const time_t *clock); //線程不安全
struct tm* localtime_r( const time_t* timer, struct tm* result );//線程安全
size_t strftime (char* ptr, size_t maxsize, const char* format,const struct tm* timeptr );
struct timeval 定義:
struct timeval { time_t tv_sec; // seconds long tv_usec; // microseconds }; struct timezone{ int tz_minuteswest; //miniutes west of Greenwich int tz_dsttime; //type of DST correction };struct timeval有兩個成員,一個是秒,一個是微秒, 所以最高精確度是微秒。
一般由函數int gettimeofday(struct timeval *tv, struct timezone *tz)獲取系統的時間
示例:
#include<stdio.h> #include<time.h> #include<sys/time.h>void nowtime_ns() {printf("---------------------------struct timespec---------------------------------------\n"); printf("[time(NULL)] : %ld\n", time(NULL)); struct timespec ts;clock_gettime(CLOCK_REALTIME, &ts);printf("clock_gettime : tv_sec=%ld, tv_nsec=%ld\n", ts.tv_sec, ts.tv_nsec);struct tm t;char date_time[64];strftime(date_time, sizeof(date_time), "%Y-%m-%d %H:%M:%S", localtime_r(&ts.tv_sec, &t));printf("clock_gettime : date_time=%s, tv_nsec=%ld\n", date_time, ts.tv_nsec); } void nowtime_us() {printf("---------------------------struct timeval----------------------------------------\n"); printf("[time(NULL)] : %ld\n", time(NULL)); struct timeval us;gettimeofday(&us,NULL);printf("gettimeofday: tv_sec=%ld, tv_usec=%ld\n", us.tv_sec, us.tv_usec);struct tm t;char date_time[64];strftime(date_time, sizeof(date_time), "%Y-%m-%d %H:%M:%S", localtime_r(&us.tv_sec, &t));printf("gettimeofday: date_time=%s, tv_usec=%ld\n", date_time, us.tv_usec); }int main(int argc, char* argv[]) {nowtime_ns();printf("\n");nowtime_us();printf("\n");return 0; }nowtime.cpp執行結果:
$tt
—————————struct timespec—————————————
[time(NULL)] : 1400233995
clock_gettime : tv_sec=1400233995, tv_nsec=828222000
clock_gettime : date_time=2014-05-16 17:53:15, tv_nsec=828222000
—————————struct timeval—————————————-
[time(NULL)] : 1400233995
gettimeofday: tv_sec=1400233995, tv_usec=828342
gettimeofday: date_time=2014-05-16 17:53:15, tv_usec=828342
PS:有關關鍵字或者接口的參照可以查看以下地址:
http://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5總結
- 上一篇: 关于安卓手机在微信浏览器中无法调起相机的
- 下一篇: Scala实践6