RM新时代网站-首页

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

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

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

什么是__attribute__?嵌入式C代碼屬性怎么定義?

Dp1040 ? 來(lái)源:漫談嵌入式 ? 2023-10-13 15:55 ? 次閱讀

嵌入式開(kāi)發(fā),離不開(kāi) C 語(yǔ)言,C語(yǔ)言中有很多語(yǔ)法會(huì)直接或間接影響你代碼的質(zhì)量,下面就來(lái)講講__attribute__ 關(guān)鍵字的用法。

1. 什么是 __attribute__

GNU C 編譯器增加了一個(gè) __attribute__ 關(guān)鍵字用來(lái)聲明一個(gè)函數(shù)、變量或類(lèi)型的特殊屬性。申明這些屬性主要用途就是指導(dǎo)編譯程序進(jìn)行特定方面的優(yōu)化或代碼檢查。

__attrabute__ 的用法非常簡(jiǎn)單,當(dāng)我們定義一個(gè)一個(gè)函數(shù)、變量或者類(lèi)型時(shí),直接在他名字旁邊添加如下屬性即可:

__attribute__((ATTRIBUTE))

需要注意的是,__attribute__ 后面是兩對(duì)小括號(hào),不能圖方便只寫(xiě)一對(duì),否則會(huì)編譯報(bào)錯(cuò)。括號(hào)里的 ATTRIUBTE 表示要聲明的屬性,目前支持十幾種屬性聲明:

section:自定義段

aligned:對(duì)齊

packed:對(duì)齊

format:檢查函數(shù)變參格式

weak:弱聲明

alias:函數(shù)起別名

noinline:無(wú)內(nèi)聯(lián)

always_inline:內(nèi)聯(lián)函數(shù)總是展開(kāi)

......

比如:

charc __attribute__((algined(8)))=4;
intglobal_val __attribute__((section(".data")));

當(dāng)然,我們對(duì)一個(gè)變量也可以同時(shí)添加多個(gè)屬性。在定義變量前,各個(gè)屬性之間用逗號(hào)隔開(kāi)。以下三種聲明方式是沒(méi)有問(wèn)題的。

charc__attribute__((packed,algined(4)));
charc__attribute__((packed,algined(4)))=4;
__attribute__((packed,algined(4)))charc=4;

2. 屬性聲明:section

section 屬性的主要作用是:在程序編譯時(shí),將一個(gè)函數(shù)或者變量放到指定的段,即指定的section 中。

一個(gè)可執(zhí)行文件注意由代碼段,數(shù)據(jù)段、BSS 段構(gòu)成。代碼段主要用來(lái)存放編譯生成的可執(zhí)行指令代碼、數(shù)據(jù)段和BSS段用來(lái)存放全局變量和未初始化的全局變量。

除了這三個(gè)段,可執(zhí)行文件還包含一些其他的段。我們可以用 readelf 去查看一個(gè)可執(zhí)行文件各個(gè)section信息

下表是不同的 section 及說(shuō)明:

section 組成
代碼段(.text) 函數(shù)定義、程序語(yǔ)句
數(shù)據(jù)段 (.data) 初始化的全局變量、初始化的靜態(tài)局部變量
BSS 段(.bss) 未初始化的全局變量,未初始化的靜態(tài)局部變量

intglobal_val=8;
intunint_val;

intmain(void)
{
return0;
}

我們使用gcc 編譯這個(gè)程序:

gcc-m32-o a.out gnu.c

查看符表號(hào)信息:

#readelf-s a.out
Num:ValueSize TypeBindVisNdx Name
44:0804c0204OBJECTGLOBAL DEFAULT24unint_val
45:080490904FUNCGLOBAL HIDDEN13__x86.get_pc_thunk.bx
46:0804c0100NOTYPEWEAKDEFAULT23data_start
47:0804c01c0NOTYPEGLOBAL DEFAULT23_edata
48:080491c40FUNCGLOBAL HIDDEN14_fini
49:0804c0184OBJECTGLOBAL DEFAULT23global_val
50:0804c0100NOTYPEGLOBAL DEFAULT23__data_start
51:000000000NOTYPEWEAKDEFAULTUND __gmon_start__
52:0804c0140OBJECTGLOBAL HIDDEN23__dso_handle
53:0804a0044OBJECTGLOBAL DEFAULT15_IO_stdin_used
54:000000000FUNCGLOBAL DEFAULTUND __libc_start_main@@GLIBC_
55:0804916085FUNCGLOBAL DEFAULT13__libc_csu_init
56:0804c0240NOTYPEGLOBAL DEFAULT24_end
57:080490801FUNCGLOBAL HIDDEN13_dl_relocate_static_pie
58:0804904055FUNCGLOBAL DEFAULT13_start
59:0804a0004OBJECTGLOBAL DEFAULT15_fp_hw
60:0804c01c0NOTYPEGLOBAL DEFAULT24__bss_start
61:0804915210FUNCGLOBAL DEFAULT13main

