GNU計划,又稱革奴計划,是由Richard Stallman在1983年9月27日公開發起的。它的目標是創建一套完全自由的操作系統。它在編寫linux的時候自己制作了一個標准成為 GNU C標准。ANSI 美國國家標准協會,它對C做的標准ANSI C標准后來被國際標准協會接收成為 標准C 所以 ANSI C 和標准C是一個概念
總體來說現在linux也支持標准C,以后標准C可以跨平台,而GUN c 一般只在linux c下應用
18.1 ANSI C和標准C++的差別
這里的ANSI C指的是最新的標准-C99
1、ANSI C不支持引用
2、ANSI C不支持函數重載
3、ANSI C多了兩個整型(long long、unsigned long long),不過最新的C++編譯器已經支持這兩種整型
4、ANSI C不支持C++中的一個變量初始化方式,例如:int a(8);
5、ANSI C聲明結構時必須使用struct關鍵字,而標准C++不需要
6、ANSI C標准庫中的一些頭文件,在標准C++中有了新的名稱,例如ctime、cstring、climits、cfloat、cctype,有些文件不僅是名稱上的變化
7、ANSI C不支持名稱空間
8、ANSI C不包含bool類型,以及true和false關鍵字
9、聲明函數時,參數為空的含義不同。在ANSI C中表示接受任意個數的參數,而在標准C++中表示不接受參數
10、ANSI C不支持內聯函數
11、ASNI C不支持默認參數
12、ANSI C不支持可用於全局變量的作用域解析操作符(::)
13、使用const定義的全局常量在ANSI C中具有外部鏈接性,在標准C++中具有內部鏈接性,所以在標准C++中聲明外部鏈接性的全局常量必須使用extern,例如:extern const int a = 10;
18.02GNU C比ANSI C擴展的地方
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)) 傳入宏的參數會被增加兩次。
這個在 嵌入式程序員應知道的0x10個基本問題 里有講過。
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 行的目標碼可能會放在較遠的位置,以保證經常執行的目標碼更緊湊。