Soru “Statik” veya “extern” içermeyen “inline”, C99'da hiç işe yaramıyor mu?


Bu kodu oluşturmaya çalıştığımda

inline void f() {}

int main()
{
    f();
}

komut satırını kullanarak

gcc -std=c99 -o a a.c

Bir linker hatası alıyorum (tanımlanmamış referans f). Kullanırsam hata kaybolur static inline veya extern inline sadece yerine inlineveya eğer derlersek -O (böylece işlev aslında satır içi).

Bu davranış C99 standardının 6.7.4 (6) paragrafında tanımlanmıştır:

Bir çeviri birimindeki bir işlev için dosya kapsamı bildirimlerinin tümü içeriyorsa inline olmadan işlev belirteci externDaha sonra, çeviri birimindeki tanım, satır içi bir tanımdır. Satır içi bir tanım, işlev için harici bir tanım sağlamaz ve başka bir çeviri biriminde bir dış tanımı yasaklamaz. Satır içi bir tanım, bir çeviricinin aynı çeviri birimindeki işleve herhangi bir çağrıyı uygulamak için kullanabileceği bir dış tanıma bir alternatif sağlar. İşlev çağrısının satır içi tanımını veya dış tanımı kullanıp kullanmadığı belirtilmemiş.

Tüm bunları doğru anlarsam, tanımlanmış bir fonksiyona sahip bir derleme birimi inline Yukarıdaki örnekte olduğu gibi, aynı ada sahip bir harici işlev varsa, sürekli olarak derler ve kendi işlevimin veya dış işlevimin çağrılıp çağrılmadığını asla bilmem.

Bu davranış tamamen delilik değil mi? Bir fonksiyonu tanımlamak hiç kullanışlı mı? inline olmadan static veya extern C99'da? Bir şey mi eksik?

Cevapların özeti

Tabi ki bir şeyleri kaçırıyordum ve davranış da delilik değil. :)

Gibi Nemo şöyle açıklıyor, fikri tanım işlev

inline void f() {}

başlık dosyasında ve sadece bir deklarasyon

extern inline void f();

karşılık gelen .c dosyasında. Sadece extern bildirim, dışarıdan görünen ikili kodun oluşturulmasını tetikler. Ve gerçekten de inline bir .c dosyasında - sadece başlıklarda kullanışlıdır.

Olarak Jonathan'ın cevabında C99 komitesinin gerekçesi , explicates inline Bir fonksiyonun tanımında bir fonksiyonun tanımlanmasını gerektiren derleyici optimizasyonları ile ilgilidir. Bu, yalnızca tanımın başlığa getirilmesiyle elde edilebilir ve elbette bir başlıktaki bir tanım, derleyici tarafından görüldüğü her zaman kod yaymamalı. Ancak derleyici aslında bir işlevi satır içi olarak zorlamak zorunda olmadığından, bir yerde harici bir tanım bulunmalıdır.


88
2018-06-10 22:14


Menşei


sınır çizgisi stackoverflow.com/questions/5369888/... - Earlz
Ve ayrıca stackoverflow.com/questions/216510/extern-inline - Nemo
@Earlz: Bağlantı için teşekkürler. Sorum şu, standardın kote edilmiş paragrafının ardındaki mantık hakkında ve eğer kullanım durumları varsa inline olmadan static ve externolsa da. Ne yazık ki, bu sorunların hiçbiri bu soruda ele alınmamıştır. - Sven Marnach
@Nemo: Bu soruyu ve cevaplarımı benim yayınlamadan önce okudum. Yine, "nasıl davranıyor?" Diye sormuyorum. ama daha ziyade "bu davranışın arkasındaki fikir nedir?" Eminim ki burada bir şey eksik. - Sven Marnach
evet, bu yüzden "sınır çizgisi" dedim. Ben şahsen bunun tamamen farklı bir soru sorduğunu düşünüyorum. - Earlz


Cevaplar:


Aslında bu mükemmel cevap da sorunuzu cevaplıyor, bence:

extern inline

Fikir, "satır içi" bir başlık dosyasında ve sonra bir .c dosyasında "extern inline" kullanılabilir. "extern inline", hangi nesne dosyasının (harici olarak görünür) oluşturulan kodu içermesi gerektiğini derleyiciye nasıl yönlendirirsiniz.

[güncellemek, detaylandırmak]

Bir .c dosyasında "inline" ("statik" veya "extern" olmadan) için herhangi bir kullanım olduğunu düşünmüyorum. Ancak bir başlık dosyasında mantıklıdır ve aslında tek başına kod üretmek için bazı .c dosyasında karşılık gelen bir "extern inline" bildirimi gerektirir.


