<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          超級炫酷的C語言技巧!

          共 2129字,需瀏覽 5分鐘

           ·

          2022-02-16 02:03

          來源:百問科技
          C語言常常讓人覺得它所能表達(dá)的東西非常有限。它不具有類似第一級函數(shù)和模式匹配這樣的高級功能。但是C非常簡單,并且仍然有一些非常有用的語法技巧和功能,只是沒有多少人知道罷了。

          一、指定的初始化

          很多人都知道像這樣來靜態(tài)地初始化數(shù)組:
          int fibs[] = {1, 1, 2, 3, 5};

          C99標(biāo)準(zhǔn)實(shí)際上支持一種更為直觀簡單的方式來初始化各種不同的集合類數(shù)據(jù)(如:結(jié)構(gòu)體,聯(lián)合體和數(shù)組)。

          二、數(shù)組

          我們可以指定數(shù)組的元素來進(jìn)行初始化。這非常有用,特別是當(dāng)我們需要根據(jù)一組#define來保持某種映射關(guān)系的同步更新時。來看看一組錯誤碼的定義,如:


          /* Entries may not correspond to actual numbers. Some entries omitted. */
          #define EINVAL 1
          #define ENOMEM 2
          #define EFAULT 3
          /* ... */
          #define E2BIG 7
          #define EBUSY 8
          /* ... */
          #define ECHILD 12
          /* ... */


          現(xiàn)在,假設(shè)我們想為每個錯誤碼提供一個錯誤描述的字符串。為了確保數(shù)組保持了最新的定義,無論頭文件做了任何修改或增補(bǔ),我們都可以用這個數(shù)組指定的語法。
          char *err_strings[] = {[0] = "Success",[EINVAL] = "Invalid argument",[ENOMEM] = "Not enough memory",[EFAULT] = "Bad address",/* ... */[E2BIG ] = "Argument list too long",[EBUSY ] = "Device or resource busy",/* ... */[ECHILD] = "No child processes"/* ... */};

          這樣就可以靜態(tài)分配足夠的空間,且保證最大的索引是合法的,同時將特殊的索引初始化為指定的值,并將剩下的索引初始化為0。

          三、結(jié)構(gòu)體與聯(lián)合體

          用結(jié)構(gòu)體與聯(lián)合體的字段名稱來初始化數(shù)據(jù)是非常有用的。假設(shè)我們定義:
          struct point {int x;int y;int z;}

          然后我們這樣初始化struct point:

          struct point p = {.x = 3, .y = 4, .z = 5};

          當(dāng)我們不想將所有字段都初始化為0時,這種作法可以很容易的在編譯時就生成結(jié)構(gòu)體,而不需要專門調(diào)用一個初始化函數(shù)。

          對聯(lián)合體來說,我們可以使用相同的辦法,只是我們只用初始化一個字段。

          四、宏列表

          C中的一個慣用方法,是說有一個已命名的實(shí)體列表,需要為它們中的每一個建立函數(shù),將它們中的每一個初始化,并在不同的代碼模塊中擴(kuò)展它們的名字。這在Mozilla的源碼中經(jīng)常用到,我就是在那時學(xué)到這個技巧的。例如,在我去年夏天工作的那個項(xiàng)目中,我們有一個針對每個命令進(jìn)行標(biāo)記的宏列表。其工作方式如下:

          #define FLAG_LIST(_) \_(InWorklist) \_(EmittedAtUses) \_(LoopInvariant) \_(Commutative) \_(Movable) \_(Lowered) \_(Guard)



          它定義了一個FLAG_LIST宏,這個宏有一個參數(shù)稱之為 _ ,這個參數(shù)本身是一個宏,它能夠調(diào)用列表中的每個參數(shù)。舉一個實(shí)際使用的例子可能更能直觀地說明問題。假設(shè)我們定義了一個宏DEFINE_FLAG,如:

          #define DEFINE_FLAG(flag) flag,enum Flag {None = 0,FLAG_LIST(DEFINE_FLAG)Total};#undef DEFINE_FLAG

          對FLAG_LIST(DEFINE_FLAG)做擴(kuò)展能夠得到如下代碼:

          enum Flag {None = 0,DEFINE_FLAG(InWorklist)DEFINE_FLAG(EmittedAtUses)DEFINE_FLAG(LoopInvariant)DEFINE_FLAG(Commutative)DEFINE_FLAG(Movable)DEFINE_FLAG(Lowered)DEFINE_FLAG(Guard)Total};

          接著,對每個參數(shù)都擴(kuò)展DEFINE_FLAG宏,這樣我們就得到了enum如下:

          enum Flag {None = 0,InWorklist,EmittedAtUses,LoopInvariant,Commutative,Movable,Lowered,Guard,Total};

          接著,我們可能要定義一些訪問函數(shù),這樣才能更好的使用flag列表:

          #define FLAG_ACCESSOR(flag) \bool is##flag() const {\return hasFlags(1 << flag);\}\void set##flag() {\JS_ASSERT(!hasFlags(1 << flag));\setFlags(1 << flag);\}\void setNot##flag() {\JS_ASSERT(hasFlags(1 << flag));\removeFlags(1 << flag);\}FLAG_LIST(FLAG_ACCESSOR)#undef FLAG_ACCESSOR

          一步步的展示其過程是非常有啟發(fā)性的,如果對它的使用還有不解,可以花一些時間在gcc –E上。

          五、編譯時斷言

          這其實(shí)是使用C語言的宏來實(shí)現(xiàn)的非常有“創(chuàng)意”的一個功能。有些時候,特別是在進(jìn)行內(nèi)核編程時,在編譯時就能夠進(jìn)行條件檢查的斷言,而不是在運(yùn)行時進(jìn)行,這非常有用。不幸的是,C99標(biāo)準(zhǔn)還不支持任何編譯時的斷言。

          但是,我們可以利用預(yù)處理來生成代碼,這些代碼只有在某些條件成立時才會通過編譯(最好是那種不做實(shí)際功能的命令)。有各種各樣不同的方式都可以做到這一點(diǎn),通常都是建立一個大小為負(fù)的數(shù)組或結(jié)構(gòu)體。最常用的方式如下:
          /* Force a compilation error if condition is false, but also produce a result* (of value 0 and type size_t), so it can be used e.g. in a structure* initializer (or wherever else comma expressions aren't permitted). *//* Linux calls these BUILD_BUG_ON_ZERO/_NULL, which is rather misleading. */#define STATIC_ZERO_ASSERT(condition) (sizeof(struct { int:-!(condition); }) )#define STATIC_NULL_ASSERT(condition) ((void *)STATIC_ZERO_ASSERT(condition) )/* Force a compilation error if condition is false */#define STATIC_ASSERT(condition) ((void)STATIC_ZERO_ASSERT(condition))
          如果(condition)計(jì)算結(jié)果為一個非零值(即C中的真值),即! (condition)為零值,那么代碼將能順利地編譯,并生成一個大小為零的結(jié)構(gòu)體。如果(condition)結(jié)果為0(在C真為假),那么在試圖生成一個負(fù)大小的結(jié)構(gòu)體時,就會產(chǎn)生編譯錯誤。

          它的使用非常簡單,如果任何某假設(shè)條件能夠靜態(tài)地檢查,那么它就可以在編譯時斷言。例如,在上面提到的標(biāo)志列表中,標(biāo)志集合的類型為uint32_t,所以,我們可以做以下斷言:
          STATIC_ASSERT(Total <= 32)

          它擴(kuò)展為:

          (void)sizeof(struct { int:-!(Total <= 32) })

          現(xiàn)在,假設(shè)Total<=32。那么-!(Total <= 32)等于0,所以這行代碼相當(dāng)于:

          (void)sizeof(struct { int: 0 })

          這是一個合法的C代碼。現(xiàn)在假設(shè)標(biāo)志不止32個,那么-!(Total <= 32)等于-1,所以這時代碼就相當(dāng)于:

          (void)sizeof(struct { int: -1 } )

          因?yàn)槲粚挒樨?fù),所以可以確定,如果標(biāo)志的數(shù)量超過了我們指派的空間,那么編譯將會失敗。



          --- EOF ---


          瀏覽 35
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产无遮挡A片又黄又爽 | 日本特黄 AA片免费视频 | 国产蜜臀AV一区二区 | 成人福利视频网站 | 黄色片大女人吃大鸡巴老头子日大逼逼 |