日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

C11 标准下的 C 语言编程

發布時間:2023/12/20 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C11 标准下的 C 语言编程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、摘要

一直以來,我們所學習的 C 語言大多是 ANSI-C 標準,也就是后來被標準化的 C89 標準。在 1999 年發布的 C99 和 2011 年發布的 C11 標準在此之上,引入了許多新的特性,也解決了許多問題。因此,隨著標準的發布,我們的 C 語言規范和寫法也要發生相應的變化。

C++ 同樣也發布了 C++99,C++11,C++14 甚至 C++17 規范。從變化上看,C++11 規范之后的 C++ 語言已經煥然一新,引入了大量非?,F代化的特性。C 語言規范的最大的變化則發生在 C99 規范之中。其后的 C11 雖然也有一些特性,但更多的算是為了于 C++ 同步而引入的新特性。

目前的 GCC 和 Clang 編譯器都已經完整支持 C99 和 C11 的特性,默認都是支持 C11 規范。如果需要顯式指定的時候,則在編譯時加入?-std=c99?或者?-std=c11?即可。

本文將介紹這兩個協議下帶來的新特性,和我們新的編碼習慣的變化。

二、新的基本數據類型規范

在 C99 規范中,有著大量對于新的數據類型的定義和補充。這是非常有必要的,原先的 int,long 等變量基本類型在不同架構的機器上,會有不同的長度,往往會導致不可預期的問題。64 位數值、布爾類型和復數類型的缺失、以及 Unicode 的缺失也阻礙了 C 語言在現代的進一步發展。因此,C99 類型中帶來了大量編碼類型的變化。

2.1 數值類型

我們經常因為數據類型在不同架構機器上的不同表現,而感到困擾。因此在 C99 規范中,引入了標準的固定長度數據類型的規范,并且引入了 64 位數據類型的支持。在 32 位機器上,你可能需要使用 long long 來建立一個 64 位的數據類型。而在 64 位機器上,long 即表示 64 位數據類型。

在 C99 中,引入了新的頭文件?<stdint.h>?在這個頭文件中,同一規范了不同長度數據類型的定義:

  • int8_t, int16_t, int32_t, int64_t 分別代表 8, 16, 32, 64 位的整型
  • uint8_t, uint16_t, uint32_t, uint64_t 分別代表 8, 16, 32, 64 位的無符號整數
  • float, double 分別代表了 32, 64 位浮點數

因此,推薦使用引入?#include <stdint.h>?,并使用這些固定長度的數據類型,來代替傳統的 int, short, long 等。

有時候,如果需要使用原生機器字長的數值類型,以實現最佳性能時,應當使用?intptr_t?類型,它在 32 位機器上等價于?int32_t?而在 64 位機器上等價于?int64_t?。無符號的?uintptr_t?也是如此。

此外,利用?sizeof?返回的類型 size_t 也是這樣的。其在不同架構的機器上字長不同。

如果需要確保使用長度最長的數值類型。可以使用類型?intmax_t?和?uintmax_t?作為最大的容器,來確保類型轉換時,沒有損失和溢出。

2.2 字符類型,寬字節和多字節

傳統 C89 標準只支持 ascii 碼,而你可能發現 C 語言其已經具有了處理 Unicode 字符集的能力。這最早在 95 年引入,并成為 C99 標準的一部分。

在 C99 中引入了?<wchar.h>?和?<wctype.h>?兩個頭文件,用于處理寬字節。傳統的 char 只有 8 位數,因此原生只能容納所有的 ascii 和 擴展 ascii 字符。而 wchar_t 類型則是 32 位或 16 位,可以容納所有的 Unicode 字符。但是這只在用于字符統計等需求時,才需要使用到寬字符類型,因此不常見其使用。

而 UTF-8, UTF-16, UTF-32 等字符編碼格式都是用不同的編碼方式來實現 Unicode 字符集。因此,寬字符類型可以直接容納 UTF-32 格式的字符,也可以正確的用于統計字數。而 UTF-8 這種通用的字長無關的編碼可以直接放在 char 類型的數組中,也可以直接被系統所讀取。唯一的問題在于 sizeof 獲取的長度并不是真正的字數。

