1. 程式人生 > >linux核心中GNU C和標準C的區別

linux核心中GNU C和標準C的區別

今天看了一下午的linux核心程式設計方面的內容,發現linux 核心中GNU C與標準C有一些差別,特記錄如下:
linux 系統上可用的C編譯器是GNU C編譯器,它建立在自由軟體基金會的程式設計許可證的基礎上,因此可以自由釋出。GNU C對標準C進行進一步擴充套件,以增強標準C的功能。下面我們對GNU C中的擴充套件進行一下總結:
1、零長度陣列
GNU C 允許使用零長度陣列,在定義變長物件的頭結構時,這個特性非常有用。例如:
struct minix_dir_entry {
    __u16 inode;
char name[0];
};
結構的最後一個元素定義為零長度陣列,它不佔結構的空間。在標準 C 中則需要定義陣列長度為 1,分配時計算物件大小比較複雜。
2、case範圍
GNU C 允許在一個 case 標號中指定一個連續範圍的值,例如:
case '0' ... '9': c -= '0'; break;
case 'a' ... 'f': c -= 'a'-10; break;
case 'A' ... 'F': c -= 'A'-10; break;
其中case '0' ... '9': 相當於   case '0': case '1': case '2': case '3': case '4':   case '5': case '6': case '7': case '8': case '9':
3、語句表示式
GNU C 把包含在括號中的複合語句看做是一個表示式,稱為語句表示式,它可以出現在任何允許表示式的地方,你可以在語句表示式中使用迴圈、區域性變數等,原本只能在複合語句中使用。例如:
#define min_t(type,x,y) \
   ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
複合語句的最後一個語句應該是一個表示式,它的值將成為這個語句表示式的值。這裡定義了一個安全的求最小值的巨集,在標準 C 中,通常定義為:
#define min(x,y) ((x) < (y) ? (x) : (y))
這個定義計算 x 和 y 分別兩次,當引數有副作用時(比如出現引數自增或自減語句時),將產生不正確的結果,使用語句表示式只計算引數一次,避免了可能的錯誤。語句表示式通常用於巨集定義。
4、typeof關鍵字
使用前一節定義的巨集需要知道引數的型別,利用 typeof 可以定義更通用的巨集,不
必事先知道引數的型別,例如:
#define min(x,y) ({ \
   const typeof(x) _x = (x);    \
   const typeof(y) _y = (y);    \
   (void) (&_x == &_y);          \
