inline關鍵字

本文介紹了GCC和C99標準中inline使用上的不同之處。inline屬性在使用的時候,要注意以下兩點:
  1. inline關鍵字在GCC參考文檔中僅有對其使用在函數定義(Definition)上的描述,而沒有提到其是否能用于函數聲明(Declare)。

    從inline的作用來看,其放置于函數聲明中應當也是毫無作用的:inline只會影響函數在translation unit(可以簡單理解為C源碼文件)內的編譯行為,只要超出了這個范圍inline屬性就沒有任何作用了。所以inline關鍵字不應該出現在函數聲明中,沒有任何作用不說,有時還可能造成編譯錯誤(在包含了sys/compiler.h的情況下,聲明中出現inline關鍵字的部分通常無法編譯通過);

  2. inline關鍵字僅僅是建議編譯器做內聯展開處理,而不是強制。在gcc編譯器中,如果編譯優化設置為-O0,即使是inline函數也不會被內聯展開,除非設置了強制內聯(__attribute__((always_inline)))屬性。

1.?GCC的inline

gcc對C語言的inline做了自己的擴展,其行為與C99標準中的inline有較大的不同。

1.1.?static inline

GCC的static inline定義很容易理解:你可以把它認為是一個static的函數,加上了inline的屬性。這個函數大部分表現和普通的static函數一樣,只不過在調用這種函數的時候,gcc會在其調用處將其匯編碼展開編譯而不為這個函數生成獨立的匯編碼。除了以下幾種情況外:

  • 函數的地址被使用的時候。如通過函數指針對函數進行了間接調用。這種情況下就不得不為static inline函數生成獨立的匯編碼,否則它沒有自己的地址。

  • 其他一些無法展開的情況,比如函數本身有遞歸調用自身的行為等。

static inline函數和static函數一樣,其定義的范圍是local的,即可以在程序內有多個同名的定義(只要不位于同一個文件內即可)。

?注意

gcc的static inline的表現行為和C99標準的static inline是一致的。所以這種定義可以放心使用而沒有兼容性問題。

要點:

  • gcc的static inline相對于static函數來說只是在調用時建議編譯器進行內聯展開;

  • gcc不會特意為static inline函數生成獨立的匯編碼,除非出現了必須生成不可的情況(如通過函數指針調用和遞歸調用);

  • gcc的static inline函數僅能作用于文件范圍內。

1.2.?inline

相對于C99的inline來說,GCC的inline更容易理解:可以認為它是一個普通全局函數加上了inline的屬性。即在其定義所在文件內,它的表現和static inline一致:在能展開的時候會被內聯展開編譯。但是為了能夠在文件外調用它,gcc一定會為它生成一份獨立的匯編碼,以便在外部進行調用。即從文件外部看來,它和一個普通的extern的函數無異。舉個例子:

foo.c:/* 這里定義了一個inline的函數foo() */
inline foo() {...;   <- 編譯器會像非inline函數一樣為foo()生成獨立的匯編碼
}void func1() {foo(); <- 同文件內foo()可能被編譯器內聯展開編譯而不是直接call上面生成的匯編碼
}

而在另一個文件里調用foo()的時候,則直接call的是上面文件內生成的匯編碼:

bar.c:extern foo(); <- 聲明foo(),注意不能在聲明內帶inline關鍵字void func2() {foo();    <- 這里就是直接call在foo.c內為foo()函數生成的匯編碼了
}
?重要

雖然gcc的inline函數的行為很好理解,但是它和C99的inline是有很大差別的。請注意看后面對C99 inline的描述(第?2.2?節 “inline”),以及如何以兼顧GCC和C99的方式使用inline函數。

要點:

  • gcc的inline函數相對于普通extern函數來說只是在同一個文件內調用時建議編譯器進行內聯展開;

  • gcc一定會為inline函數生成一份獨立的匯編碼,以便其在本文件之外被調用。在別的文件內看來,這個inline函數和普通的extern函數無異;

  • gcc的inline函數是全局性的:在文件內可以作為一個內聯函數被內聯展開,而在文件外可以調用它。

