你知道GNU C 对C语言的扩展吗?
為了方便使用,GNU C在標準C語言的基礎(chǔ)上進行了部分方便開發(fā)的擴展。
這里講解一些開發(fā)中可能會用到的,或者使用頻率比較高的內(nèi)容。
零長度數(shù)組和變量長度數(shù)組
GNU C 允許使用零長度數(shù)組,比如:
char data[0];GNU C 允許使用一個變量定義數(shù)組的長度如:
int n=0; scanf("%d",&n); int array[n];case 范圍
GNU C支持 case x...y這樣的語法,[x,y]之間數(shù)均滿足條件。
case 'a'...'z': /*from 'a' to 'z'*/ break;語句表達式
GNU C 把包含在括號中的復(fù)合語句看作是一個表達式,稱為語句表達式。
這種寫法可以避免
#define min_t(x,y) ((x)<(y)?(x):(y))在min_t(x++,++y)中出現(xiàn)的副作用
typeof 關(guān)鍵字
typeof(x)可以獲得x的類型借助typeof關(guān)鍵字我們可以重新定義min_t:
#define min_t(x,y)\({typeof(x) __x=(x); typeof(y) __y=(y);__x<__y?__x:__y;})可變參數(shù)宏
GNU C中宏也支持可變參數(shù)
#define pr_debug(fmt,arg...) \printk(fmt,##arg)這里,如果可變參數(shù)被忽略或為空,“##”操作將使預(yù)處理器去掉它前面的那個逗號。如果你在宏調(diào)用時,確實提供了一些可變參數(shù),GNU C也會工作正常,它會把這些可變參數(shù)放到逗號的后面。
標號元素
標準C要求數(shù)組或結(jié)構(gòu)體的初始化值必須以固定的順序出現(xiàn),在GNU C中,通過指定索引或結(jié)構(gòu)體成員名,允許初始化以任意順序出現(xiàn)。
unsigned char data[MAX] = {[0]=10,[10]=100, };struct file_operations ext2_file_operations= {open:ext2_open,close:ext2_close, };在linux 2.6中推薦如下方式:
struct file_operations ext2_file_operations= {.read=ext2_read,.write=ext2_write, };當前函數(shù)名
GNU C中預(yù)定義兩個標志符保存當前函數(shù)的名字,__ FUNCTION __ 保存函數(shù)在源碼中的名字, __ PRETTY__ FUNCTION __保存帶語言特色的名字。在C函數(shù)中這兩個名字是相同的.
void func_example() {printf("the function name is %s",__FUNCTION__); }在C99中支持__ func __ 宏,因此建議使用 __ func __ 替代 __ FUNCTION __ 。
特殊屬性聲明
GNU C 允許聲明函數(shù)、變量和類型的特殊屬性,以便進行手工的代碼優(yōu)化和定制。如果要指定一個屬性聲明,只需要在聲明后添加__ attribute __((ATTRIBUTE))。其中ATTRIBUTE為屬性說明,如果存在多個屬性,則以逗號分隔。GNU C 支持noreturn,noinline, always_inline, pure, const, nothrow, format, format_arg, no_instrument_function, p, constructor, destructor, used, unused, deprecated, weak, malloc, alias warn_unused_result nonnull等十個屬性。
noreturn屬性作用于函數(shù),表示該函數(shù)從不返回。這會讓編譯器優(yōu)化代碼并消除不必要的警告信息。例如:
#define ATTRIB_NORET __attribute__((noreturn)) .... asmlinkage NORET_TYPE void do_exit(long error_code) ATTRIB_NORET;packed屬性作用于變量和類型,用于變量或結(jié)構(gòu)域時,表示使用最小可能的對齊,用于枚舉、結(jié)構(gòu)或聯(lián)合類型時表示該類型使用最小的內(nèi)存。如對于結(jié)構(gòu)體,就是它告訴編譯器取消結(jié)構(gòu)在編譯過程中的優(yōu)化對齊,按照實際占用字節(jié)數(shù)進行對齊。例如:
struct example_struct {char a;int b;long c; } __attribute__((packed));regparm屬性用于指定最多可以使用n個寄存器(eax, edx, ecx)傳遞參數(shù),n的范圍是0~3,超過n時則將參數(shù)壓入棧中(n=0表示不用寄存器傳遞參數(shù))。
注意:以上這些屬性都是在X86處理器體系結(jié)構(gòu)下的,在X64體系結(jié)構(gòu)下,大部分內(nèi)容都是同樣有效的。但是,這里要注意regparm屬性,由于在X64體系結(jié)構(gòu)下,GUN C的默認調(diào)用約定使用寄存器傳遞參數(shù)。所以,如果regparm屬性里使用的寄存器個數(shù)超過3個,也仍然會使用其他寄存器來傳遞參數(shù)。這一點要遵循X64體系結(jié)構(gòu)的調(diào)用約定。
下面可以看一個例子。
int q = 0x5a; int t1 = 1; int t2 = 2; int t3 = 3; int t4 = 4; #define REGPARM3 __attribute((regparm(3))) #define REGPARM0 __attribute((regparm(0))) void REGPARM0 p1(int a) {q = a + 1; }void REGPARM3 p2(int a, int b, int c, int d) {q = a + b + c + d + 1; }int main() {p1(t1);p2(t1,t2,t3,t4);return 0; }使用objdump命令反匯編,相關(guān)命令如下:
objdump -D 可執(zhí)行程序其中-D選項用于反匯編所有的程序段,包括:代碼段、數(shù)據(jù)段、只讀數(shù)據(jù)段以及一些系統(tǒng)段等等。而-d命令只反匯編代碼段的內(nèi)容。
反匯編后的關(guān)鍵代碼如下:
Disassembly of p .text: 0000000000400474 <p1>:400474: 55 push %rbp400475: 48 89 e5 mov %rsp,%rbp400478: 89 7d fc mov %edi,-0x4(%rbp)40047b: 8b 45 fc mov -0x4(%rbp),%eax40047e: 83 c0 01 add $0x1,%eax400481: 89 05 3d 04 20 00 mov %eax,0x20043d(%rip) # 6008c4 <q>400487: c9 leaveq 400488: c3 retq 0000000000400489 <p2>:400489: 55 push %rbp40048a: 48 89 e5 mov %rsp,%rbp40048d: 89 7d fc mov %edi,-0x4(%rbp)400490: 89 75 f8 mov %esi,-0x8(%rbp)400493: 89 55 f4 mov %edx,-0xc(%rbp)400496: 89 4d f0 mov %ecx,-0x10(%rbp)400499: 8b 45 f8 mov -0x8(%rbp),%eax40049c: 8b 55 fc mov -0x4(%rbp),%edx40049f: 8d 04 02 lea (%rdx,%rax,1),%eax4004a2: 03 45 f4 add -0xc(%rbp),%eax4004a5: 03 45 f0 add -0x10(%rbp),%eax4004a8: 83 c0 01 add $0x1,%eax4004ab: 89 05 13 04 20 00 mov %eax,0x200413(%rip) # 6008c4 <q>4004b1: c9 leaveq 4004b2: c3 retq 00000000004004b3 <main>:4004b3: 55 push %rbp4004b4: 48 89 e5 mov %rsp,%rbp4004b7: 53 push %rbx4004b8: 8b 05 0a 04 20 00 mov 0x20040a(%rip),%eax # 6008c8 <t1>4004be: 89 c7 mov %eax,%edi4004c0: e8 af ff ff ff callq 400474 <p1>4004c5: 8b 0d 09 04 20 00 mov 0x200409(%rip),%ecx # 6008d4 <t4>4004cb: 8b 15 ff 03 20 00 mov 0x2003ff(%rip),%edx # 6008d0 <t3>4004d1: 8b 1d f5 03 20 00 mov 0x2003f5(%rip),%ebx # 6008cc <t2>4004d7: 8b 05 eb 03 20 00 mov 0x2003eb(%rip),%eax # 6008c8 <t1>4004dd: 89 de mov %ebx,%esi4004df: 89 c7 mov %eax,%edi4004e1: e8 a3 ff ff ff callq 400489 <p2>4004e6: b8 00 00 00 00 mov $0x0,%eax4004eb: 5b pop %rbx4004ec: c9 leaveq 4004ed: c3 retq 4004ee: 90 nop4004ef: 90 nopDisassembly of p .data: 00000000006008c0 <__data_start>:6008c0: 00 00 add %al,(%rax)...00000000006008c4 <q>:6008c4: 5a pop %rdx6008c5: 00 00 add %al,(%rax)...00000000006008c8 <t1>:6008c8: 01 00 add %eax,(%rax)...00000000006008cc <t2>:6008cc: 02 00 add (%rax),%al...00000000006008d0 <t3>:6008d0: 03 00 add (%rax),%eax...00000000006008d4 <t4>:6008d4: 04 00 add $0x0,%al...如果讀者還記得2.2.3節(jié)中,關(guān)于GCC基于X64體系結(jié)構(gòu)的調(diào)用約定的話,那就很容易可以看出,函數(shù)p1和p2都使用寄存器傳遞參數(shù),順序就是RDI, RSI, RDX, RCX,這些細節(jié)已經(jīng)跟regparm的規(guī)定完全不一致了。所以,在這里作者覺得,regparm已經(jīng)不起作用了。
轉(zhuǎn)自:https://my.oschina.net/LinuxDaxingxing/blog/751319
推薦閱讀:
專輯|Linux文章匯總
專輯|程序人生
專輯|C語言
我的知識小密圈
關(guān)注公眾號,后臺回復(fù)「1024」獲取學(xué)習(xí)資料網(wǎng)盤鏈接。
歡迎點贊,關(guān)注,轉(zhuǎn)發(fā),在看,您的每一次鼓勵,我都將銘記于心~
嵌入式Linux
微信掃描二維碼,關(guān)注我的公眾號
總結(jié)
以上是生活随笔為你收集整理的你知道GNU C 对C语言的扩展吗?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《浪潮之巅》11~14章
- 下一篇: Sql Server 数据库出现“可疑”