在 C11 中?<uchar.h>?頭文件對字符集的 Unicode 支持進一步擴充。支持定義如下字符串:

char s1[] = "你好"; // 標準支持 char s2[] = u8"你好"; // utf-8 編碼 char16_t s3[] = u"你好"; // 16 位寬字符 char32_t s4[] = U"你好"; // 32 位寬字符 wchar_t s5[] = L"你好"; // 根據本機架構決定寬字符長度

2.3 布爾類型

在 C99 規范中引入了新的布爾類型,再也不需要要自行定義了。頭文件?<stdbool.h>?包括其實現。布爾類型的關鍵字是?_Bool,也有一個宏定義為?bool?,取值為?true?和?false?。

因此我們可以這樣使用了:

bool found = true; bool empty = false; bool is_foo();

2.4 復數類型

C99 中引入了復數類型,這意味著我們可以直接表示復數或者平面中的一個點。其聲明在?<complex.h>?頭文件中。分別有三種類型的復數類型:

  • double complex
  • float complex
  • long double complex

有宏?_Complex_I?或者?I?來聲明一個復數。此外還有一些常用的復數函數,例如:

  • ccos, csin, ccos, csinh 等三角函數和雙曲函數

  • cexp, clog, cabs, cpow, csqrt 等數學函數

  • carg, cimag, creal 獲取象限角、虛數部分、實數部分等函數

  • 下面是一個簡單的例子:

    double complex a = 1.0 + 2.0 * I; double complex b = 5.0 + 4.0 * I; a *= b; a = csin(b); a = creal(b);

    2.5 指針類型

    通產需要使用 void* 等來聲明一個指針,或者需要使用強制類型轉換為 long 來進行運算。在?<stdint.h>?中定義了專門的指針類型:?unitptr_t?和在?<stddef.h>?終端指針差值類型?ptrdiff_t。

    ptrdiff_t diff = (uintptr_t)ptrOld - (uintptr_t)ptrNew;

    三、數組和結構體

    在 C99 和 C11 中引入了新的特性,可以使我們更加靈活地使用數組和結構體以及聯合體。

    3.1 可變長數組(VLA)

    在 C99 之前,如果數組的長度在編譯時無法確定,遇到這種情況,我們通常只有兩種做法:一是申請一個足夠長度數組(需要對長度進行估計,否則很可能會溢出),一個是使用 malloc 在堆中分配數組(但是需要維護,需要釋放等)。

    在 C99 之后,引入了可變長數組(VLA)的概念,可以實現數組的長度在編譯時不一定需要確定。這樣可以實現在運行時確定數組長度,而作用于結束后自動釋放。

    比如:

    int n; int array[n];

    但是這種用法也有一些限制,比如:

    • n 和 array 必須位于同一個文件作用域
    • 不可以用于 typedef
    • 不可使用在結構體中
    • 不可以申明為 static 變量
    • 不可以申明為 extern 變量或 extern 變量的指針

    3.2 靈活的初始化

    在 C99 中帶來了非常靈活的初始化數組和結構體的方法,我們不在需要對完整的數組或者結構體進行初始化,可以只對其一部分進行初始化。比如:

    uint32_t a1[64] = {0}; // 全部填充 0 struct thing {uint64_t index;uint32_t counter;}; struct thing t1 = {0}; // 填充 0uint32_t a2[10] = {[2] = 1, [4] = 6}; //對數組部分位置賦值。 struct thing t2 = {.index = 3} // 結構體部分位置賦值 struct thing t3 = {counter: 0}; // 也可以使用類似 Python 的形式

    3.3 alignof

    在 C11 標準中,定義了新的 alignof 運算符,和 sizeof 相對應。在頭文件?<stdalign.h>?中申明。定義了一個對象的對齊要求。

    alignof(char); // 1 alignof(struct {char c; int n;}; // 4 alignof(float[1024]); // 4

    四、宏定義和預編譯

    C99 在宏定義部分有一些新的變化,最常用的就是 Pragma 運算符和可變宏的引入。

    4.1 Pragma 運算符

    C99 中引入。主要有?_Pragma?運算符和?#pragma?宏。是用于指定編譯時的行為,比如:

    # 編譯時顯示消息 #pragma message(“_X86 macro activated!”) # 注釋 #pragma comment(…)

    此外,#pragma once?使用的非常多,這是一個非標準但是被普遍實現的特性(Clang, GCC, Visual C 等主流編譯器均支持)。用于指出該頭文件只引入一次。和下面語句等效:

    #ifndef xxx #def xxx#endif

    4.2可變長宏

    定義宏的時候可以引入不定長度的輸入參數,具體用法不在列出。

    五、兼容 C++ 的改變

    這里是一些引入的 C++ 中的特性。

    5.1 單行注釋

    在 C99 中引入了單行注釋?//?這個在 C++ 中早已實現,也被較多編譯器所支持。在此被列入了標準。

    5.2 任意位置申明

    早前的 C 語言申明語句一定位于語句塊的最開頭。而 C99 之后打破了這種約定,可以在任意位置申明語句。因此下面的內聯計數器也可以直接使用:

    for(int i = 0; i < 10 ; ++i) {//do something. }

    六、堆的分配

    在《how to c in 2016》中指出,應當盡可能使用 calloc 函數代替 malloc 函數,因為其分配空間時會自動初始化為 0,比 malloc 分配后再使用 memset 高效。

    此外也建議不再使用 memset 函數。

    函數原型是:?calloc(object count, size per object)

    七、幾個關鍵字

    7.1 restrict 關鍵字

    這個關鍵字是函數的輸入參數為指針時候的可選關鍵字。比如下面的兩個 restrict 表明了 s1 和 s2 不可以指向同一地址。用于防止未定義行為的發生。

    void *memcpy(void *restrict s1, const void *restrict s2,size_t size);

    7.2 inline 關鍵字

    用于定義函數的關鍵字。使得函數在編譯時在被調用位置直接展開,因此可以極大的提高效率。它比宏的好處在于可以可讀性好,也有編譯時類型檢查。

    7.3?_Noreturn?修飾符

    在 C11 中定義,用于表示函數無返回值,防止未定義行為發生。在?<stdnoreturn.h>?中定義了 noreturn 宏:

    八、輸出和輸入

    8.1 gets_s

    在 C11 中定義,一個安全的讀取字符串函數,取代了危險的 gets 函數。

    char *gets_s( char *str, rsize_t n );

    8.2 fopen “x” 模式

    fopen()?的新的打開、創建模式"x"。用于表明其對于文件的獨占。常常用于文件鎖中。

    九、C11 的輕量級泛型支持

    從 C11 開始,引入了對于泛型的簡單支持。引入了?_Gerneric?關鍵字。其作用是把一族相似功能的函數聚合成一個對外接口。比如:

    _Generic((x), int:abs, float:fabsf, double:fabs)

    首先接受參數 x,而后根據 x 的類型匹配不同的函數來分別調用。此時可以使用一個?#define?來完成聚合,比如:

    #define GENERAL_ABS(x) _Generic((x),int:abs,float:fabsf,double:fabs)(x)GENERAL_ABS(1); GENERAL_ABS(1.1);

    在此基礎上,C11 提供了基于泛型的數學函數庫?<tgmath.h>?其中的函數全部是在?<math.h>?和?<complex.h>?中定義的數學函數所聚合而成的。因此可以無需再根據輸入參數的不同而選用不同的函數了。

    十、C11 線程

    在 C11 中,引入了輕量級線程的標準實現。在?<thread.h>?中,有主要線程使用的函數聲明以及互斥等的聲明,比如線程創建函數?thrd_create, 線程等待合并函數?thrd_join?等。

    此外在?<stdatomic.h>?頭文件中引入了原子類型的相關定義。其中?_Atomic?類型修飾符可以用于申明一個類型的相關讀寫操作是原子的。使用這個申明可以避免一些并發引起的沖突。

    十一、總結

    在這篇博客中,主要簡潔地介紹了一些 C99 和 C11 中引入 C 語言的新特性和用法。其他諸如變量長度限制、遞歸限制等諸多細節也并沒有加以介紹。從結果上來看,這些特性的引入使得 C 語言程序的現代化有所提升,更加安全、更加通用、也更加簡潔。因此只算是一個引子,具體的諸多用法還要在實際編寫中加以體會。

    總結

    以上是生活随笔為你收集整理的C11 标准下的 C 语言编程的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。