GNU C的扩展
GNU C的擴展
Author:tiger-john
Time:2013-08-3(重新修改)
mail:jibo.tiger@gmail.com
Blog:http://blog.csdn.net/tigerjb/article/details/8299557
轉載請注明出處!
一.? 結構體賦值:
1、對成員賦值
例如結構體
struct st1 { int a; int b; int c; }
1.1 用 {}形式
struct st1 st1 = {1,2,3);1.2 ?linux kernel風格
struct st1 st1 = { .a = 1, .b = 2, .c =3, };注:此風格(即在成員變量之前加點“.”),可以不按成員變量的順序進行賦值。如可以為
struct st1 st1 = { .c = 3, .a = 1, .b = 2, };2.對整體賦值.
struct st1 a, b; b = a;3.結構體作為函數返回值對另一個結構體賦值.
struct st1 func1(); struct st1 a = func1();二內聯函數 inline
在c 中,為了解決一些頻繁調用的小函數而大量消耗棧空間或者是叫棧內存的問題,特別的引入了inline修飾符,表示為內聯函數。內聯函數使用inline關鍵字定義,并且函數體和聲明必須結合在一起,否則編譯器將他作為普通函數對待。inline函數一般放在頭文件中。
inline void function(int x); //僅僅是聲明函數,沒有任何效果 inline void function(int x) //正確 { return x; }三.typeof用法
1.關鍵字typeof用于獲取表達式的數據類型
(1)char*chptr;
typeof (*chptr) ch; //等價于char ch typeof (ch) *chptr1; //等價于char *chptr1 typeof (chptr1) array[5]; //char *array[5] chptr1的數據類型為char *(2)typeof常用在linux內核中
#define min(x,y) ({ \ typeof(x) __min1 = (x); \ typeof(y) __min2 = (y); \ (void) (& __min1 == & __min2); \ __min1 < __min2 ? __min1 :min2})通過typeof獲得x和y的數據,然后定義兩個臨時變量,并把x和y的分別賦給兩個臨時變量最后進行比較。另外,宏定義中(void)(& __min1 = &__min2)語句的作用是用來警告x和y不能屬于不同的數據類型。
實例:
#include <stdio.h> #define min(x,y) ({ \ typeof(x) __min1 = (x); \ typeof(y) __min2 = (y); \ (void) (& __min1 == & __min2); \ __min1 < __min2 ? __min1 :min2}) int main() { int a=4; int b=6; int min_data; min_data=min(a,b); printf(“the min=%d\n”,min_data); return 0; }執行結果:
the min=4
如改為:
#include <stdio.h> #define min(x,y) ({ \ typeof(x) __min1 = (x); \ typeof(y) __min2 = (y); \ (void) (& __min1 == & __min2); \ __min1 < __min2 ? __min1 :min2}) int main() { int a=4; float b=6; int min_data; min_data=min(a,b); printf(“the min=%d\n”,min_data); return 0; }則會提示以下警告:
main.c: In function ‘main’:
main.c:17:9: warning: comparison of distinct pointer types lacks acast [enabled by default]
四.GNU C之表達式中的復合語句
1.前言:
標準C中,表達式指的是運算符和操作數的組合,而復合語句指的是由一個或多個被括在花括號里的語句構成的代碼塊。在標準C中,不允許將復合語句用于表達式中。
在GNU C中,允許用小括號括起來的復合語句出現在一個表達式中。這個表達式的類型為復合語句中以分號結尾的最后一個子語句表達式的類型,其值也為最后子表達式的值。
?實例:
#include <stdio.h> main() { int a = ({ int b =4; int c =3; b+c; b+c-2; }); printf("a = %d\n",a); return 0; }輸出結果為:
jibo@jibo-VirtualBox:~/cv_work/work/GNUC/brace$ ./main
a = 5
說明:
a的數值是復合語句中最后一個語句的值,并且它的數據類型與最后一個語句的數據類型相匹配。
2.這種特性常用于在Linux內核中常被用于宏的定義中
#define min(x,y) ({ \ typeof(x) __min1 = (x); \ typeof(y) __min2 = (y); \ (void) (& __min1 == & __min2); \ __min1 < __min2 ? __min1 :min2})此處定義了一個安全的求最小值的宏,在標準C中,通常定義為:
#define min(x,y) ((x) < (y) ? (x) : (y))這個定義計算 x 和 y 分別兩次,當參數有副作用時,將產生不正確的結果,使用語句表達式只計算參數一次,避免了可能的錯誤。語句表達式通常用于宏定義。
五.GNU C之標號元素
標準 C 要求數組或結構變量的初使化值必須以固定的順序出現,在 GNU C 中,通過指定索引或結構域名,允許初始化值以任意順序出現。指定數組索引的方法是在初始化值前寫 '[INDEX] =',要指定一個范圍使用 '[FIRST ... LAST] =' 的形式。
1.在數組中的應用
在數組的初始化列表中使用“[index]=value”這樣的形式即可以實現對指定(通過index指定)的某個元素進行初始化。
實例
#include <stdio.h> int main(void) { int i; int arr[6] = {[3] =10,11,[0]=5,6}; for (i=0;i<6;i++) printf("a[%d]=%d\n",i,arr[i]); return 0; }執行結果為:
jibo@jibo-VirtualBox:~/cv_work/work/GNUC/initializer $ ./main
a[0]=5
a[1]=6
a[2]=0
a[3]=10
a[4]=11
a[5]=0
說明:
如果在一個指定初始化項目后跟有不至一個值,如[3]=10,11。則這些多余的數值將用來對后續的數組元素進行初始化,即數值11用來初始化arr[4].
對C語言數組來說,在初始化一個或多個元素后,其中未經初始化的元素將被自動地初始化為0后者NULL(針對指針變量而言)。未經過任何初始化的數組,其所有元素的值都將是不確定的。
2.GNU C還支持”[first…last]=value”的形式,即以個范圍內的幾個元素被初始化為同一值。
實例
#include <stdio.h> int main() { int i; int arr[]={ [0 ... 3] =1,[4 ... 5]=2,[6 ... 9] =3}; for(i=0; i<sizeof(arr)/sizeof(arr[0]);i++ ) printf("arr[%d]:%d\n",i,arr[i]); return 0; }執行結果為:
jibo@jibo-VirtualBox:~/cv_work/work/GNUC/initializer $ ./main1
arr[0]:1
arr[1]:1
arr[2]:1
arr[3]:1
arr[4]:2
arr[5]:2
arr[6]:3
arr[7]:3
arr[8]:3
arr[9]:3
六.GNU C之匿名聯合或結構體
在GNU C中,可以在結構體中聲明某個聯合體(或結構體)而不用指出它的名字,這樣之后,就可以像使用結構體成員一樣直接使用其中聯合體(或結構體)的成員一樣直接使用其中聯合體(或結構體)的成員。
實例:
#include <stdio.h> struct test_struct { char * name; union { char gender; int id; }; int num; }; int main(void) { struct test_struct test_struct={"jibo",'F',28}; printf("test_struct.gender=%c,test_struct.id=%d\n",test_struct.gende, test_sturct.id) return 0; }執行結果為:
jibo@jibo-VirtualBox:~/cv_work/work/GNUC/non_struct $./main
test_struct.gender=F,test_struct.id=70
注:在linux內核中常用匿名聯合(或結構體)。
七.GNU C之分支聲明:
對于條件選擇語句,gcc內建了一條指令用于優化,在該條件經常出現,或者該條件很少出現的時候,編譯器可以根據這條指令對條件分支選擇進行優化。內核把這條指令封裝成了宏,即likely()和unlikely()
例如:
if (foo){ /**/ }如果想要把這個選擇標記成絕少發生的分支:
if (unlikely(foo)){ /**/ }相反,如果想把一個分支標記為通常為真的選擇:
if(likely(foo)) { /**/ }注:關于likely()和unlikely()的具體分析見Linux 內核源碼中likely()和unlikely()
八.零長度的數組
在標準C中長度為零的數組是被禁止的,其要求數組最少長度為1個字節。但是GCC允許定義零長數組。那么為什么GNU C中要支持零長度數組,它的好處是什么,它如何使使用,以及零長度數組主要用在什么場合:
(1)目的:是為了訪問不定長結構體時節省空間和便利性。
(2)用法:在一個結構體的最后 ,申明一個長度為0的數組,就可以使得這個結構體是可變長的。對于編譯器來說,此時長度為0的數組并不占用空間,因為數組名本身不占空間,它只是一個偏移量, 數組名這個符號本身代表了一個不可修改的地址常量 (注意:數組名永遠都不會是指針!),但對于這個數組的大小,我們可以進行動態分配。
實例:
struct demo { int a; char b[256]; char follow[0]; };現在程序中要分配一個struct demo結構體,并緊鄰其后分配長度為LEN個字節空間,則可以使用如下方法得到:
structdemo *demo=(struct demo*)malloc(sizeof(struct demo)+LEN);
這樣就可以用demo->fellow來訪問結構體demo隨后的空間數據,非常方便。當然也可以使用指針來達到這樣的目的。
struct demo{ int a; char b[256]; char *follow; };structdemo *demo=(struct demo *)malloc(sizeof(struct demo)+LEN);
同樣可以達到零長度數組的效果,但是卻多分配了一個char指針。如果分配額外數據空間還好,否則就是白白浪費了空間。
注:
在某一結構末尾如定義類似 charbytes[0] 的零長數組,表示該結構不定長,可通過數組的方式進行擴展。結構中必包含一個長度信息。結構本身類似于一個信息頭。同時,此結構只能通過堆方式分配內存。
優點:比起在結構體中聲明一個指針變量、再進行動態分配的辦法,這種方法效率要高。因為在訪問數組內容時,不需要間接訪問,避免了兩次訪存。
實例:
#include <stdio.h> #include <stdlib.h> struct test{ int count; //reverse is array name;the array is no item; //the array address follow test struct int reverse[0]; }; int main() { int i; struct test *ptest = (struct test *)malloc(sizeof(struct test)+sizeof(int)*10); for(i=0;i<10;i++){ ptest->reverse[i]=i+1; } for(i=0;i<10;i++){ printf("reverse[%d]=%d \n",i,ptest->reverse[i]); } printf("sizeof(struct test) =%d\n",sizeof(struct test)); int a = *(&ptest->count +1 ); printf("a=%d\n",a); return 0; }執行結果為:
jibo@jibo-VirtualBox:~/cv_work/work/zeor_arry $ ./main
reverse[0]=1
reverse[1]=2
reverse[2]=3
reverse[3]=4
reverse[4]=5
reverse[5]=6
reverse[6]=7
reverse[7]=8
reverse[8]=9
reverse[9]=10
sizeof(struct test) =4
a=1
分析:
可以看到test結構體中reverse數組并不占用空間。sizeofstruct test占用內存空間為4。并且可以看到結果體conunt變量之后就是零長度數組的內容。
九.范圍標記
GCC還擴充了范圍標記,通過它可以表示一段數值范圍。這可以用在C程序的很多地方。最常用的莫過于switch/case 語句中。
實例:
static int sd_major(int major_idx) { switch (major_idx) { case 0: return SCSI_DISK0_MAJOR; case 1 ... 7: return SCSI_DISK1_MAJOR + major_idx - 1; case 8 ... 15: return SCSI_DISK8_MAJOR + major_idx - 8; default: BUG(); return 0; /* shut up gcc */ } }范圍標記還可以用來初始化數組里面的一些連續元素。如第五章中所介紹的數組。
十.給函數、變量和數據類型指定屬性
屬性是程序員向編譯器傳送信息或命令的工具,通常用它們來命令編譯器在編譯程序的時候,幫我們完成某些特殊的處理。屬性可以指定到不同種類的對象上,包括函數、變量和類型等。指定屬性時,必須用關鍵字”__attribute__”然后再在后面跟上用兩個圓括號括起來的屬性列表,屬性列表中的屬性用逗號隔開。
使用方法如下:
__attrbitue__((attr_1,attr_2,attr_3))1. noreturn
function屬性,noreturn 用于函數,表示該函數從不返回。這可以讓編譯器生成稍微優化的代碼,最重要的是可以消除不必要的警告信息比如未初始化的變量。
2. format(ARCHETYPE,STRING-INDEX,FIRST-TO-CHECK)
function屬性 ,format 用于函數,表示該函數使用printf, scanf 或strftime 風格的參數,使用這類函數最容易犯的錯誤是格式串與參數不匹配,指定format 屬性可以讓編譯器根據格式串檢查參數類型.
參數說明:
1>“archetype”指定是哪種風格;
2>“string-index”指定傳入函數的第幾個參數是格式化字符串
3>“first-to-check”指定從函數的第幾個參數開始按上述規則進行檢查
實例:
include/linux/kernel.h asmlinkage int printk(const char * fmt, ...) __attribute__ ((format (printf, 1, 2)));3.unused:
function屬性,unused屬性用于函數和變量,表示該函數或變量可能不使用,這個屬性可以避免編譯器產生警告信息。
4.deprecated:
function屬性,deprecated指出函數已經被廢棄,不應該再使用。如果試圖使用已經廢棄的函數,就會收到警告。還可以對類型和變量應用這個屬性,促使開發人員盡可能少使用它們。
5.section ("section-name")
function屬性,__attribute__中的section屬性將函數或數據放入指定名為”section_name”輸入段中而不是輸出段中。
注:
在 linux 驅動程序設計中,模塊加載函數前有一個 __init 宏,也用了attribute 的 section屬性。
#define __init __attribute__ ((__section__(".init.text")))
在linux內核中,所有標示為__init的函數在鏈接的時候都放在.init.text這個段中。此外,所有的__init函數在區段.initcall.init中還保存了一份函數指針,在初始化時內核會通過這些函數指針調用這些 __init 函數,并在初始化完成后釋放 init 區段
在linux 內核源代碼中,與段相關的重要宏定義有:
__init , __initdata, __exit, __exitdata 及類似的宏
6.aligned(ALIGNMENT)
function:可以在定義變量時,添加__attribut__,來決定是否使用內存對齊,或是內存對齊到幾個字節。
實例:
struct i387_fxsave_struct { unsigned short cwd; unsigned short swd; unsigned short twd; unsigned short fop; } __attribute__ ((aligned (16)));表示該結構類型的變量以16字節對齊。
7.packed:
function屬性,告訴編譯器取消結構在編譯過程中的優化對齊,按照實際占用字節數對齊。
8.interrupt(“”):
在 ARM 平臺上 "__attribute((interrupt(”IRQ”)))" 表示改函數是一個interrupt handler。
十一.可變參數宏
在 GNU C 中,宏可以接受可變數目的參數,就象函數一樣。
實例:
include/linux/kernel.h #define pr_debug(fmt,arg...) \ printk(KERN_DEBUG fmt,##arg)說明:arg表示其余的參數,可以是零個或多個,這些參數以及參數之間的逗號構成arg的值,在宏擴展時替換arg
例如:
pr_debug("%s:%d",filename,line)擴展為
printk("<7>" "%s:%d", filename, line)使用 ## 的原因是處理 arg 不匹配任何參數的情況,這時 arg 的值為空,GNUC 預處理器在這種特殊情況下,丟棄 ## 之前的逗號,這樣
pr_debug("success!\n")擴展為
printk("<7>" "success!\n")注意最后沒有逗號。
十二.內建函數
GNUC 提供了大量的內建函數,其中很多是標準C庫函數的內建版本,例如memcpy,它們與對應的C庫函數功能相同。還有一些其它內建函數,它們的名字通常以__builtin開始。
1.__builtin_return_address(LEVEL)
內建函數__builtin_return_address返回當前函數或其調用者的返回地址,參數LEVEL指定在棧上搜素框架的個數,0表示當前函數的返回地址,1表示當前函數的調用者的返回地址。
實例:
kernel/sched.c printk(KERN_ERR "schedule_timeout: wrong timeout" "value %lx from %p\n", timeout, __builtin_return_address(0));2.__builtin_constant_p(EXP)
內建函數__builtin_constant_p用于判斷一個值是否為編譯時常數,如果參數EXP的值是常數,函數返回1,否則返回0.
實例:
include/asm-i386/bitops.h #define test_bit(nr,addr) \ (__builtin_constant_p(nr) ? \ constant_test_bit((nr),(addr)) : \ variable_test_bit((nr),(addr)))說明:很多計算或操作在參數為常數時有更優化的實現,在GNU C中用上面的方法可以根據參數是否為常數,只編譯常數版本或非常數版本,這樣既不失通用性,又能在參數是常數時編譯出最優化的代碼。
3.__builtin_expect(EXP,C)
內建函數__builtin_expect用于為編譯器提供分支預測信息,其返回值是整數表達式EXP的值,C的值必須是編譯時常數。
實例:
include/linux/compiler.h #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) kernel/sched.c if(unlikely(in_interrupt())) { printk("Scheduling in interrupt\n"); BUG(); }這個內建函數的語義是 EXP 的預期值是 C,編譯器可以根據這個信息適當地重排語句塊的順序,使程序在預期的情況下有更高的執行效率。上面的例子表示處于中斷上下文是很少發生的。
轉載于:https://blog.51cto.com/tigerjibo/1276013
總結
- 上一篇: malloc和new有什么区别
- 下一篇: 提升领导力 六商是基础