36
2018-06-10 22:48



Evet, bunu şimdiye kadar asla anlayamadım, bu yüzden sorduğun için teşekkürler :-). Bu extern olmayan "inline" içinde garip tanım "extern inline" iken başlığa girer (ancak her hangi bir kod üretimi ile sonuçlanmaz) deklarasyon .c dosyasında gider ve aslında kod oluşturulmasına neden olur. - Nemo
Bu yazdığım anlamına mı geliyor? inline void f() {} başlıkta ve extern inline void f(); .c dosyasında? Bu nedenle, asıl işlev tanımı başlığa gider ve .c dosyası, bu durumda, olağan düzenin tersine çevrilmesinde yalnızca bir beyan içerir. - Sven Marnach
@endolith: Sorunuzu anlamıyorum. Biz tartışıyor inline  olmadan  static veya extern. Tabii ki static inline iyi, ama bu soru ve cevabın ne olduğu değil. - Nemo
"Fikir şu ki" satır içi "bir başlık dosyasında ve sonra bir .c dosyasında" extern inline "olarak kullanılabilir." => Diğerini kastetmiyor musun? En azından benim kullandığım gcc, bağlantı zamanımdaki yinelenen sembollerden şikayetçidir inline .h dosyasında extern olmadan. - Matthieu Moy
@MatthieuMoy Kullanmanız gerekiyor -std=c99 yerine -std=gnu89. - a3f


Standardın (ISO / IEC 9899: 1999) kendisinden:

Ek J.2 Tanımsız Davranış

  • ...
  • Harici bağlantıya sahip bir işlev, bir inline işlev belirteci, ancak aynı çeviri biriminde de tanımlanmamıştır (6.7.4).
  • ...

C99 Komitesi bir gerekçeve diyor ki:

6.7.4 İşlev belirleyicileri

C99'un yeni bir özelliği:  inline C ++’dan uyarlanan anahtar kelime fonksiyon belirteci o   sadece işlev bildirimlerinde kullanılabilir. Gerektiren program optimizasyonları için kullanışlıdır.   Bir çağrının yerinde görülebilen bir işlevin tanımı. (Standardın bu optimizasyonların niteliğini belirlemeye çalışmadığını unutmayın.)

Fonksiyonun iç bağlantısı varsa veya dış bağlantı ve çağrı varsa görünürlük sağlanır.   harici çeviri ile aynı çeviri birimindedir. Bu durumlarda, varlığı    inline Bir beyannamede veya işlevin tanımında anahtar kelime, bir   bu fonksiyonun çağrılmasının tercih edilmeden, diğer inline Anahtar kelime.

Görünürlük, çağrının bir yerde olduğu harici bağlantıya sahip bir işlevin çağrılması için bir sorundur.   İşlevden farklı çeviri birimi. Bu durumda inline anahtar kelime   aramayı içeren çeviri biriminin ayrıca yerel veya satır içi bir tanımını içermesine izin verir.   işlevi.

Bir program, harici bir tanıma sahip bir çeviri birimi,   satır içi tanımı ve bir bildirime sahip bir çeviri birimi, ancak bir işlev için tanım yok. Aramalar   ikinci çeviri ünitesinde harici tanımı her zamanki gibi kullanacaktır.

Bir fonksiyonun satır içi bir tanımının dışsaldan farklı bir tanım olduğu düşünülür.   tanım. Bazı işlevlere bir çağrı func harici bağlantı ile bir satır içi oluşur    tanım görünür, davranış, arama başka bir işleve yapılmışsa aynıdır    __funciç bağlantı ile. Uygun bir program hangi işleve bağlı olmamalıdır   aradı. Bu, Standarttaki inline modelidir.

Uygun bir program, inline tanımını kullanarak uygulamaya güvenmemelidir, ne de   Dış tanımı kullanarak uygulamaya dayanır. Bir fonksiyonun adresi her zaman harici tanımlamaya karşılık gelen adrestir, ancak bu adres çağrıldığında kullanılır.   işlev, satır içi tanımı kullanılabilir. Bu nedenle, aşağıdaki örnek olmayabilir   beklendiği gibi davranmak.

inline const char *saddr(void)
{
    static const char name[] = "saddr";
    return name;
}
int compare_name(void)
{
    return saddr() == saddr(); // unspecified behavior
}

Uygulama, çağrılardan biri için satır içi tanımını kullanabileceğinden saddr ve kullan   Diğeri için dış tanım, eşitlik işlemi değerlendirmek için garanti edilmez 1   (doğru). Bu, satır içi tanım içinde tanımlanan statik nesnelerin, birbirinden farklı olduğunu gösterir.   dış tanımda karşılık gelen nesne. Bu kısıtlamaya bile karşı koydu.   olmayanconstbu tür nesneler.

