0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

探究Linux GNU C與ANSI C之間的區(qū)別

奈因PCB電路板設(shè)計(jì) ? 來(lái)源:嵌入式Linux系統(tǒng)開(kāi)發(fā) ? 作者:嵌入式Linux系統(tǒng)開(kāi) ? 2021-07-26 16:54 ? 次閱讀

Linux 上可用的 C 編譯器是 GNU C 編譯器,它建立在自由軟件基金會(huì)的編程許可證的基礎(chǔ)上,因此可以自由發(fā)布。GNU C對(duì)標(biāo)準(zhǔn)C進(jìn)行一系列擴(kuò)展,以增強(qiáng)標(biāo)準(zhǔn)C的功能。

1.零長(zhǎng)度和變量長(zhǎng)度數(shù)組

GNU C允許使用零長(zhǎng)度數(shù)組,在定義變長(zhǎng)對(duì)象的頭結(jié)構(gòu)時(shí),這個(gè)特性非常有用。例如:

struct var_data {

int len;

char data[0];

};

char data[0]僅僅意味著程序中通過(guò)var_data結(jié)構(gòu)體實(shí)例的data[index]成員可以訪問(wèn)len之后的第index個(gè)地址,它并 沒(méi)有為data[]數(shù)組分配內(nèi)存,因此sizeof(struct var_data)=sizeof(int)。

假設(shè)struct var_data的數(shù)據(jù)域就保存在struct var_data緊接著的內(nèi)存區(qū)域中,則通過(guò)如下代碼可以遍歷這些數(shù)據(jù):

struct var_data s;

。。。

for (i = 0; i 《 s.len; i++)

printf(“%02x”, s.data[i]);

GNU C中也可以使用1個(gè)變量定義數(shù)組,例如如下代碼中定義的“double x[n]”:

int main (int argc, char *argv[])

{

int i, n = argc;

double x[n];

for (i = 0; i 《 n; i++)

x[i] = i;

return 0;

}

2.case范圍

GNU C支持case x…y這樣的語(yǔ)法,區(qū)間[x,y]中的數(shù)都會(huì)滿足這個(gè)case的條件,請(qǐng)看下面的代碼:

switch (ch) {

case ‘0’。。。 ‘9’: c -= ‘0’;

break;

case ‘a(chǎn)’。。。 ‘f’: c -= ‘a(chǎn)’ - 10;

break;

case ‘A’。。。 ‘F’: c -= ‘A’ - 10;

break;

}

代碼中的case‘0’。。?!?’等價(jià)于標(biāo)準(zhǔn)C中的:

case ‘0’: case ‘1’: case ‘2’: case ‘3’: case ‘4’:

case ‘5’: case ‘6’: case ‘7’: case ‘8’: case ‘9’:

3.語(yǔ)句表達(dá)式

GNU C把包含在括號(hào)中的復(fù)合語(yǔ)句看成是一個(gè)表達(dá)式,稱為語(yǔ)句表達(dá)式,它可以出現(xiàn)在任何允許表達(dá)式的地 方。我們可以在語(yǔ)句表達(dá)式中使用原本只能在復(fù)合語(yǔ)句中使用的循環(huán)、局部變量等,例如:

#define min_t(type,x,y)

( { type _ _x =(x);type _ _y = (y); _ _x《_ _y _ _x: _ _y; })

int ia, ib, mini;

float fa, fb, minf;

mini = min_t(int, ia, ib);

minf = min_t(float, fa, fb);

因?yàn)橹匦露x了__xx和__y這兩個(gè)局部變量,所以用上述方式定義的宏將不會(huì)有副作用。在標(biāo)準(zhǔn)C中,對(duì)應(yīng)的如 下宏則會(huì)產(chǎn)生副作用:

#define min(x,y) ((x) 《 (y) (x) : (y))

代碼min(++ia,++ib)會(huì)展開(kāi)為((++ia)《(++ib)(++ia):(++ib)),傳入宏的“參數(shù)”增加兩次。

4.typeof關(guān)鍵字

typeof(x)語(yǔ)句可以獲得x的類(lèi)型,因此,可以借助typeof重新定義min這個(gè)宏:

#define min(x,y) ({ const typeof(x) _x = (x);

const typeof(y) _y = (y);

(void) (&_x == &_y);