1.3.?extern inline

GCC的static inline和inline都很好理解:看起來都像是對普通函數添加了可內聯的屬性。但是這個extern inline就千萬不能想當然地理解成就是一個extern的函數+inline屬性了。實際上gcc的extern inline十分古怪:一個extern inline的函數只會被內聯進去,而絕對不會生成獨立的匯編碼!即使是通過指針應用或者是遞歸調用也不會讓編譯器為它生成匯編碼,在這種時候對此函數的調用會被處理成一個外部引用。另外,extern inline的函數允許和外部函數重名,即在存在一個外部定義的全局庫函數的情況下,再定義一個同名的extern inline函數也是合法的。以下用例子具體說明一下extern inline的特點:

foo.c:extern inline
int foo(int a)
{return (-a);
}void func1()
{...;a = foo(a);   ①p_foo = foo;  ②b = p_foo(b); ③
}

在這個文件內,gcc不會生成foo函數的匯編碼。在func1中的調用點①,編譯器會將上面定義的foo函數在這里內聯展開編譯,其表現類似于普通inline函數。因為這樣的調用是能夠進行內聯處理的。而在②處,引用了foo函數的地址。但是注意:編譯器是絕對不會為extern inline函數生成獨立匯編碼的!所以在這種非要個函數地址不可的情況下,編譯器不得不將其處理為外部引用,在鏈接的時候鏈接到外部的foo函數去(填寫外部函數的地址)。這時如果外部沒有再定義全局的foo函數的話就會在鏈接時產生foo函數未定義的錯誤。

假設在另一個文件里面也定義了一個全局函數foo:

foo2.c:int foo(int a)
{return (a);
}

那么在上面那個例子里面,后面一個對foo函數地址的引用就會在鏈接時被指到這個foo2.c中定義的foo函數去。也就是說:①調用foo函數的結果是a=-a,因為其內聯了foo.c內的foo函數;而③調用的結果則是b=b,因為其實際上調用的是foo2.c里面的foo函數!

extern inline的用法很奇怪也很少見,但是還是有其實用價值的。第一:它可以表現得像宏一樣,可以在文件內用extern inline版本的定義取代外部定義的庫函數(前提是文件內對其的調用不能出現無法內聯的情況);第二:它可以讓一個庫函數在能夠被內聯的時候盡量被內聯使用。舉個例子:

在一個庫函數的c文件內,定義一個普通版本的庫函數libfunc:

lib.c:void libfunc()
{...;
}

然后再在其頭文件內,定義(注意不是聲明!)一個實現相同的exterin inline的版本:

lib.h:extern inline libfunc()
{...;
}

那么在別的文件要使用這個庫函數的時候,只要include了lib.h,在能內聯展開的地方,編譯器都會使用頭文件內extern inline的版本來展開。而在無法展開的時候(函數指針引用等情況),編譯器就會引用lib.c中的那個獨立編譯的普通版本。即看起來似乎是個可以在外部被內聯的函數一樣,所以這應該是gcc的extern inline意義的由來。

但是注意這樣的使用是有代價的:c文件中的全局函數的實現必須和頭文件內extern inline版本的實現完全相同。否則就會出現前面所舉例子中直接內聯和間接調用時函數表現不一致的問題。

?重要

gcc的extern inline函數的用法相當奇怪,使用的范圍也非常狹窄:幾乎沒有什么情況會需要用它。

在C99中,也沒有關于extern inline這樣的描述,所以不建議大家使用extern inline,除非你明確理解了這種用法的意義并且有充足的理由使用它!

要點:

  • gcc絕對不會為extern inline的函數生成獨立匯編碼

  • extern inline函數允許和全局函數重名,可以在文件范圍內替代外部定義的全局函數

  • extern inline函數的應用范圍十分狹窄,而且行為比較奇怪,不建議使用

2.?C99的inline

