GNU C 、ANSI C、标准C、标准c++区别和联系
轉載源:http://www.cnblogs.com/balaamwe/archive/2012/01/07/2316076.html
總覽
(1)GNU計劃,又稱革奴計劃,是由Richard Stallman在1983年9月27日公開發起的。它的目標是創建一套完全自由的操作系統。它在編寫linux的時候自己制作了一個標準,稱為GNU C標準。
(2)ANSI(美國國家標準協會),它對C做了一個標準ANSI C,后來被國際標準協會接收成為 標準C,所以 ANSI C 和標準C是一個概念。
(3)總體來說現在linux也支持標準C,以后標準C可以跨平臺,而GUN C 一般只在linux c下應用。
一、ANSI C和標準C++的差別
(1)這里的ANSI C指的是最新的標準——C99;
(2)區別:
(1)允許零長度數組
GNU C允許零長度數組,在定義變長對象的頭結構時,這個特性非常有用。
struct var_data
{? ? ? int len;
? ? ? char data[0];
};
char data[0]僅僅意味著程序中通過var_data的結構體實例的data[index]成員可以訪問len之后的第index個地址,并沒有為data[0]分配內存。
假設struct var_data的數據域保存在struct var_data緊接著的內存區域,通過如下代碼可以遍歷這些數據:
struct var_data s;
...
for (i=0;i<s.len;i++)
{
? ? printf("%02x",s.data[i]);
}
(2)case范圍
GNU C 支持case x...y 這樣的語法,區間[x,y]的數都會滿足這個case的條件,記得數據結構試驗時,有的同學為了做菜單使用了僅100個case,還好我做的是GUI的。
switch(c)
{
? ? ? case '0'...'9': c-='0';? ? ? break;
? ? ? case 'a'...'f': c-='a'-10;
? ? ? break;
? ? ? case 'A'...'F': c-='A'-10;
? ? ? break;
}
這個case的特點大家都看得出來,比標準C少敲了多少case啊
(3)語句表達式
GNU C把包含在括號里的復合語句看做是一個表達式,稱為語句表達式,它可以出現在任何允許表達式的地方。我們可以在語句表達式中使用原本只能在復合語句中使用的循環變量、局部變量等,例如
#define min_t(type,x,y) /
({type __x=(x); type __y=(y);__x<__y?__x:__y})
int ia,ib,mini;
mini=min_t(int,ia,ib);
這樣,因為重新定義了__x和__y這兩個局部變量,所以上述方法定義的宏將不會有副作用。在標準C中,對應的宏通常會有副作用:
#define min(x,y) ((x)<(y)?(x):(y))
而代碼min(++ia,++ib)將會被展開為((++ia)<(++ib)?(++ia):(++ib)), 傳入宏的參數會被增加兩次。
(4)typeof關鍵字
typeof(x)語句可以獲得x的類型,因此,我們可以借助typeof重新定義第3條提到的min_t這個宏
#define min(x,y) /
({ /
? ? ? const typeof(x) _x=(x);/
? ? ? const typeof(y) _y=(y);/
? ? ? (void) (&_x==&_y);/
? ? ? ?_x<_y ? _x: _y ; })
我們不需要像第三條時那樣傳一個type進去,因為通過typeof(x)可以得到type。
代碼 (void) (&_x==&_y);的作用是檢查_x和_y的類型是否一致。
5、可變參數的宏
標準C只支持可變參數的函數,意味著函數的參數可以是不固定的
例如printf()函數的原型是
int printf(const char *format [,argument]...)
而在GNU C中,宏也可以接受可變數目的參數,例如
#define pr_debug(fmt,arg...) printk(fmt,##arg)
這里arg表示其余的參數可以是零個或多個,這些參數以及參數之間的逗號構成arg的值,
在宏擴展時替換arg ,例如
pr_debug("%s:%d",filename,line);
被擴展為
printk("%s:%d",filename,line);
使用##的原因是為了處理arg不代表任何參數的情況,這時候,前面的逗號就變得多余了。
使用##之后,GNU C預處理器會丟棄前面的逗號,這樣代碼
pr_debug("success!/n") 會被正確擴展為 printk("success!/n")
而不是 printk("success!/n",);
6.標號元素
標準c要求數組或結構體的初始化值必須以固定的順序出現,在GNU C中,通過指定索引或結構體成員名,允許初始化值得以任意順序出現。
指定數組索引的方法是在初始化值前添加 [INDEX]= ,當然也可以用 [FIRST...LAST]= 的形式指定一個范圍。例如下面的代碼定義一個數組,并把其中的所有元素賦值為0:
unsigned char data[MAX] ={[0...MAX-1]=0 };
下面的代碼借助結構體成員名初始化結構體:
struct file_operations DEMO_fops = {
? ? owner : ? ?THIS_MODULE,
? ? llseek: ? ? ?DEMO_llseek,
? ? read: ? ? ? DEMO_read,
? ? write: ? ? ? DEMO_write,
? ? ioctl: ? ? ? ?DEMO_ioctl,
? ? open: ? ? ? ?DEMO_open,
? ? release: ? DEMO_release,
};
但是Linux 2.6還是推薦采用標準C的方式,如下
struct file_operations DEMO_fops = {
? ? .owner = ? ?THIS_MODULE,
? ? .llseek = ? DEMO_llseek,
? ? .read = ? ? DEMO_read,
? ? .write = ? ?DEMO_write,
? ? .ioctl = ? ?DEMO_ioctl,
? ? .open = ? ? DEMO_open,
? ? .release = DEMO_release,
};
7.當前函數名
GUN C預定義了兩個標識符保存當前的函數名,__FUNCTION__保存函數在源碼中的名字,
__PRETTY_FUNCTION__保存帶語言特色的名字。在c函數中,這兩個名字是相同的。
void example()
{
? ? ? printf("This is function: %s ",__FUNCTION__);
}
代碼中的__FUNCTION__意味著字符串"example"
8、特殊屬性聲明
GNU C允許聲明函數、變量和類型的特殊屬性,以便進行手工的代碼優化和定制代碼檢查的方法。指定一個聲明的屬性,只需要在申明后添加 __attribute__((ATTRIBUTE))
其中ATTRIBUTE為屬性說明,如果存在多個屬性,則以逗號分隔。GNU C支持noreturn format section aligned packed等十多個屬性
noreturn屬性作用于函數,表示該函數從不返回。這會讓編譯器優化代碼,并消除不必要的的警告信息。例如
#define ATTRIB_NORET __attribute__ ((noreturn)) ....
asmlinkage NORET_TYPE void do_exit(long error_code) ATTRIB_NORET;
format屬性也可用于函數,表示該函數printf scanf 或strftime風格的參數,指定format屬性可以讓編譯器根據格式串檢查參數類型。例如:
asmlinkage int printk(const char * fmt,...)/
__attribute__((format(printf,1,2)));
詳細的可以看http://blog.163.com/sunm_lin/blog/static/9192142200741533038695/
unused屬性作用于函數和變量,表示該函數或變量可能不會被用到,避免編譯器產生的警告信息。
aligned屬性指定結構體、變量、聯合體的對齊方式。packed屬性作用于變量和類型,表示壓縮結構體,使用最小的內存。
struct examprl_struct
{
? ? ? char a;
? ? ? int b;
? ? ? long c;
}__attribute__((packed));
注意,這個__attribute__((packed))只能用在GNU C
關于在VC下的結構體對齊,參照http://hi.baidu.com/deep_pro/blog/item/421db081aeb604debd3e1e01.html
9、內建函數
GNU C 提供了大量的內建函數,其中很多是標準 C 庫函數的內建版本,例如memcpy,它們與對應的 C 庫函數功能相同,本文不討論這類函數,其他內建函數的名字通常以 __builtin 開始。
* __builtin_return_address (LEVEL)
內建函數 __builtin_return_address 返回當前函數或其調用者的返回地址,參數LEVEL 指定在棧上搜索框架的個數,0 表示當前函數的返回地址,1 表示當前函數的調用者的返回地址,依此類推。例如:
++++ kernel/sched.c
437: ? ? ? ? ? ? ? ? printk(KERN_ERR “schedule_timeout: wrong timeout ”
438: ? ? ? ? ? ? ? ? ? ? ? ?“value %lx from %p/n”, timeout,
439: ? ? ? ? ? ? ? ? ? ? ? ?__builtin_return_address(0));
* __builtin_constant_p(EXP)
內建函數 __builtin_constant_p 用于判斷一個值是否為編譯時常數,如果參數 EXP 的值是常數,函數返回 1,否則返回0。例如:
++++ include/asm-i386/bitops.h
249: #define test_bit(nr,addr) /
250: (__builtin_constant_p(nr) ? /
251: constant_test_bit((nr),(addr)) : /
252: variable_test_bit((nr),(addr)))
很多計算或操作在參數為常數時有更優化的實現,在 GNU C 中用上面的方法可以根據參數是否為常數,只編譯常數版本或非常數版本,這樣既不失通用性,又能在參數是常數時編譯出最優化的代碼。
* __builtin_expect(EXP, C)
內建函數 __builtin_expect 用于為編譯器提供分支預測信息,其返回值是整數表達式 EXP 的值,C 的值必須是編譯時常數。例如:
++++ include/linux/compiler.h
13: #define likely(x) ? ? ? __builtin_expect((x),1)
14: #define unlikely(x) ? ? __builtin_expect((x),0)
++++ kernel/sched.c
564: ? ? ? ? if (unlikely(in_interrupt())) {
565: ? ? ? ? ? ? ? ? printk(”Scheduling in interrupt/n”);
566: ? ? ? ? ? ? ? ? BUG();
567: ? ? ? ? }
這個內建函數的語義是 EXP 的預期值是 C,編譯器可以根據這個信息適當地重排語句塊的順序,使程序在預期的情況下有更高的執行效率。上面的例子表示處于中斷上下文是很少發生的,第 565-566 行的目標碼可能會放在較遠的位置,以保證經常執行的目標碼更緊湊。
總結
以上是生活随笔為你收集整理的GNU C 、ANSI C、标准C、标准c++区别和联系的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PHP+Ajax点击加载更多列表数据实例
- 下一篇: 实现上一篇明日方舟官网仿制的代码