_x 《 _y _x : _y; })

我們不需要像min_t(type,x,y)那個(gè)宏那樣把type傳入,因?yàn)橥ㄟ^(guò)typeof(x)、typeof(y)可以獲得type。代 碼行(void)(&_x==&_y)的作用是檢查_(kāi)x和_y的類(lèi)型是否一致。

5.可變參數(shù)宏

標(biāo)準(zhǔn)C就支持可變參數(shù)函數(shù),意味著函數(shù)的參數(shù)是不固定的,例如printf()函數(shù)的原型為:

int printf( const char *format [, argument]。。。 );

而在GNU C中,宏也可以接受可變數(shù)目的參數(shù),例如:

#define pr_debug(fmt,arg.。。)

printk(fmt,##arg)

這里arg表示其余的參數(shù),可以有零個(gè)或多個(gè)參數(shù),這些參數(shù)以及參數(shù)之間的逗號(hào)構(gòu)成arg的值,在宏擴(kuò)展時(shí)替換 arg,如下列代碼:

pr_debug(“%s:%d”,filename,line)

會(huì)被擴(kuò)展為:

printk(“%s:%d”, filename, line)

使用“##”是為了處理arg不代表任何參數(shù)的情況,這時(shí)候,前面的逗號(hào)就變得多余了。使用“##”之后,GNU C預(yù) 處理器會(huì)丟棄前面的逗號(hào),這樣,下列代碼:

pr_debug(“success!

”)

會(huì)被正確地?cái)U(kuò)展為:

printk(“success!

”)

而不是:

printk(“success!

”,)

6.標(biāo)號(hào)元素

標(biāo)準(zhǔn)C要求數(shù)組或結(jié)構(gòu)體的初始化值必須以固定的順序出現(xiàn),在GNU C中,通過(guò)指定索引或結(jié)構(gòu)體成員名,允許 初始化值以任意順序出現(xiàn)。

指定數(shù)組索引的方法是在初始化值前添加“[INDEX]=”,當(dāng)然也可以用“[FIRST.。.LAST]=”的形式指定一個(gè)范圍。例如,下面的代碼定義了一個(gè)數(shù)組,并把其中的所有元素賦值為0:

unsigned char data[MAX] = { [0 。。。 MAX-1] = 0 };

下面的代碼借助結(jié)構(gòu)體成員名初始化結(jié)構(gòu)體:

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,

};

但是,Linux 2.6推薦類(lèi)似的代碼應(yīng)該盡量采用標(biāo)準(zhǔn)C的方式:

struct file_operations ext2_file_operations = {

.llseek = generic_file_llseek,

.read = generic_file_read,

.write = generic_file_write,

.aio_read = generic_file_aio_read,

.aio_write = generic_file_aio_write,

.ioct = ext2_ioctl,

.mmap = generic_file_mmap,

.open = generic_file_open,

.release = ext2_release_file,

.fsync = ext2_sync_file,

.readv = generic_file_readv,

.writev = generic_file_writev,

.sendfile = generic_file_sendfile,

};

7.當(dāng)前函數(shù)名

GNU C預(yù)定義了兩個(gè)標(biāo)識(shí)符保存當(dāng)前函數(shù)的名字,__FUNCTION__保存函數(shù)在源碼中的名字,__PRETTY_FUNCTION__保存帶語(yǔ)言特色的名字。在C函數(shù)中,這兩個(gè)名字是相同的。

void example()

{

printf(“This is function:%s”, __FUNCTION__);

}

代碼中的__FUNCTION__意味著字符串“example”。C99已經(jīng)支持__func__宏,因此建議在Linux編程中不再使用__FUNCTION__,而轉(zhuǎn)而使用__func__:

void example(void)

{

printf(“This is function:%s”, __func__);

}

8.特殊屬性聲明

GNU C允許聲明函數(shù)、變量和類(lèi)型的特殊屬性,以便手動(dòng)優(yōu)化代碼和定制代碼檢查的方法。要指定一個(gè)聲明的 屬性,只需要在聲明后添加__attribute__((ATTRIBUTE))。其中ATTRIBUTE為屬性說(shuō)明,如果存在多個(gè)屬 性,則以逗號(hào)分隔。GNU C支持noreturn、format、section、aligned、packed等十多個(gè)屬性。