以下主要描述C99的inline與Gcc不同的部分。對于相同的部分請參考GCC inline的說明。

2.1.?static inline

同GCC的static inline(第?1.1?節 “static inline”)。

2.2.?inline

C99的inline的使用相當令人費解。當一個定義為inline的函數沒有被聲明為extern的時候,其表現有點類似于gcc中extern inline那樣,即如果一個inline函數在文件范圍內沒有被聲明為extern的話,這個函數在文件內的表現就和gcc的extern inline相似:在本文件內調用時允許編譯器使用本文件內定義的這個內聯版本,但同時也允許外部存在同名的全局函數。只是比較奇怪的是C99居然沒有指定編譯器是否必須在本文件內使用這個inline的版本而是讓編譯器廠家自己來決定,相當模糊的定義。

如果在文件內把這個inline函數聲明為extern,則這個inline函數的表現就和gcc的inline一致了:這個函數即成為一個“external definition”(可以簡單理解為全局函數):可以在外部被調用,并且在程序內僅能存在一個這樣名字的定義。

下面舉例說明C99中inline的特性:

inline double fahr(double t)
{return (9.0 * t) / 5.0 + 32.0;
}inline double cels(double t)
{return (5.0 * (t - 32.0)) / 9.0;
}extern double fahr(double);   ①double convert(int is_fahr, double temp)
{return is_fahr ? cels(temp) : fahr(temp);   ②
}

在上面這個例子里,函數fahr是個全局函數:因為在①處將fahr聲明為extern,因此在②處調用fahr的時候使用的一定是這個文件內所定義的版本(只不過編譯器可以將這里的調用進行內聯展開)。在文件外部也可以調用這個函數(說明像gcc的inline一樣,編譯器在這種情況下會為fahr生成獨立的匯編碼)。

而cels函數因為沒有在文件范圍內被聲明為extern,因此它就是前面所說的“inline definition”,這時候它實際上僅能作用于本文件范圍(就像一個static的函數一樣),外部也可能存在一個名字也為cels的同名全局函數。在②處調用cels的時候編譯器可能選擇用本文件內的inline版本,也有可能跑去調用外部定義的cels函數(C99沒有規定此時的行為,不過編譯器肯定都會盡量使用文件內定義的inline版本,要不然inline函數就沒有存在的意義了)。從這里的表現上看C99中未被聲明為extern的inline函數已經和gcc的extern inline十分相似了:本文件內的inline函數可以作為外部庫函數的替代。

?重要

C99標準中的inline函數行為定義的比較模糊,并且inline函數有沒有在文件范圍內被聲明為extern的其表現有本質不同。如果和gcc的inline函數比較的話,一個被聲明為extern的inline函數基本等價于GCC的普通inline函數;而一個沒有被聲明為extern的inline函數基本等價于GCC的extern inline函數。

因為C99的inline函數如此古怪,所以在使用的時候,建議為所有的inline函數都在頭文件中創建extern的聲明:

foo.h:extern foo();

而在定義inline函數的c文件內include這個頭文件:

foo.c:#include "foo.h"inline void foo()
{...;
}

這樣無論是用gcc的inline規則還是C99的,都能得到完全相同的結果:foo函數會在foo.c文件內被內聯使用,而在外部又可以像普通全局函數一樣直接調用。

?

2.3.?extern inline

C99沒有見到extern inline的用法。

?

轉載于:https://www.cnblogs.com/chengxuyuancc/articles/2999397.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/259872.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/259872.shtml
英文地址,請注明出處:http://en.pswp.cn/news/259872.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

springmvc 組合注解

組合注解的意思就是一個注解中包含多個注解。在springmvc 的RestController中&#xff0c;你就可發現. Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Controller ResponseBody public interface RestController {/*** The value may indicate a su…

人才管理是什么意思_上海托管倉庫外包倉庫管理什么意思

