日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

c语言 隐式声明,关于C#:隐式函数声明和链接

發布時間:2025/3/15 C# 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c语言 隐式声明,关于C#:隐式函数声明和链接 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近,我了解了C語言中的隱式函數聲明。主要思想很明確,但在這種情況下,我對理解鏈接過程有些麻煩。

考慮以下代碼(文件a.c):

#include

int main() {

double someValue = f();

printf("%f

", someValue);

return 0;

}

如果我嘗試編譯它:

gcc -c a.c -std=c99

我看到有關函數f()的隱式聲明的警告。

如果我嘗試編譯和鏈接:

gcc a.c -std=c99

我有未定義的參考錯誤。 所以一切都很好。

然后添加另一個文件(文件b.c):

double f(double x) {

return x;

}

并調用下一個命令:

gcc a.c b.c -std=c99

令人驚訝的是,一切都成功地鏈接了。 當然,在./a.out調用之后,我會看到一個垃圾輸出。

因此,我的問題是:具有隱式聲明的函數的程序如何鏈接? 在我的示例中,在編譯器/鏈接器的作用下會發生什么?

我在SO上讀了許多這樣的話題,但仍然有問題。

首先,由于C99,從標準中刪除了函數的隱式聲明。編譯器可能支持此功能以編譯舊代碼,但這不是強制性的。引用標準序言,

remove implicit function declaration

也就是說,根據C11第6.5.2.2節

If the function is defined with a type that does not include a prototype, and the types of

the arguments after promotion are not compatible with those of the parameters after

promotion, the behavior is undefined.

所以,就您而言,

函數調用本身是隱式聲明(自C99以來已成為非標準聲明),

并且由于函數簽名的不匹配[假定函數的隱式聲明具有返回類型int],您的代碼將調用未定義的行為。

僅添加一點參考,如果您在調用后嘗試在同一編譯單元中定義函數,由于簽名不匹配,您將收到編譯錯誤。

但是,由于函數是在單獨的編譯單元中定義的(并且缺少原型聲明),因此編譯器無法檢查簽名。編譯之后,鏈接器將獲取目標文件,并且由于鏈接器中沒有任何類型檢查(并且目標文件中也沒有任何信息),因此可以愉快地鏈接它們。最后,它將最終成功完成與UB的編譯和鏈接。

感謝您的詳細回答。

這是正在發生的事情。

如果沒有f()的聲明,則編譯器會采用像int f(void)這樣的隱式聲明。然后愉快地編譯a.c。

編譯b.c時,編譯器沒有f()的任何先前聲明,因此可以從f()的定義中直觀地看出來。通常,您會在頭文件中放置f()的聲明,并將其同時包含在a.c和b.c中。因為兩個文件將看到相同的聲明,所以編譯器可以強制執行一致性。它將抱怨與聲明不匹配的實體。但是在這種情況下,沒有通用的原型可以參考。

在C中,編譯器不會在目標文件中存儲有關原型的任何信息,并且鏈接程序不會執行任何一致性檢查(不能)。它所看到的只是a.c中未解析的符號f和b.c中定義的符號f。它很高興地解析符號,并完成了鏈接。

但是,事情在運行時會失敗,因為編譯器會根據在此假設的原型在a.c中設置調用。哪個與b.c中的定義不匹配。 f()(來自b.c)將從堆棧中獲取一個垃圾參數,并將其返回為double,在a.c中返回時將被解釋為int。

我認為編譯器在處理隱式聲明時采用int f()而不是int f(void)。

病態檢查,但考慮到a.c中的調用沒有參數,因此假設int f(void)是有意義的,因為int f()表示any number of arguments。

隱式聲明在現代C語言中無效,并且是UB。不像int f(void);那樣提供隱式的。它僅提供類似int f();的聲明,編譯器在編譯b.c時不會" intuit",并且不需要事先定義原型。函數定義也提供其原型。如果以前提供過原型,則對其進行檢查。否則,不會。"通用原型"不是一回事。原型是否可用。與編譯器如何獲取該信息無關(通過頭文件或實際定義等)。

雖然(3)和(4)很好,但在標準中將其簡單定義為未定義的行為。

@ I3x所有這些都是問題的外圍部分,并且大多是在挑剔我表達某些觀點的方式。但是無所謂。

How are programmes with implicitly declared functions are linked? And what happens in my example under the hood of compiler/linker?

自C99以來,隱含的int規則已被C標準取締。因此,具有隱式函數聲明的程序是無效的。

自C99起無效。在此之前,如果沒有可見的原型,則編譯器會隱式聲明一個具有int返回類型的原型。

Surprisingly everything is linked successfully. Of course after

./a.out invocation I see a rubbish output.

由于您沒有原型,因此編譯器會為f()隱式聲明一個類型為int的原型。但是f()的實際定義返回double。兩種類型不兼容,這是未定義的行為。

即使在C89 / C90中,這也是未定義的,在C89 / C90中,隱式int規則有效,因為隱式原型與實際的f()返回類型不兼容。因此,此示例在所有C標準中均未定義(a.c和b.c)。

具有隱式函數聲明不再有用或無效。因此,有關編譯器/鏈接器處理方式的實際細節僅具有歷史意義。它可以追溯到K&R C的標準前時間,后者沒有函數原型,并且函數默認返回int。將功能原型以C89 / C90標準添加到C中。最重要的是,對于有效的C程序中的所有函數,必須具有原型(或在使用前定義函數)。

這還不完整。這個問題與隱式聲明無關,但是由于鏈接器對類型一無所知,他找到了一個名為f的函數的定義,該函數適用于鏈接。為了避免這種問題,某些語言使用名稱修飾將代碼類型編碼到函數名稱中。但是在C語言中不是這樣。擁有原型是不夠的,您需要一致的原型!

@ Jean-BaptisteYuns我已經解釋過,隱式原型的類型與實際類型不兼容,因此UB被調用。沒有"一致原型"之類的東西。原型是有關函數返回類型及其參數的完整信息。

@ i3x,但最大的問題(我認為)是鏈接為什么完成,以及解釋鏈接過程的詳細信息。

@ Jean-BaptisteYuns是的,主要的誤解是關于這種情況下的鏈接問題。因此,我閱讀了Sourav和l3x的答案。所以他們有點混在我的頭上。現在,Souravs的答案似乎更加完整。

@Ziffusion我是說它的UB,并且由于C.Btw的古老規則而被編譯和鏈接,我只是讀了您的答案,它并不完全準確。

@ i3x我想你錯過了重點。主要原因是:原型不一致(隱式聲明邊界效應)和無類型鏈接。兩者都與"古代C規則"無關。

編譯后,所有類型信息都將丟失(調試信息中可能除外,但鏈接程序不會對此進行注意)。唯一剩下的就是"在地址0xdeadbeef處有一個名為" f"的符號"。

標頭的目的是告訴C符號的類型,包括對于函數而言,它采用什么參數以及它返回什么。如果將實際值與聲明的值(顯式或隱式)不匹配,則會得到未定義的行為。

總結

以上是生活随笔為你收集整理的c语言 隐式声明,关于C#:隐式函数声明和链接的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。