noreturn屬性作用于函數(shù),表示該函數(shù)從不返回。這會(huì)讓編譯器優(yōu)化代碼,并消除不必要的警告信息。例如:

# define ATTRIB_NORET __attribute__((noreturn)) 。。。。 asmlinkage NORET_TYPE void do_exit(long error_code) ATTRIB_NORET;

format屬性也用于函數(shù),表示該函數(shù)使用printf、scanf或strftime風(fēng)格的參數(shù),指定format屬性可以讓編譯器根據(jù)格 式串檢查參數(shù)類(lèi)型。例如:

asmlinkage int printk(const char * fmt, 。。。) __attribute__ ((format (printf, 1, 2)));

上述代碼中的第1個(gè)參數(shù)是格式串,從第2個(gè)參數(shù)開(kāi)始都會(huì)根據(jù)printf()函數(shù)的格式串規(guī)則檢查參數(shù)。

unused屬性作用于函數(shù)和變量,表示該函數(shù)或變量可能不會(huì)用到,這個(gè)屬性可以避免編譯器產(chǎn)生警告信息。

aligned屬性用于變量、結(jié)構(gòu)體或聯(lián)合體,指定變量、結(jié)構(gòu)體或聯(lián)合體的對(duì)齊方式,以字節(jié)為單位,例如:

struct example_struct {

char a;

int b;

long c;

} __attribute__((aligned(4)));

表示該結(jié)構(gòu)類(lèi)型的變量以4字節(jié)對(duì)齊。

packed屬性作用于變量和類(lèi)型,用于變量或結(jié)構(gòu)體成員時(shí)表示使用最小可能的對(duì)齊,用于枚舉、結(jié)構(gòu)體或聯(lián)合體類(lèi)型時(shí)表示該類(lèi)型使用最小的內(nèi)存。例如:

struct example_struct {

char a;

int b;

long c __attribute__((packed));

};

編譯器對(duì)結(jié)構(gòu)體成員及變量對(duì)齊的目的是為了更快地訪問(wèn)結(jié)構(gòu)體成員及變量占據(jù)的內(nèi)存。例如,對(duì) 于一個(gè)32位的整型變量,若以4字節(jié)方式存放(即低兩位地址為00),則CPU在一個(gè)總線周期內(nèi)就可以讀取32 位;否則,CPU需要兩個(gè)總線周期才能讀取32位。

9.內(nèi)建函數(shù)

GNU C提供了大量?jī)?nèi)建函數(shù),其中大部分是標(biāo)準(zhǔn)C庫(kù)函數(shù)的GNU C編譯器內(nèi)建版本,例如memcpy()等,它們與對(duì)應(yīng)的標(biāo)準(zhǔn)C庫(kù)函數(shù)功能相同。

不屬于庫(kù)函數(shù)的其他內(nèi)建函數(shù)的命名通常以__builtin開(kāi)始,如下所示。

內(nèi)建函數(shù)__builtin_return_address(LEVEL)返回當(dāng)前函數(shù)或其調(diào)用者的返回地址,參數(shù)LEVEL指定調(diào)用棧的級(jí)數(shù),如0表示當(dāng)前函數(shù)的返回地址,1表示當(dāng)前函數(shù)的調(diào)用者的返回地址。

內(nèi)建函數(shù)__builtin_constant_p(EXP)用于判斷一個(gè)值是否為編譯時(shí)常數(shù),如果參數(shù)EXP的值是常數(shù),函數(shù)返回1,否則返回0。例如,下面的代碼可檢測(cè)第1個(gè)參數(shù)是否為編譯時(shí)常數(shù)以確定采用參數(shù)版本還是非參數(shù)版本:

#define test_bit(nr,addr)

(__builtin_constant_p(nr)

constant_test_bit((nr),(addr)) :

variable_test_bit((nr),(addr)))

內(nèi)建函數(shù)__builtin_expect(EXP,C)用于為編譯器提供分支預(yù)測(cè)信息,其返回值是整數(shù)表達(dá)式EXP的值,C的 值必須是編譯時(shí)常數(shù)。

Linux內(nèi)核編程時(shí)常用的likely()和unlikely()底層調(diào)用的likely_notrace()、unlikely_notrace()就是基于 __builtin_expect(EXP,C)實(shí)現(xiàn)的。