上海托管倉庫外包倉庫管理什么意思上海倉庫托管外包。好的上海倉庫托管是預估好自己的貨物總計有多少個方。車子的體積有多少&#xff0c;然后估算出總計需要多少車需要多少錢&#xff0c;需要怎么裝車、卸貨碼放方式是什么樣的&#xff0c;算出總的費用然后包干給搬家公司。這…

window server 安裝與卸載

安裝window server 程序:C:\Windows\Microsoft.NET\Framework\v2.0.50727\installutil DataUpdateService.exe net start LuceneServer 卸載window server 程序:net stop LuceneServer C:\Windows\Microsoft.NET\Framework\v2.0.50727\installutil /U DataUpdateService.exe …

Makefile學習(二)[第二版]

復雜實例#示例1:在上一個示例的基礎上再增加一個可執行文件03test[修改之處已標紅].PHONY: clean all CC gcc CFLAGS -Wall -gBIN 01test 02test 03testSOURCES $(BIN:.c)OBJECTS $(BIN:.o)all: $(BIN)01test: 01test.o02test: 02test.o03test: 03test.o.c.o:$(CC) $(CFLA…

計算機網絡asp視頻教程,輕輕松松學編程!ASP互動視頻教程

從2006年5月18日開始&#xff0c;PConline將與FIF聯合推出國內網上第一部互動視頻教程&#xff1a;《ASP互動視頻教程》。它預示著一個全新的自助學習時代的到來。盡管相較于傳統的圖文教程&#xff0c;以前的多媒體視頻課件優點非常明顯&#xff0c;但它仍然存在交互性差的缺點…

Oracle查詢和解鎖表

一些ORACLE中的進程被殺掉后&#xff0c;狀態被置為"killed"&#xff0c;但是鎖定的資源很長時間不釋放&#xff0c;有時實在沒辦法&#xff0c;只好重啟數據庫。現在提供一種方法解決這種問題&#xff0c;那就是在ORACLE中殺不掉的&#xff0c;在OS一級再殺。1.下面…

三維家可以導入別人的方案嗎_廣州深圳天津形位公差檢測三維缺陷檢測服務

形位公差檢測三維缺陷檢測服務標簽&#xff1a;形位公差檢測 三維缺陷檢測服務 三維缺陷檢測鑄造工藝是一種經濟實惠的毛坯成形方式&#xff0c;對于一些形狀復雜的零件更能顯示出它的經濟性。比如汽車發動機的缸體和缸蓋&#xff0c;船舶螺旋槳以及精致的藝術品等。本期案例的…

計算機缺失esul.dll,SceneUI.ES.dll

我該如何安裝從金山毒霸下載的DLL文件&#xff1f;一&#xff1a;1、從金山毒霸下載壓縮文件。2、將DLL文件解壓到電腦上的某個地方。3、把該文件跟要求使用它的程序放在同一路徑上。注意32位程序需要使用32位的DLL文件&#xff0c;64位程序需要使用64位的DLL文件。否則會出現0…

android+ import R錯誤

import android.R; 在開發過程中有時候eclipse自動導入的包&#xff0c;該包有時候會導致一些奇怪的錯誤&#xff0c;再次出現該問題的時候&#xff0c;把import android.R;刪掉。setContentView(R.layout.secondactivity); //不刪掉 import android R&#xff1b;就會報錯轉載…

一、后臺首頁index.php【dedecms后臺源碼分析】

后臺目錄dede目錄的所有問價的源碼分析 使用的dedecms的版本5.7&#xff08;2012-04-01更新&#xff09; 后臺登陸之后的首頁分析dede/index.php <?php /*** 管理后臺首頁** version $Id: index.php 1 11:06 2010年7月13日Z tianya $* package DedeCMS.Admin…

transferto 文件不存在_文件上傳時,MultipartFile.transferTo() 方法報 FileNotFoundException...

Spring Upload File 報錯FileNotFoundException環境&#xff1a;Springboot2.0.4JDK1.8內嵌 Apache Tomcat/8.5.321、前端代碼前端上傳網頁表單&#xff0c;enctype 和 input 的typefile 即可&#xff0c;使用單文件上傳舉例&#xff1a;圖片2、后端代碼RestControllerRequestM…

