c语言 隐式声明,关于C#:隐式函数声明和链接
最近,我了解了C語(yǔ)言中的隱式函數(shù)聲明。主要思想很明確,但在這種情況下,我對(duì)理解鏈接過(guò)程有些麻煩。
考慮以下代碼(文件a.c):
#include
int main() {
double someValue = f();
printf("%f
", someValue);
return 0;
}
如果我嘗試編譯它:
gcc -c a.c -std=c99
我看到有關(guān)函數(shù)f()的隱式聲明的警告。
如果我嘗試編譯和鏈接:
gcc a.c -std=c99
我有未定義的參考錯(cuò)誤。 所以一切都很好。
然后添加另一個(gè)文件(文件b.c):
double f(double x) {
return x;
}
并調(diào)用下一個(gè)命令:
gcc a.c b.c -std=c99
令人驚訝的是,一切都成功地鏈接了。 當(dāng)然,在./a.out調(diào)用之后,我會(huì)看到一個(gè)垃圾輸出。
因此,我的問(wèn)題是:具有隱式聲明的函數(shù)的程序如何鏈接? 在我的示例中,在編譯器/鏈接器的作用下會(huì)發(fā)生什么?
我在SO上讀了許多這樣的話題,但仍然有問(wèn)題。
首先,由于C99,從標(biāo)準(zhǔn)中刪除了函數(shù)的隱式聲明。編譯器可能支持此功能以編譯舊代碼,但這不是強(qiáng)制性的。引用標(biāo)準(zhǔn)序言,
remove implicit function declaration
也就是說(shuō),根據(jù)C11第6.5.2.2節(jié)
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.
所以,就您而言,
函數(shù)調(diào)用本身是隱式聲明(自C99以來(lái)已成為非標(biāo)準(zhǔn)聲明),
并且由于函數(shù)簽名的不匹配[假定函數(shù)的隱式聲明具有返回類型int],您的代碼將調(diào)用未定義的行為。
僅添加一點(diǎn)參考,如果您在調(diào)用后嘗試在同一編譯單元中定義函數(shù),由于簽名不匹配,您將收到編譯錯(cuò)誤。
但是,由于函數(shù)是在單獨(dú)的編譯單元中定義的(并且缺少原型聲明),因此編譯器無(wú)法檢查簽名。編譯之后,鏈接器將獲取目標(biāo)文件,并且由于鏈接器中沒有任何類型檢查(并且目標(biāo)文件中也沒有任何信息),因此可以愉快地鏈接它們。最后,它將最終成功完成與UB的編譯和鏈接。
感謝您的詳細(xì)回答。
這是正在發(fā)生的事情。
如果沒有f()的聲明,則編譯器會(huì)采用像int f(void)這樣的隱式聲明。然后愉快地編譯a.c。
編譯b.c時(shí),編譯器沒有f()的任何先前聲明,因此可以從f()的定義中直觀地看出來(lái)。通常,您會(huì)在頭文件中放置f()的聲明,并將其同時(shí)包含在a.c和b.c中。因?yàn)閮蓚€(gè)文件將看到相同的聲明,所以編譯器可以強(qiáng)制執(zhí)行一致性。它將抱怨與聲明不匹配的實(shí)體。但是在這種情況下,沒有通用的原型可以參考。
在C中,編譯器不會(huì)在目標(biāo)文件中存儲(chǔ)有關(guān)原型的任何信息,并且鏈接程序不會(huì)執(zhí)行任何一致性檢查(不能)。它所看到的只是a.c中未解析的符號(hào)f和b.c中定義的符號(hào)f。它很高興地解析符號(hào),并完成了鏈接。
但是,事情在運(yùn)行時(shí)會(huì)失敗,因?yàn)榫幾g器會(huì)根據(jù)在此假設(shè)的原型在a.c中設(shè)置調(diào)用。哪個(gè)與b.c中的定義不匹配。 f()(來(lái)自b.c)將從堆棧中獲取一個(gè)垃圾參數(shù),并將其返回為double,在a.c中返回時(shí)將被解釋為int。
我認(rèn)為編譯器在處理隱式聲明時(shí)采用int f()而不是int f(void)。
病態(tài)檢查,但考慮到a.c中的調(diào)用沒有參數(shù),因此假設(shè)int f(void)是有意義的,因?yàn)閕nt f()表示any number of arguments。
隱式聲明在現(xiàn)代C語(yǔ)言中無(wú)效,并且是UB。不像int f(void);那樣提供隱式的。它僅提供類似int f();的聲明,編譯器在編譯b.c時(shí)不會(huì)" intuit",并且不需要事先定義原型。函數(shù)定義也提供其原型。如果以前提供過(guò)原型,則對(duì)其進(jìn)行檢查。否則,不會(huì)。"通用原型"不是一回事。原型是否可用。與編譯器如何獲取該信息無(wú)關(guān)(通過(guò)頭文件或?qū)嶋H定義等)。
雖然(3)和(4)很好,但在標(biāo)準(zhǔn)中將其簡(jiǎn)單定義為未定義的行為。
@ I3x所有這些都是問(wèn)題的外圍部分,并且大多是在挑剔我表達(dá)某些觀點(diǎn)的方式。但是無(wú)所謂。
How are programmes with implicitly declared functions are linked? And what happens in my example under the hood of compiler/linker?
自C99以來(lái),隱含的int規(guī)則已被C標(biāo)準(zhǔn)取締。因此,具有隱式函數(shù)聲明的程序是無(wú)效的。
自C99起無(wú)效。在此之前,如果沒有可見的原型,則編譯器會(huì)隱式聲明一個(gè)具有int返回類型的原型。
Surprisingly everything is linked successfully. Of course after
./a.out invocation I see a rubbish output.
由于您沒有原型,因此編譯器會(huì)為f()隱式聲明一個(gè)類型為int的原型。但是f()的實(shí)際定義返回double。兩種類型不兼容,這是未定義的行為。
即使在C89 / C90中,這也是未定義的,在C89 / C90中,隱式int規(guī)則有效,因?yàn)殡[式原型與實(shí)際的f()返回類型不兼容。因此,此示例在所有C標(biāo)準(zhǔn)中均未定義(a.c和b.c)。
具有隱式函數(shù)聲明不再有用或無(wú)效。因此,有關(guān)編譯器/鏈接器處理方式的實(shí)際細(xì)節(jié)僅具有歷史意義。它可以追溯到K&R C的標(biāo)準(zhǔn)前時(shí)間,后者沒有函數(shù)原型,并且函數(shù)默認(rèn)返回int。將功能原型以C89 / C90標(biāo)準(zhǔn)添加到C中。最重要的是,對(duì)于有效的C程序中的所有函數(shù),必須具有原型(或在使用前定義函數(shù))。
這還不完整。這個(gè)問(wèn)題與隱式聲明無(wú)關(guān),但是由于鏈接器對(duì)類型一無(wú)所知,他找到了一個(gè)名為f的函數(shù)的定義,該函數(shù)適用于鏈接。為了避免這種問(wèn)題,某些語(yǔ)言使用名稱修飾將代碼類型編碼到函數(shù)名稱中。但是在C語(yǔ)言中不是這樣。擁有原型是不夠的,您需要一致的原型!
@ Jean-BaptisteYuns我已經(jīng)解釋過(guò),隱式原型的類型與實(shí)際類型不兼容,因此UB被調(diào)用。沒有"一致原型"之類的東西。原型是有關(guān)函數(shù)返回類型及其參數(shù)的完整信息。
@ i3x,但最大的問(wèn)題(我認(rèn)為)是鏈接為什么完成,以及解釋鏈接過(guò)程的詳細(xì)信息。
@ Jean-BaptisteYuns是的,主要的誤解是關(guān)于這種情況下的鏈接問(wèn)題。因此,我閱讀了Sourav和l3x的答案。所以他們有點(diǎn)混在我的頭上。現(xiàn)在,Souravs的答案似乎更加完整。
@Ziffusion我是說(shuō)它的UB,并且由于C.Btw的古老規(guī)則而被編譯和鏈接,我只是讀了您的答案,它并不完全準(zhǔn)確。
@ i3x我想你錯(cuò)過(guò)了重點(diǎn)。主要原因是:原型不一致(隱式聲明邊界效應(yīng))和無(wú)類型鏈接。兩者都與"古代C規(guī)則"無(wú)關(guān)。
編譯后,所有類型信息都將丟失(調(diào)試信息中可能除外,但鏈接程序不會(huì)對(duì)此進(jìn)行注意)。唯一剩下的就是"在地址0xdeadbeef處有一個(gè)名為" f"的符號(hào)"。
標(biāo)頭的目的是告訴C符號(hào)的類型,包括對(duì)于函數(shù)而言,它采用什么參數(shù)以及它返回什么。如果將實(shí)際值與聲明的值(顯式或隱式)不匹配,則會(huì)得到未定義的行為。
總結(jié)
以上是生活随笔為你收集整理的c语言 隐式声明,关于C#:隐式函数声明和链接的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 谷歌go语言课程讲解资源
- 下一篇: windows 解决 Go下载包失败 设