#define likely_notrace(x) __builtin_expect(?。。▁), 1) #define unlikely_notrace(x) __builtin_expect(?。。▁), 0)

若代碼中出現(xiàn)分支,則即可能中斷流水線,我們可以通過(guò)likely()和unlikely()暗示分支容易成立還是不容易 成立,例如:

if (likely(!IN_DEV_ROUTE_LOCALNET(in_dev)))

if (ipv4_is_loopback(saddr))

goto e_inval;

在使用gcc編譯C程序的時(shí)候,如果使用“-ansi–pedantic”編譯選項(xiàng),則會(huì)告訴編譯器不使用GNU擴(kuò)展語(yǔ)法。例如對(duì) 于如下C程序test.c:

struct var_data {

int len;

char data[0];

};

struct var_data a;

直接編譯可以通過(guò):

gcc -c test.c

如果使用“-ansi–pedantic”編譯選項(xiàng),編譯會(huì)報(bào)警:

gcc -ansi -pedantic -c test.c

test.c:3: warning: ISO C forbids zero-size array ‘data’

編輯:jq

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11312

    瀏覽量

    209711

原文標(biāo)題:Linux GNU C 與 ANSI C 有什么區(qū)別?

文章出處:【微信號(hào):pcbgood,微信公眾號(hào):奈因PCB電路板設(shè)計(jì)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    同樣是函數(shù),在CC++中有什么區(qū)別

    同樣是函數(shù),在 CC++ 中有什么區(qū)別? 第一個(gè)返回值。 C語(yǔ)言的函數(shù)可以不寫(xiě)返回值類(lèi)型,編譯器會(huì)默認(rèn)為返回 int。 但是 C++
    的頭像 發(fā)表于 11-29 10:25 ?334次閱讀

    C語(yǔ)言和C++中結(jié)構(gòu)體的區(qū)別

    同樣是結(jié)構(gòu)體,看看在C語(yǔ)言和C++中有什么區(qū)別
    的頭像 發(fā)表于 10-30 15:11 ?247次閱讀

    GNU構(gòu)建裸機(jī)系統(tǒng)

    基于AT91SAM7S平臺(tái),介紹裸機(jī)開(kāi)發(fā),以閃燈為藍(lán)本,涉及匯編、鏈接、C/C++、中斷等。   無(wú)處不在的ARM處理器家族得到了GNU C/
    發(fā)表于 10-16 17:34 ?0次下載

    linux開(kāi)發(fā)板和單片機(jī)開(kāi)發(fā)的區(qū)別

    硬件架構(gòu) Linux開(kāi)發(fā)板和單片機(jī)開(kāi)發(fā)在硬件架構(gòu)上有很大的區(qū)別。Linux開(kāi)發(fā)板通常基于ARM、x86或其他處理器架構(gòu),具有較高的處理能力和內(nèi)存容量。而單片機(jī)開(kāi)發(fā)則基于微控制器,如8051、AVR
    的頭像 發(fā)表于 08-30 15:30 ?923次閱讀

    PLC編程語(yǔ)言和C語(yǔ)言的區(qū)別

    在工業(yè)自動(dòng)化和計(jì)算機(jī)編程領(lǐng)域中,PLC(可編程邏輯控制器)編程語(yǔ)言和C語(yǔ)言各自扮演著重要的角色。盡管兩者都是編程語(yǔ)言,但它們?cè)诙鄠€(gè)方面存在顯著的區(qū)別。本文將從多個(gè)維度深入探討PLC編程語(yǔ)言和C語(yǔ)言的
    的頭像 發(fā)表于 06-14 17:11 ?2889次閱讀

    USB-C和TYPE-C有哪些區(qū)別

    USB-C和TYPE-C都是現(xiàn)代電子設(shè)備中常見(jiàn)的接口類(lèi)型,它們?cè)谕庥^和功能上具有一定的相似性,但也存在一些區(qū)別。本文將從多個(gè)方面對(duì)USB-C和TYPE-
    的頭像 發(fā)表于 04-03 15:33 ?5646次閱讀
    USB-<b class='flag-5'>C</b>和TYPE-<b class='flag-5'>C</b>有哪些<b class='flag-5'>區(qū)別</b>

    在STM32中,通信串口USART與I2C之間有啥原理上的區(qū)別?二者之間又有什么聯(lián)系?

    請(qǐng)問(wèn)一下,在STM32中,通信串口USART與I2C之間有啥原理上的區(qū)別?二者之間又有什么聯(lián)系?對(duì)于所有的通信之間,又存在什么樣的關(guān)聯(lián)?
    發(fā)表于 03-25 07:27

    type-c和usb-c有什么區(qū)別

    type-c和usb-c有什么區(qū)別? Type-C 和 USB-C 都是指同一個(gè)連接標(biāo)準(zhǔn),它使用統(tǒng)一的連接器,在電子設(shè)備
    的頭像 發(fā)表于 02-19 10:00 ?2651次閱讀

    plc編程語(yǔ)言與c語(yǔ)言的聯(lián)系 c語(yǔ)言和PLC有什么區(qū)別

    語(yǔ)言,主要用于開(kāi)發(fā)各種應(yīng)用程序。盡管PLC編程語(yǔ)言和C語(yǔ)言有一些相似之處,但它們之間也存在一些明顯的區(qū)別。 首先,PLC編程語(yǔ)言和C語(yǔ)言都是基于結(jié)構(gòu)化編程原則的。它們都支持變量聲明和使
    的頭像 發(fā)表于 02-05 14:21 ?4183次閱讀

    c語(yǔ)言,c++,java,python區(qū)別

    C語(yǔ)言、C++、Java和Python是四種常見(jiàn)的編程語(yǔ)言,各有優(yōu)點(diǎn)和特點(diǎn)。 C語(yǔ)言: C語(yǔ)言是一種面向過(guò)程的編程語(yǔ)言。它具有底層的特性,能夠?qū)τ?jì)算機(jī)硬件進(jìn)行直接操作。
    的頭像 發(fā)表于 02-05 14:11 ?2430次閱讀

    vb語(yǔ)言和c++語(yǔ)言的區(qū)別

    VB語(yǔ)言和C++語(yǔ)言是兩種不同的編程語(yǔ)言,雖然它們都屬于高級(jí)編程語(yǔ)言,但在設(shè)計(jì)和用途上有很多區(qū)別。下面將詳細(xì)比較VB語(yǔ)言和C++語(yǔ)言的區(qū)別。 設(shè)計(jì)目標(biāo): VB語(yǔ)言(Visual Bas
    的頭像 發(fā)表于 02-01 10:20 ?2368次閱讀

    C++在Linux內(nèi)核開(kāi)發(fā)中從爭(zhēng)議到成熟

    Linux 內(nèi)核郵件列表中一篇已有六年歷史的老帖近日再次引發(fā)激烈討論 —— 主題是建議將 Linux 內(nèi)核的開(kāi)發(fā)語(yǔ)言從 C 轉(zhuǎn)換為更現(xiàn)代的 C++。
    的頭像 發(fā)表于 01-31 14:11 ?642次閱讀
    <b class='flag-5'>C</b>++在<b class='flag-5'>Linux</b>內(nèi)核開(kāi)發(fā)中從爭(zhēng)議到成熟

    usb-c和type-c區(qū)別一樣嗎

    USB-C 和 Type-C 是同一種接口的兩種叫法,它們沒(méi)有區(qū)別。即 USB-C 和 Type-C 是同義詞,用來(lái)描述一種連接接口標(biāo)準(zhǔn)。
    的頭像 發(fā)表于 01-31 10:44 ?6457次閱讀

    賽昉系列:OK7110-C_Qt5.15.2+Linux5.15.0_編譯手冊(cè)_V1.0

    OK7110-C_Qt5.15.2+Linux5.15.0_編譯手冊(cè)_V1.0_20230820
    發(fā)表于 01-23 16:23 ?0次下載

    USB–C和type–C一樣嗎 type-c和usb-c有什么區(qū)別

    小巧、可逆插拔以及支持多種功能而得到了廣泛的應(yīng)用。 USB-C連接器相較于傳統(tǒng)的USB連接器有以下幾個(gè)方面的區(qū)別: 外形設(shè)計(jì):USB-C連接器的外形更加小巧,比傳統(tǒng)的Type-A和Type-B連接器要
    的頭像 發(fā)表于 01-18 09:52 ?9352次閱讀