查看 section 信息:

#readelf-S a.out


c1f7f0ea-699d-11ee-939d-92fbcf53809c.png

使用 __attribute__ ((section("xxx"))),修改段的屬性。

intglobal_val=0;
intunint_val __attribute__((section(".data")));

intmain()
{
return0;
}

可以看到 unint_val 這個(gè)變量,已經(jīng)被編譯器放在數(shù)據(jù)段中。當(dāng)然也可以自定義段的名稱(chēng)。

c20d2d2a-699d-11ee-939d-92fbcf53809c.png

3. 屬性聲明:aligned

GNU C 通過(guò) __attribute__ 來(lái)聲明 aligned 和 packed 屬性,指定一個(gè)變量或類(lèi)型的對(duì)齊方式。

通過(guò) aligned 屬性,我們可以顯示地指定變量 a 在內(nèi)存中的地址對(duì)齊方式。aligned 有一個(gè)參數(shù),表示要按幾個(gè)字節(jié)對(duì)齊,使用時(shí)要注意,地址對(duì)齊的字節(jié)數(shù)必須是 2 的冪次方,否則編譯就會(huì)報(bào)錯(cuò)。

3.1 地址對(duì)齊

#include

inta=1;
intb=2;
charc1=3;
charc2=4;
intmain()
{
printf("a=%p
",&a);
printf("b=%p
",&b);
printf("c1=%p
",&c1);
printf("c2=%p
",&c2);

return0;
}

可以看到,char 占一個(gè)字節(jié),c2的地址緊挨著 c1

a=0x404030
b=0x404034
c1=0x404038
c2=0x404039

使用 aligned 地址對(duì)齊

#include

inta=1;
intb=2;
charc1=3;
charc2\__attribute__((aligned(4)))=4;
intmain()
{
printf("a=%p
",&a);
printf("b=%p
",&b);
printf("c1=%p
",&c1);
printf("c2=%p
",&c2);

return0;
}

可以看到,c2 的地址是按照4字節(jié)對(duì)齊

a=0x404030
b=0x404034
c1=0x404038
c2=0x40403c

通過(guò) aligned 屬性聲明,雖然可以顯示的指定變量地址的對(duì)齊方式,但是也會(huì)因?yàn)檫吔鐚?duì)齊造成一定的內(nèi)存空間浪費(fèi)。

地址對(duì)齊的好處是,為了配合計(jì)算機(jī)硬件設(shè)計(jì),可以簡(jiǎn)化CPU和內(nèi)存RAM之間的接口和硬件設(shè)計(jì)。

例如,一個(gè)32位的計(jì)算機(jī)操作系統(tǒng),在CPU讀取內(nèi)存時(shí),硬件設(shè)計(jì)上可能只支持4字節(jié)或者4字節(jié)倍數(shù)對(duì)齊地址訪問(wèn),CPU 每次向 RAM 讀寫(xiě)數(shù)據(jù)時(shí),一個(gè)周期可以讀寫(xiě)4字節(jié)。如果我們把一個(gè)int型數(shù)據(jù)就放在4字節(jié)對(duì)齊的地址上,那么CPU就可以一次性把數(shù)據(jù)讀取完畢,否則可能需要讀取兩次。

3.2 結(jié)構(gòu)體對(duì)齊

結(jié)構(gòu)體作為一種復(fù)雜的數(shù)據(jù)類(lèi)型,編譯器在給一個(gè)結(jié)構(gòu)體變量分配存儲(chǔ)空間時(shí),不僅要考慮結(jié)構(gòu)體內(nèi)各個(gè)成員的對(duì)齊,還要考慮結(jié)構(gòu)體整體的對(duì)齊。

為了結(jié)構(gòu)體各成員對(duì)齊,編譯器可能會(huì)在結(jié)構(gòu)體內(nèi)填充一些字節(jié)。為了結(jié)構(gòu)體的整體對(duì)齊,編譯器可能會(huì)在結(jié)構(gòu)體的末尾一些空間。

#include

structdata{
chara;
intb;
short c;
};

intmain()
{
structdatas;
printf("size=%d
",sizeof(s));
printf("a=%p
",&s.a);
printf("b=%p
",&s.b);
printf("c=%p
",&s.c);

return0;
}

四字節(jié)對(duì)齊:占12字節(jié)

size=12
a=0xffb6c374
b=0xffb6c378
c=0xffb6c37c

結(jié)構(gòu)體成員順序不同,所占大小有可能不同:

#include

structdata{
chara;
short b;
intc;
};

intmain()
{
structdatas;
printf("size=%d
",sizeof(s));
printf("a=%p
",&s.a);
printf("b=%p
",&s.b);
printf("c=%p
",&s.c);

return0;
}

四字節(jié)對(duì)齊:占8字節(jié)

size=8
a=0xffa2d9f8
b=0xffa2d9fa
c=0xffa2d9fc

顯示的指定成員的對(duì)齊方式:

#include

structdata{
chara;
short b __attribute__((aligned(4)));
intc;
};

intmain()
{
structdatas;
printf("size=%d
",sizeof(s));
printf("a=%p
",&s.a);
printf("b=%p
",&s.b);
printf("c=%p
",&s.c);

return0;
}

四字節(jié)對(duì)齊:占12字節(jié)

size=12
a=0xffb6c374
b=0xffb6c378
c=0xffb6c37c

顯示指定結(jié)構(gòu)體對(duì)齊方式:

#include

structdata{
chara;
short b;
intc;
}__attribute__((aligned(16)));

intmain()
{
structdatas;
printf("size=%d
",sizeof(s));
printf("a=%p
",&s.a);
printf("b=%p
",&s.b);
printf("c=%p
",&s.c);

return0;
}

16字節(jié)對(duì)齊,末尾填充8字節(jié):占16字節(jié)

size=16
a=0xffa2d9f8
b=0xffa2d9fa
c=0xffa2d9fc

3.3 編譯器一定會(huì)按照 aligend 指定的方式對(duì)齊嗎?

我們通過(guò)這個(gè)屬性聲明,其實(shí)只是建議編譯器按照這種大小地址對(duì)齊,但不能超過(guò)編譯器允許的最大值。一個(gè)編譯器,對(duì)每個(gè)基本的數(shù)據(jù)類(lèi)型都有默認(rèn)的最大邊界對(duì)齊字節(jié)數(shù),如果超過(guò)了,則編譯器只能按照它規(guī)定的最大對(duì)齊字節(jié)數(shù)來(lái)對(duì)變量分配地址。

4. 屬性聲明:packed

aligned 屬性一般用來(lái)增大變量的地址對(duì)齊,元素之間地址對(duì)齊會(huì)造成一定的內(nèi)存空洞,而packed屬性則正好相反,一般用來(lái)減少地址對(duì)齊,指定變量或類(lèi)型使用最可能小的地址對(duì)齊方式。

顯示的對(duì)結(jié)構(gòu)體成員使用packed

#include
structdata{
chara;
short b __attribute__((packed));
intc __attribute__((packed));
};
intmain()
{
structdatas;
printf("size=%d
",sizeof(s));
printf("a=%p
",&s.a);
printf("b=%p
",&s.b);
printf("c=%p
",&s.c);

return0;
}

使用最小一字節(jié)對(duì)齊:

size=7
a=0xfff38fb9
b=0xfff38fba
c=0xfff38fbc

對(duì)整個(gè)結(jié)構(gòu)體添加packed屬性

structdata{
chara;
short b;
intc;
}__attribute__((packed));

內(nèi)核中的packed、aligned 聲明

在內(nèi)核源碼中,我們經(jīng)常看到aligned 和 packed 一起使用,即對(duì)一個(gè)變量或者類(lèi)型同時(shí)使用packed 和 aligned 屬性聲明。這樣做的好處是即避免了結(jié)構(gòu)體各成員間地址對(duì)齊產(chǎn)生的內(nèi)存空洞,又指定了整個(gè)結(jié)構(gòu)體的對(duì)齊方式。

structdata{
chara;
short b;
intc;
}__attribute__((packed,aligned(8)));

5. 屬性聲明:format

GNU 通過(guò) __attribute__ 擴(kuò)展的 format 屬性,來(lái)指定變參函數(shù)的參數(shù)格式檢查。

它的使用方法如下:

__attribute__((format(archetype,string-index,frist-to-check)))
voidLOG(constchar*fmt,...)__attribute__((format(printf,1,2)));

屬性format(printf,1,2) 有3各參數(shù),第一個(gè)參數(shù)pritnf 是告訴編譯器,按照printf的標(biāo)準(zhǔn)來(lái)檢查;第二個(gè)參數(shù)表示LOG()函數(shù)所有的參數(shù)列表中格式字符串的位置索引,第三個(gè)參數(shù)是告訴編譯器要檢查的參數(shù)的起始位置。

LOG("hello world,i am%d ages
",age);/*前者表示格式字符串,后者表示所有的參數(shù)*/

6. 屬性聲明:weak

GNU C 通過(guò) weak 屬性聲明,可以將一個(gè)強(qiáng)符號(hào),轉(zhuǎn)換為弱符號(hào)。使用方法如下:

void__attribute__((weak))func(void);
intnum __attribute__((weak));

在一個(gè)程序中,無(wú)論是變量名,還是函數(shù)名,在編譯器眼里,就是一個(gè)符號(hào)而已,符號(hào)可以分為強(qiáng)符號(hào)和弱符號(hào)。

強(qiáng)符號(hào):函數(shù)名,初始化的全局變量名

弱符號(hào):未初始化的全局變量名。

在一個(gè)工程項(xiàng)目中,對(duì)于相同的全局變量名、函數(shù)名,我們一般可以歸結(jié)為以下3種場(chǎng)景:

強(qiáng)符號(hào) + 強(qiáng)符號(hào)

強(qiáng)符號(hào) + 弱符號(hào)

弱符號(hào) + 弱符號(hào)

強(qiáng)符號(hào)和弱符號(hào)主要用來(lái)解決在程序鏈接過(guò)程中,出現(xiàn)多個(gè)同名全局變量、同名函數(shù)的沖突問(wèn)題,一般我們遵循以下3個(gè)原則:

一山不容二虎

強(qiáng)弱可以共處

體積大者勝出

在一個(gè)項(xiàng)目中,不可能同時(shí)存在兩個(gè)強(qiáng)符號(hào)。如果在一個(gè)多文件的項(xiàng)目中定義兩個(gè)同名的函數(shù)后者全局變量,那么連接器在鏈接時(shí)就會(huì)報(bào)重定義錯(cuò)誤。

但是,在一個(gè)工程中允許強(qiáng)符號(hào)和弱符號(hào)同時(shí)存在,比如可以定義一個(gè)初始化的全局變量和一個(gè)未初始化的全局變量,這種寫(xiě)法在編譯時(shí)是可以編過(guò)的。

編譯器對(duì)這種同名符號(hào)沖突時(shí),在做符號(hào)決議時(shí),一般會(huì)選擇強(qiáng)符號(hào),丟掉弱符號(hào)。

還有一種情況是,在一個(gè)工程中,當(dāng)都是弱符號(hào)時(shí),那么編譯器該選擇哪個(gè)呢?誰(shuí)在內(nèi)存中存儲(chǔ)空間大,就選誰(shuí)。

變量的弱符號(hào)與強(qiáng)符號(hào)

//func1.c
inta=1;
intb;
voidfunc(void)
{
printf("func.a=%d
",a);
printf("func.b=%d
",b);
}
//main.c
inta;
intb=2;
voidfunc();

intmain()
{
printf("main.a=%d
",a);
printf("main.b=%d
",b);
func();

return0;
}

編譯后,程序運(yùn)行結(jié)果如下??梢钥闯龃蛴〉亩际菑?qiáng)符號(hào)的值。

main.a=1
main.b=2
func.a=1
func.b=2

一般不建議在一個(gè)工程中定義多個(gè)不同類(lèi)型的同名弱符號(hào),編譯時(shí)可能會(huì)出現(xiàn)各種各樣的問(wèn)題。也不能同時(shí)定義兩個(gè)同名的強(qiáng)符號(hào),否則會(huì)報(bào)重定義錯(cuò)誤。我們可以使用GNU C 的擴(kuò)展 weak 屬性,將一個(gè)強(qiáng)符號(hào)轉(zhuǎn)換為弱符號(hào)。

inta __attribute__((weak))=1;

函數(shù)的強(qiáng)符號(hào)與弱符號(hào)

鏈接器對(duì)于同名的函數(shù)沖突,同樣遵循相同的規(guī)則。函數(shù)名本身是一個(gè)強(qiáng)符號(hào),在一個(gè)工程中定義兩個(gè)同名的函數(shù),編譯器肯定會(huì)報(bào)重定義錯(cuò)誤。但是,我們可以通過(guò)weak 屬性聲明,將其中的一個(gè)函數(shù)名轉(zhuǎn)換為弱符號(hào)。

//func1.c
inta __attribute__((weak))=1;
voidfunc(void)
{
printf("func.a=%d
",a);
}

//main.c
inta=4;
void__attribute__((weak))func(void)
{
printf("main.a=%d
",a);
}

intmain(void)
{
func();
return0;
}

弱符號(hào)的用途

在一個(gè)源文件中引用一個(gè)編號(hào)或者函數(shù),當(dāng)編譯器只看到聲明,而沒(méi)看到其定義時(shí),一般編譯時(shí)不會(huì)報(bào)錯(cuò)。在鏈接階段,鏈接器會(huì)到其他文件中找到這些符號(hào)的定義,若未找到,則報(bào)未定義錯(cuò)誤。

當(dāng)函數(shù)被聲明一個(gè)弱符號(hào)時(shí),會(huì)有一個(gè)奇特地方:當(dāng)鏈接器找不到這個(gè)函數(shù)的定義時(shí),也不會(huì)報(bào)錯(cuò)。編譯器會(huì)將這個(gè)函數(shù)名,即弱符號(hào),設(shè)置為0或者一個(gè)特殊值。只有當(dāng)程序運(yùn)行時(shí),調(diào)用到這個(gè)函數(shù),跳轉(zhuǎn)到零地址或者一個(gè)特殊的地址才會(huì)報(bào)錯(cuò)誤,產(chǎn)生一個(gè)內(nèi)存錯(cuò)誤。

如果我們?cè)谑褂煤瘮?shù)前,判斷這個(gè)函數(shù)地址是否為0,即可避免段錯(cuò)誤。你會(huì)發(fā)現(xiàn),即使函數(shù)未定義也可以正常編過(guò)。

弱符號(hào)的這個(gè)特性在庫(kù)函數(shù)開(kāi)發(fā)設(shè)計(jì)中應(yīng)用十分廣泛,如果在開(kāi)發(fā)一個(gè)庫(kù)時(shí),基礎(chǔ)功能已經(jīng)實(shí)現(xiàn),有些高級(jí)功能還未實(shí)現(xiàn),那么你就可以將這些函數(shù)通過(guò)weak 屬性聲明轉(zhuǎn)換為一個(gè)弱符號(hào)。

7. 屬性聲明:alias

GNU C 擴(kuò)展了一個(gè) alias 屬性,這個(gè)屬性很簡(jiǎn)單,主要用來(lái)給函數(shù)定義一個(gè)別名。

void__f(void)
{
printf("__f
");
}

voidf(void)__attribute__((alias("__f")));

intmain(void)
{
f();
return0;
}

Linux 內(nèi)核中你會(huì)發(fā)現(xiàn)alias有時(shí)候會(huì)和weak屬性一起使用。如有些接口隨著內(nèi)核版本升級(jí),函數(shù)接口發(fā)生了變化,我們可以通過(guò)alias屬性對(duì)舊的接口名字進(jìn)行封裝,重新起一個(gè)接口名字。

//f.c
void__f(void)
{
printf("__f
");
}

voidf()__attribute__((weak,alias("__f")));

//main.c
void__attribute__((weak))f(void);
voidf(void)
{
printf("f
");
}

intmain()
{
f();
return0;
}

如果我們?cè)趍ain.c 中定義了f()函數(shù),那么main 函數(shù)調(diào)用f()會(huì)調(diào)用薪定義的函數(shù),否則調(diào)用__f()函數(shù)。

8. 屬性聲明:noinline 和 always_inline

8.1 什么是內(nèi)聯(lián)函數(shù)

說(shuō)起內(nèi)聯(lián)函數(shù),就不得不說(shuō)起函數(shù)調(diào)用開(kāi)銷(xiāo)。一個(gè)函數(shù)在執(zhí)行過(guò)程中,如果要調(diào)用其他函數(shù),則一般會(huì)執(zhí)行以下過(guò)程:

保存當(dāng)前函數(shù)現(xiàn)場(chǎng)

跳到調(diào)用函數(shù)執(zhí)行

恢復(fù)當(dāng)前函數(shù)現(xiàn)場(chǎng)

繼續(xù)執(zhí)行當(dāng)前函數(shù)

對(duì)于一些短小精悍,并且調(diào)用頻繁的函數(shù),調(diào)用開(kāi)銷(xiāo)大,這個(gè)時(shí)候我們可以將函數(shù)聲明為內(nèi)聯(lián)函數(shù)。編譯器遇到內(nèi)聯(lián)函數(shù)會(huì)想宏一樣將內(nèi)聯(lián)函數(shù)之間在調(diào)用處展開(kāi),這樣做就減少了函數(shù)調(diào)用的開(kāi)銷(xiāo)。

8.2 內(nèi)聯(lián)函數(shù)與宏

與宏相比,內(nèi)聯(lián)函數(shù)有以下優(yōu)勢(shì):

參數(shù)類(lèi)型檢查:內(nèi)聯(lián)函數(shù)本質(zhì)上還是一個(gè)函數(shù),在編譯過(guò)程中編譯器會(huì)對(duì)齊進(jìn)行參數(shù)檢查,而宏不具備這個(gè)特性。

便于調(diào)試:函數(shù)支持的調(diào)試功能有斷點(diǎn)、單步等。

返回值:內(nèi)聯(lián)函數(shù)有返回值。這個(gè)優(yōu)勢(shì)是相對(duì)于ANSI C 說(shuō)的。因?yàn)楝F(xiàn)在的宏也有返回值和類(lèi)型了,如使用語(yǔ)句表達(dá)式定義的宏。

接口封裝:有些內(nèi)聯(lián)函數(shù)可以用來(lái)封裝一個(gè)接口,而宏不具備這個(gè)特性。

8.3 編譯器對(duì)內(nèi)聯(lián)函數(shù)的處理

我們雖然可以通過(guò)inline 關(guān)鍵字將一個(gè)函數(shù)聲明為一個(gè)內(nèi)聯(lián)函數(shù),但是編譯器不一定會(huì)對(duì)這個(gè)函數(shù)內(nèi)聯(lián)展開(kāi)。編譯器也要根據(jù)實(shí)際情況進(jìn)行評(píng)估,權(quán)衡展開(kāi)和不展開(kāi)的利弊,并最終決定要不要展開(kāi)。

內(nèi)聯(lián)函數(shù)并不是完美的,也有一些缺點(diǎn)。內(nèi)聯(lián)函數(shù)會(huì)增大程序的體積。

一般而言判斷一個(gè)內(nèi)聯(lián)函數(shù)是否展開(kāi),從程序員的角度主要從以下幾點(diǎn)出發(fā):

函數(shù)體積小

函數(shù)體內(nèi)無(wú)指針賦值、遞歸、循環(huán)語(yǔ)句等

調(diào)用頻繁

當(dāng)我們認(rèn)為一個(gè)函數(shù)體積小、而且被大量調(diào)用,應(yīng)做內(nèi)聯(lián)展開(kāi)時(shí),就可以使用static inline 關(guān)鍵字修飾它,但是編譯器不一定會(huì)內(nèi)聯(lián)展開(kāi)。如果想明確告訴編譯器一定要展開(kāi),或者不展開(kāi)就可以使用 noinline 和 always_inline 對(duì)函數(shù)的屬性做一個(gè)聲明。

8.4 內(nèi)聯(lián)函數(shù)為什么定義在頭文件中?

在Linux 內(nèi)核中,你會(huì)看到大量的內(nèi)聯(lián)函數(shù)被定義在頭文件中,而且常常使用static關(guān)鍵字修飾。

為什么定義在頭文件中呢?因?yàn)樗且粋€(gè)內(nèi)聯(lián)函數(shù),可以像宏一樣使用,在任何想使用內(nèi)聯(lián)函數(shù)的源文件中,都不必親自在定義一遍,直接包含這個(gè)頭文件即可。

為什么還要用static 修飾呢?因?yàn)槭褂胕nline關(guān)鍵字定義的內(nèi)聯(lián)函數(shù),編譯器不一定會(huì)內(nèi)聯(lián)展開(kāi),那么當(dāng)一個(gè)工程中多個(gè)頭文件包含這個(gè)內(nèi)聯(lián)函數(shù)的定義時(shí),編譯時(shí)就可能報(bào)重復(fù)定義的錯(cuò)誤。使用satic 關(guān)鍵字修飾,則可以限定這個(gè)函數(shù)的作用域在各自的源文件內(nèi),避免重復(fù)定義的發(fā)生。

9. 總結(jié)

本文主要介紹了 GNU C 的擴(kuò)展語(yǔ)法 __attributr__ 關(guān)鍵字,并對(duì)其中常用的屬性聲明做了詳細(xì)的介紹:

section

packed

aligned

format

alias

weak

noinline

always_inline






審核編輯:劉清

聲明:本文內(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)投訴
  • RAM
    RAM
    +關(guān)注

    關(guān)注

    8

    文章

    1368

    瀏覽量

    114640
  • C語(yǔ)言
    +關(guān)注

    關(guān)注

    180

    文章

    7604

    瀏覽量

    136684
  • 嵌入式開(kāi)發(fā)

    關(guān)注

    18

    文章

    1028

    瀏覽量

    47563
  • BSS
    BSS
    +關(guān)注

    關(guān)注

    0

    文章

    18

    瀏覽量

    12210
  • GNU
    GNU
    +關(guān)注

    關(guān)注

    0

    文章

    143

    瀏覽量

    17492

原文標(biāo)題:嵌入式C代碼屬性怎么定義?

文章出處:【微信號(hào):玩點(diǎn)嵌入式,微信公眾號(hào):玩點(diǎn)嵌入式】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    嵌入式C語(yǔ)言的弱符號(hào)和弱引用

    總之,__attribute__ 起到了給編譯器提供上下文的作用,如果錯(cuò)誤的使用 __attribute__ 指令,因?yàn)榻o編譯器提供了錯(cuò)誤的上下文,由此引起的錯(cuò)誤通常很難被發(fā)現(xiàn)。
    發(fā)表于 12-23 10:36 ?376次閱讀

    求助,請(qǐng)問(wèn)一個(gè)結(jié)構(gòu)體如何全部定義到 __attribute__ 區(qū)域?

    請(qǐng)問(wèn)一個(gè)結(jié)構(gòu)體如何全部定義到 __attribute__ 區(qū)域? 例如我這里涉及到一些高速計(jì)算的緩存,計(jì)劃將緩存數(shù)據(jù)存儲(chǔ)到 __attribute__ 區(qū)域。 三個(gè)結(jié)構(gòu)體 ,每個(gè)結(jié)構(gòu)體的數(shù)據(jù)大小為 4K *uint16t 這
    發(fā)表于 01-16 07:29

    __ATTRIBUTE__ 你知多少?

    GNU C 的一大特色就是__attribute__ 機(jī)制。__attribute__ 可以設(shè)置函數(shù)屬性(Func[color=rgb(68, 68, 68) !important]t
    發(fā)表于 09-05 11:12

    attribute用法section部分的資料大合集

    轉(zhuǎn)載:http://blog.sina.com.cn/s/blog_5e11a56a0100c8h5.html###1. gcc的__attribute__編譯屬性要了解Linux Kernel
    發(fā)表于 11-25 08:25

    來(lái)了解一下GNU C __attribute__機(jī)制

    mystr{int16_t a[3];} __attribute__ ((aligned));3、sectionsection控制變量或函數(shù)在編譯時(shí)的段名。在嵌入式軟件開(kāi)發(fā)時(shí)用的非常多,比如有外擴(kuò)
    發(fā)表于 03-03 15:49

    怎么理解RTT中#define UNUSED __attribute__((unused))這個(gè)語(yǔ)句呢

    'serial.c line 69Project: RTT193.mcp, Target: DebugReal, Source File: serial.c應(yīng)該是不識(shí)別__attribute__((unused))這里原來(lái)應(yīng)該是
    發(fā)表于 03-29 09:27

    請(qǐng)問(wèn)GCC支持attribute at屬性嗎?

    MEM2_ALLOC_TABLE_SIZEMEM2_MAX_SIZE/MEM2_BLOCK_SIZE//內(nèi)存表大小malloc.c//內(nèi)存池(32字節(jié)對(duì)齊)__attribute__((aligned
    發(fā)表于 07-18 08:59

    嵌入式外中斷c語(yǔ)言代碼

    嵌入式外中斷c語(yǔ)言代碼(arm嵌入式開(kāi)發(fā)實(shí)例)-嵌入式外中斷c語(yǔ)言
    發(fā)表于 07-30 11:29 ?4次下載
    <b class='flag-5'>嵌入式</b>外中斷<b class='flag-5'>c</b>語(yǔ)言<b class='flag-5'>代碼</b>

    嵌入式系統(tǒng)定義

    嵌入式系統(tǒng)定義(嵌入式開(kāi)發(fā)培訓(xùn)方案)-嵌入式系統(tǒng)定義? ? ? ? ? ? ? ? ? ? ? ??
    發(fā)表于 07-30 14:27 ?12次下載
    <b class='flag-5'>嵌入式</b>系統(tǒng)<b class='flag-5'>定義</b>

    __attribute__((section(x))) 使用詳解

    無(wú)論是GNU還是ARM的編譯器,都支持__attribute__所指定的編譯屬性,這里著重講解一下在KEIL環(huán)境下__attribute__中的section的使用方法。section關(guān)鍵字可以將
    發(fā)表于 11-16 18:06 ?8次下載
    __<b class='flag-5'>attribute__</b>((section(x))) 使用詳解

    C語(yǔ)言中的__attribute__定義之section屬性

    C語(yǔ)言中的 __attribute__宏之section屬性文章目錄C語(yǔ)言中的 __attribute__宏之section
    發(fā)表于 11-16 18:21 ?47次下載
    <b class='flag-5'>C</b>語(yǔ)言中的__<b class='flag-5'>attribute__</b>宏<b class='flag-5'>定義</b>之section<b class='flag-5'>屬性</b>

    __attribute__((section(“section_name“)))使用方法

    __attribute__((section("section_name")))使用方法內(nèi)容待補(bǔ)充?。。。?/div>
    發(fā)表于 11-16 19:06 ?12次下載
    __<b class='flag-5'>attribute__</b>((section(“section_name“)))使用方法

    【STM32CubeIDE】將變量定義到指定地址

    使用Keil在使用Keil編寫(xiě)程序的時(shí)候我們可以很輕松的將變量定義到指定地址uint8_t array[1024] __attribute__((at(0x20010000
    發(fā)表于 12-27 19:08 ?28次下載
    【STM32CubeIDE】將變量<b class='flag-5'>定義</b>到指定地址

    C語(yǔ)言中__attribute__ 關(guān)鍵字的用法

    嵌入式開(kāi)發(fā),離不開(kāi) C 語(yǔ)言,C語(yǔ)言中有很多語(yǔ)法會(huì)直接或間接影響你代碼的質(zhì)量,下面就來(lái)講講__attribute__ 關(guān)鍵字的用法。 1.
    發(fā)表于 10-19 09:06 ?1.2w次閱讀

    rC語(yǔ)言__attribute__的運(yùn)用

    即,在某一個(gè)結(jié)構(gòu)體完成定義后,跟上一個(gè)__attribute__(xxx),這是GNU C的一個(gè)特色機(jī)制,使用__attribute__可以用來(lái)設(shè)置函數(shù)
    發(fā)表于 05-23 11:26 ?1067次閱讀
    RM新时代网站-首页