對“粘連”footer布局的思考和總結

經典的"粘連"footer布局 參考文章鏈接在文章末尾&#xff0c;簡單的語言總結如下&#xff1a; 經典的“粘連”footer布局就是。我們有一塊內容<main>。當<main>的高度足夠長的時候&#xff0c;緊跟在<main>后面的元素<footer>會跟在<main…

計算機函數公式一等獎怎么算,信息技術應用 用計算機畫函數圖象教案設計(一等獎)...

衛鵬展地區&#xff1a; 湖北省 - 黃岡市 - 英山縣學校&#xff1a;英山縣金鋪鎮金鋪中學 共1課時信息技術應用 用計算機畫函數圖象">信息技術應用 用計算機畫… 初中數學 人教2011課標版 1教學目標1&#xff0e;結合具體情境理解一次函數的意義&#xff0c;能結…

這樣去寫你的 HTML

昨天在 twitter 上說&#xff0c;怎么忍心把頁面寫得這么難用&#xff1f;是的&#xff0c;這個世界還有一群人等著我們創建出來的東西&#xff0c;可以讓他們的生活能過得更容易呢。比如那些需要讀屏軟件的用戶。作為一個前端&#xff0c;我們又怎么會忍心呢。之前就一直想寫這…

iframe懶加載_前端常見問題

原地址&#xff1a;https://blog.csdn.net/Mr_JavaScript/article/details/843110681. flex布局&#xff1a;又叫做彈性布局任何一個容器都可以指定flex布局&#xff0c;如display:flex 或 display:inline-flex注意&#xff1a;設置了flex布局以后&#xff0c;子元素的float&am…

手機運行服務器無響應,《最強蝸牛》服務器無響應怎么辦 服務器無響應解決方法...

導讀最強蝸牛服務器無響應怎么辦&#xff1f;本作在今日迎來了正式的公測&#xff0c;這會導致大批量的玩家同時涌入進來&#xff0c;而服務器也因此而遭受到了非常大的符合&#xff0c;所以會導致后續加入進來的玩家出現服務器無響應進不去的現象。下面就為大家帶...最強蝸牛服…

Android 開源框架Universal-Image-Loader學習

Android 開源框架Universal-Image-Loader完全解析&#xff08;一&#xff09;--- 基本介紹及使用 Android 開源框架Universal-Image-Loader完全解析&#xff08;二&#xff09;--- 圖片緩存策略詳解 Android 開源框架Universal-Image-Loader完全解析&#xff08;三&#xff09;…

自己動手寫操作系統--個人實踐

近期開始看于淵的《自己動手寫操作系統》這本書&#xff0c;剛開始看就發現做系統的引導盤居然是軟盤&#xff01;心里那個汗啊&#xff01; 如今都是U盤了&#xff0c;誰還用軟盤。于是考慮用U盤。 于是開始下面步驟&#xff1a; 1、既然書上說給先要把軟盤做引導盤&#xff0…

蔻馳和mk哪個更大牌_mk和coach哪個好?mk和coach包包是一個檔次嗎?

說到包包&#xff0c;mk和coach可謂是輕奢界的兩大巨頭了。因此&#xff0c;兩個品牌的包包不可避免的會被經常拿來作比較。那么&#xff0c;從各個角度來講mk和coach那款包包更好呢&#xff1f;這兩款包包又有哪些本質上的區別呢&#xff1f;mk和coach哪個好價格對比大致上&am…

Linux 配置 swap 區

Linux 配置 swap 區 很多時候我們需要配置 swap 主要的原因是物理內存太貴了&#xff0c; 服務器也是一樣&#xff0c; 當內存不夠用時&#xff0c; 系統會卡死&#xff0c; 因此我們寧愿犧牲一點性能也要讓系統正常運行。 當然&#xff0c; 在系統物理內存足夠的條件下&#x…