_x < _y ? _x : _y; })
這裡 typeof(x) 表示 x 的值型別, const typeof(x) _x = (x); 中定義了一個與 x 型別相同的區域性變數 _x 並初使化為 x, (void) (&_x == &_y); 的作用是檢查引數 x 和 y 的型別是否相同。typeof 可以用在任何型別可以使用的地方,通常用於巨集定義。
5、可變引數的巨集
在 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 不匹配任何引數的情況,這時 arg 的值為空,GNU C 前處理器在這種特殊情況下,丟棄 ## 之前的逗號,這樣
pr_debug("success!\n")
會被擴充套件為
printk("success!\n")
而不是printk("success!\n",)
注意最後的逗號。
6、標號元素
標準 C 要求陣列或結構變數的初使化值必須以固定的順序出現,在 GNU C 中,通過指定索引或結構域名,允許初始化值以任意順序出現。指定陣列索引的方法是在初始化值前寫 '[INDEX] =',要指定一個範圍使用 '[FIRST ... LAST] =' 的形式,例如:
static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL };
將陣列的所有元素初使化為 ~0UL,這可以看做是一種簡寫形式。
要指定結構元素,在元素值前寫 'FIELDNAME:',例如:
struct file_operations ext2_file_operations = {
      llseek:       generic_file_llseek,
      read:           generic_file_read,
   write:       generic_file_write,
      ioctl:       ext2_ioctl,
       mmap:           generic_file_mmap,
       open:           generic_file_open,
   release:        ext2_release_file,
   fsync:       ext2_sync_file,
};
將結構 ext2_file_operations 的元素 llseek 初始化為 generic_file_llseek,元素 read 初始化為 genenric_file_read,依次類推。我覺得這是 GNU C 擴充套件中最好的特性之一,當結構的定義變化以至元素的偏移改變時,這種初始化方法仍然保證已知元素的正確性。對於未出現在初始化中的元素,其初值為 0。
7、當前函式名
GNU CC 預定義了兩個標誌符儲存當前函式的名字,__FUNCTION__ 儲存函式在原始碼中的名字,__PRETTY_FUNCTION__ 儲存帶語言特色的名字。在 C 函式中,這兩個名字是相同的,在 C++ 函式中,__PRETTY_FUNCTION__ 包括函式返回型別等額外資訊,Linux 核心只使用了 __FUNCTION__。
void example()
{
printf{“This is function:%s”, __FUNCTION__};
}
程式碼中__FUNCTION__意味著字串“example”。
8、特殊屬性宣告
GNU C 允許宣告函式、變數和型別的特殊屬性,以便手工的程式碼優化和更仔細的程式碼檢查。要指定一個宣告的屬性,在聲明後寫   __attribute__ (( ATTRIBUTE ))其中 ATTRIBUTE 是屬性說明,多個屬性以逗號分隔。GNU C 支援十幾個屬性,這裡介紹最常用的:
* noreturn
屬性 noreturn 用於函式,表示該函式從不返回。這可以讓編譯器生成稍微優化的程式碼,最重要的是可以消除不必要的警告資訊比如未初使化的變數。例如:
# define ATTRIB_NORET   __attribute__((noreturn))....
asmlinkage NORET_TYPE void do_exit(long error_code) ATTRIB_NORET;
* format
屬性 format 用於函式,表示該函式使用 printf, scanf 或 strftime 風格的引數,使用這類函式最容易犯的錯誤是格式串與引數不匹配,指定 format 屬性可以讓編譯器根據格式串檢查引數型別。例如:
asmlinkage int printk(const char * fmt, ...)    __attribute__ ((format (printf, 1, 2)));
表示第一個引數是格式串,從第二個引數起根據格式串檢查引數。
* unused
屬性 unused 用於函式和變數,表示該函式或變數可能不使用,這個屬性可以避免編譯器產生警告資訊。
* aligned
屬性 aligned 用於變數、結構或聯合型別,指定變數、結構域、結構或聯合的對齊量,以位元組為單位,例如:
struct example_struct
{
char a;
int   b;
long c;
} __attribute__((aligned(4)));
表示該結構型別的變數以4位元組對界。
* packed
屬性 packed 用於變數和型別,用於變數或結構域時表示使用最小可能的對齊,用
於列舉、結構或聯合型別時表示該型別使用最小的記憶體。例如:
struct example_struct
{
char a;
int b__attribute__ ((packed));
long c__attribute__((packed));
};
對於結構體example_struct而言,在i386平臺下,其sizeof的結果為9,如果刪除其中的2個—attribute__((packed)),其sizeof將為12.
9、內建函式
GNU C 提供了大量的內建函式,其中很多是標準 C 庫函式的內建版本,例如memcpy,它們與對應的 C 庫函式功能相同,不屬於庫函式的其他內建函式的名字通常以 __builtin 開始。例如:
* __builtin_return_address (LEVEL)
內建函式 __builtin_return_address 返回當前函式或其呼叫者的返回地址,引數 LEVEL 指定在棧上搜索框架的個數,0 表示當前函式的返回地址,1 表示當前函式的呼叫者的返回地址,依此類推。
* __builtin_constant_p(EXP)
內建函式 __builtin_constant_p 用於判斷一個值是否為編譯時常數,如果引數EXP 的值是常數,函式返回 1,否則返回 0。
* __builtin_expect(EXP, C)
內建函式 __builtin_expect 用於為編譯器提供分支預測資訊,其返回值是整數表
達式 EXP 的值,C 的值必須是編譯時常數。
例如,下面的程式碼檢測第一個引數是否為編譯時常數以確定採用引數版本還是非引數版本程式碼:
#define test_bit(nr,addr) \
(__builtin_constant_p(nr) ? \
constant_test_bit((nr),(addr)) : \
   variable_test_bit((nr),(addr)))

相關推薦

linux心中GNU C標準C區別

今天看了一下午的linux核心程式設計方面的內容,發現linux 核心中GNU C與標準C有一些差別,特記錄如下: linux 系統上可用的C編譯器是GNU C編譯器,它建立在自由軟體基金會的程式設計許可證的基礎上,因此可以自由釋出。GNU C對標準C進行進一步擴充套件,以增強標準C的功能。下面我們對GNU

linux心中的copy_to_usercopy_from_user