Inlining, standart olarak mevcut linker ile uygulanabilecek şekilde eklenmiştir.   teknoloji ve C99 inlining'in bir alt kümesi C ++ ile uyumludur. Bu, bir satır içi işlevin tanımını içeren tam bir çeviri biriminin gerekmesiyle başarıldı.   işlev için harici tanımı sağlayan olarak belirtilir. Cünkü bu   şartname, eksik olan bir beyandan ibarettir. inline anahtar kelime veya   her ikisi de inline ve externAyrıca bir C ++ çevirmeni tarafından kabul edilecektir.

C99'da satır aralığı, C ++ özelliğini iki şekilde genişletir. İlk olarak, bir işlev ilan edilirse    inline bir çeviri biriminde bildirilmesi gerekli değildir inline Diğer tüm çeviri birimlerinde.   Bu, örneğin kütüphane içinde belirtilebilecek ancak mevcut olan bir kütüphane fonksiyonuna izin verir.   sadece başka bir yerde harici bir tanımla. Sarma işlevi kullanma seçeneği   harici fonksiyon ek bir isim gerektirir; ve ayrıca performansı olumsuz etkileyebilir   Çevirmen aslında satır içi ikamesi yapmazsa.

İkincisi, bir satır içi işlevin tüm tanımlarının "tam olarak aynı olması" gerekliliği   Programın davranışının bir   çağrı, bir fonksiyonun görünür bir satır içi tanımı veya harici tanımı ile gerçekleştirilir.   Bu, bir satır içi tanımın, belirli bir çeviri birimi içinde kullanımı için uzmanlaşmasına izin verir.   Örneğin, bir kitaplık işlevinin dış tanımı, aynı kitaplıkta bulunan diğer işlevlerden yapılan çağrılar için gerekli olmayan bazı argüman doğrulamasını içerebilir. Bunlar   uzantılar bazı avantajlar sunar; ve uyumluluk konusunda endişe duyan programcılar   sadece daha sıkı C ++ kurallarına uyabilir.

Olduğunu unutmayın değil standart satır içi tanımları sağlamak için uygulamalar için uygun   standart üstbilgilerdeki kitaplık işlevleri, çünkü bu, başlıklarını da ekledikten sonra standart kitaplık işlevlerini yeniden tanıyan bazı eski kodları kırabilir. inline anahtar kelime   sadece kullanıcılara işlevlerin sıralamasını önermek için taşınabilir bir yol sağlamayı amaçlamaktadır. Çünkü   standart başlıkların taşınabilir olması gerekmez, uygulamaların şu satırlarda başka seçenekleri vardır:

#define abs(x) __builtin_abs(x)

veya standart kütüphane fonksiyonlarını belirtmek için diğer taşınabilir olmayan mekanizmalar.


22
2018-06-10 22:57



Teşekkürler, bu çok ayrıntılı - ve hemen standart olarak okunabilir. :-) Bir şeyi kaçırmam gerektiğini biliyordum. Bunu nereden aldığın bir referans verebilir misin? - Sven Marnach
Bağlantıları bulmak için Google’ı kullanabileceğinizi aklımı geçti: open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf - Sven Marnach
@Sven: URL’yi aramanızdan ödünç alıp cevaba koydum. Kullandığım belge daha önce kaydetmiş olduğum gerekçenin bir kopyasıydı (2005'te), fakat aynı zamanda V5.10 idi. - Jonathan Leffler
Tekrar teşekkürler. Cevapdan aldığım en değerli bilgiler, C99 standardının mantığını içeren bir belge (ve muhtemelen başka standartlar için de belgeler var). Nemo'nun cevabı bana bir balık verirken, bu bana balık tutmayı öğretti. - Sven Marnach


> Bir linker hatası alıyorum (undefined reference f)

Burada çalışır: Linux x86-64, GCC 4.1.2. Derleyicinizde bir hata olabilir; Verilen paragrafta, verilen programı yasaklayan standarttan hiçbir şey göremiyorum. Kullanımı not Eğer ziyade iFF.


0
2018-06-10 22:57



Standart, derleyicinin her zaman aynı ada sahip harici bir işlev olduğunu ve satır içi sürümü yerine çağırdığını varsayar, bu yüzden derleyici bir hata olduğunu düşünmüyorum. Gcc 4.3, 4.4 ve 4.5 ile denedim, hepsi bir bağlayıcı hatası verir. - Sven Marnach
@Sven: Haklısın. Güncellenmiş. - Fred Foo