Kernel version:2.6.14 CPU architecture:ARM920T Author:ce123(http://blog.csdn.net/ce123) 1.copy_from_user 在學習Linux核心驅動的時候,經常會碰到copy_from_user和c

linux心中:likelyunlikely函式

  核心原始碼:linux-2.6.38.8.tar.bz2      在Linux核心中likely和unlikely函式有兩種(只能兩者選一)實現方式,它們的實現原理稍有不同,但作用是相同的,下面將結合linux-2.6.38.8版本的核心程式碼來進行講解。    

Linux心中的kobjectkset介紹

本文會圍繞kobject、ktype和kset三個概念進行介紹,我們先大概瞭解一下相關概念以及它們之間的關係: kobject在核心中應用最多的就是裝置驅動模型————匯流排、裝置、驅動、類的管理都使用了kobject,但是kobject並不只為裝置驅動模型服

GNU C 、ANSI C標準C標準c++的區別聯絡

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+

linux心中的get userput user

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Linux環境下如何編譯執行c程式

1 單個檔案的編譯和執行 建立main.c檔案,內容如下:  編譯: 執行: 2 多個檔案的編譯和執行建立sum.c檔案,內容如下: 建立main.c檔案,內容如下:   編譯:    生成可執行檔案,檔名為main: 執行程式:

Linux環境下如何編譯執行c程序

文件的 ron 當前 cto directory 變量 沒有 執行文件 nbsp 1 單個文件的編譯和執行 創建main.c文件,內容如下: 編譯: 執行: 2 多個文件的編譯和執行創建sum.c文件,內容如下: 創建main.c文件,內容如下: 編譯:

linux心中GPIO的使用(二)--標準介面函式

在linux核心中,有一些基本模組可以使用標準的介面函式來操作,比如GPIO、interrupt、clock,所謂的標準介面函式是指一些與硬體平臺無關的、linux下做驅動通用的函式, 常用的有: gpio_request();gpio_free()

linux心中likelyunlikely的含義

在核心程式碼中經常會看到unlikely和likely的蹤影。他們實際上是定義在 linux/compiler.h 中的兩個巨集。   #define likely(x)    __builtin_ex

C++java的區別聯系

討論 重要 優勢 net 類型轉換 總結 沒有 中間件 釋放 今晚,數院的一個兄弟借我Java課本,順便問了一句“Java和C++到底有啥區別啊”。一下子有點問蒙了,“啊額.....運行平臺不同....” "一個在高層,一個在底層...." "執行效率不同......

linux調優tcp_max_syn_backlogsomaxconn的區別

linux內核調優The behavior of the backlog argument on TCP sockets changed with Linux 2.2. Now it specifies the queue length for completely established sockets

從彙編底層的角度看cc語言

從彙編和底層的角度看c和類c語言       寫這篇文章的目的是對近期底層學習的總結,也算是勉勵自己吧,畢竟是光靠興趣苦逼自學不是自己專業的東西要承受很多壓力。 https://blog.csdn.net/jggyyhh/article/details

C++cuda C的結合發揮各自的優點

本次實驗主要是實現vector複製到cuda中,一般教材上都是使用指標,很是繁瑣,而且STL優秀的演算法不太容易使用。為了實現C++ STL的優秀演算法和cuda的平行計算能力,二設計的本次實驗。 一下程式碼僅供提供思路。  #include <cuda_runtime.h&

淺析linux心中的idr機制

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

【轉】Linux 心中的Device Mapper機制

轉自:https://www.ibm.com/developerworks/cn/linux/l-devmapper/index.html,寫的很通俗易懂,轉載學習下。 Device Mapper 是 Linux2.6 核心中支援邏輯卷管理的通用裝置對映機制,它為實現用於儲存資源管理的塊裝置驅動

linux心中Makefile的使用

linux核心中Makefile 的作用是根據配置的情況,構造出需要編譯的原始檔列表,然後分別編譯,並把目的碼連結到一起,最終形成 Linux 核心二進位制檔案。     由於 Linux 核心原始碼是按照樹形結構組織的,所以 Makefile 也被分佈在

如何開啟linux心中dev_dbg的開關

比如要開啟某個驅動中的dev_dbg,那麼需要在驅動檔案.c中這些行"<linux/device.h>"或者"<linux /platfom_device.h>"(device.h包含platform_device.h)之前定義DEBUG 如: #define DEBUG 1 #

Linux心中的per-cpu變數

per CPU變數 per-CPU變數從字面上即可猜出它大概的含義,這種型別的變數實際上每個CPU都分配了一個該變數的副本。對於per-CPU的訪問幾乎不需要鎖定,因為每個CPU都工作在自己的副本上,另外per CPU變數還可以儲存在CPU自己的快取上,這樣就最大的優化訪問速度和減少

Microsoft Visual C Borland C Builder 之比較

                         來源:百度(最原始的地方未知,我大概整理了一下格式)。        本文就試圖從技術水平、易用性、穩定性、發展前景等對Visual   C++和C++Builder(Delphi)這兩個重量級開發工具進行比較分析。              由於Delphi