Harici satır içi ne yapar?


93

Tek inlinebaşına derleyiciye bir öneri olduğunu ve kendi takdirine bağlı olarak işlevi satır içi olabilir veya olmayabilir ve ayrıca bağlanabilir nesne kodu üreteceğini anlıyorum.

Bunun static inlineaynı şeyi yaptığını (satır içi olabilir veya olmayabilir), ancak satır içi olduğunda bağlanabilir nesne kodu üretmeyeceğini düşünüyorum (çünkü başka hiçbir modül buna bağlanamaz).

extern inlineResme nereye sığar?

Bir önişlemci makrosunu bir satır içi işlevle değiştirmek istediğimi ve bu işlevin satır içi olmasını istediğimi varsayalım (örneğin, çağıran için çözmesi gereken ancak bu işlev olarak adlandırılan değil) __FILE__ve __LINE__makrolarını kullandığı için ). Yani, işlevin satır içi olmaması durumunda bir derleyici veya bağlayıcı hatası görmek istiyorum. extern inlineBunu yapar mı ? (Olmazsa, makroya bağlı kalmaktan başka bu davranışı elde etmenin bir yolu olmadığını varsayıyorum.)

C ++ ve C arasında fark var mı?

Farklı derleyici satıcıları ve sürümleri arasında farklılıklar var mı?

Yanıtlar:


129

K&R C veya C89'da satır içi dilin bir parçası değildi. Birçok derleyici bunu bir uzantı olarak uyguladı, ancak nasıl çalıştığına dair tanımlanmış bir anlambilim yoktu. GCC inlining uygulamak için ilk ülkelerden biri oldu ve tanıtılan inline, static inlineve extern inlineyapılar; çoğu C99 öncesi derleyici genellikle liderliğini takip eder.

GNU89:

  • inline: fonksiyon satır içi olabilir (bu sadece bir ipucu). Hat dışı bir sürüm her zaman yayınlanır ve dışarıdan görülebilir. Bu nedenle, yalnızca bir derleme biriminde böyle bir satır içi tanımlanmış olabilir ve diğerlerinin bunu satır dışı bir işlev olarak görmesi gerekir (veya bağlantı zamanında yinelenen simgeler alırsınız).
  • extern inline hat dışı bir sürüm oluşturmaz, ancak birini çağırabilir (bu nedenle başka bir derleme biriminde tanımlamanız gerekir. Tek tanımlama kuralı geçerlidir; satır dışı sürüm ile aynı koda sahip olmalıdır Derleyicinin onu çağırması durumunda burada satır içi teklif edilir.
  • static inlineharici olarak görünür bir hat dışı sürüm oluşturmaz, ancak statik bir dosya oluşturabilir. Tek tanım kuralı uygulanmaz çünkü hiçbir zaman dışarıdan bir sembol yayılmaz veya birine çağrı yapılmaz.

C99 (veya GNU99):

  • inline: GNU89 "extern inline" gibi; dışarıdan görülebilen hiçbir işlev yayınlanmaz, ancak biri çağrılabilir ve bu nedenle var olmalıdır
  • extern inline: GNU89 "satır içi" gibi: harici olarak görünür kod yayınlanır, bu nedenle en fazla bir çeviri birimi bunu kullanabilir.
  • static inline: GNU89 "statik satır içi" gibi. Bu, gnu89 ve c99 arasındaki tek taşınabilir olanıdır

C ++:

Herhangi bir yerde satır içi olan bir işlev, her yerde aynı tanımla satır içi olmalıdır. Derleyici / bağlayıcı, sembolün birden çok örneğini sıralayacaktır. static inlineVeya extern inlinebirçok derleyiciye sahip olmasına rağmen (tipik olarak gnu89 modelini izleyerek) tanımı yoktur .


2
Klasik klasik C'de "satır içi" bir anahtar kelime değildi; değişken adı olarak kullanım için mevcuttu. Bu C89 ve ön standart (K&R) C için geçerli olacaktır.
Jonathan Leffler

Görünüşe göre haklısın. Sabit. C89'da bir anahtar kelime olarak ayrıldığını düşündüm (K & R'de olmasa da), ama sanırım yanlış hatırladım
puetzk

Bunu Microsoft'un Visual C ++ için eklemek istiyorum, işlevinizin satır içine alınmasını sağlayacak bir __forceinline anahtar sözcüğü var. Bu açıkça yalnızca VC ++ için derleyiciye özgü bir uzantıdır.
untitled8468927

C99 "harici satır içi" ile tanımlayıcı olmaması arasında herhangi bir fark var mı?
Jo So

Anlamsal olarak hayır; tıpkı satır içi olmayan bir fonksiyon gibi, extern inlinetek tanım kuralına tabidir ve bu tanımdır. Ancak, uygulama tanımlı optimizasyon buluşsal yöntemi, inlineanahtar kelimeyi "işleve çağrıların olabildiğince hızlı olması" önerisi olarak kullanma önerisini izlerse (ISO 9899: 1999 §6.7.4 (5), extern inlinecounts
puetzk

31

Bu ifadeye dayanarak __FILE__ ve __LINE__’ı yanlış anladığınıza inanıyorum:

çünkü arayan için çözülmesi gereken __FILE__ ve __LINE__ makrolarını kullanır, ancak bu işlev olarak adlandırılmaz

Derlemenin birkaç aşaması vardır ve ön işleme ilk aşamadır. __FILE__ ve __LINE__ bu aşamada değiştirilir. Bu nedenle, derleyici satır içi olarak işlevini değerlendirebildiği zaman, zaten değiştirilmişlerdir.


14

Görünüşe göre böyle bir şey yazmaya çalışıyorsun

inline void printLocation()
{
  cout <<"You're at " __FILE__ ", line number" __LINE__;
}

{
...
  printLocation();
...
  printLocation();
...
  printLocation();

ve her seferinde farklı değerler basacağınızı umuyoruz. Don'un dediği gibi, __FILE__ ve __LINE__ önişlemci tarafından uygulandığı için, ancak inline derleyici tarafından uygulandığı için yapmayacaksınız. Yani printLocation'ı nereden ararsanız arayın, aynı sonucu alırsınız.

Bunu çalıştırmanın tek yolu printLocation'ı bir makro yapmaktır. (Evet biliyorum...)

#define PRINT_LOCATION  {cout <<"You're at " __FILE__ ", line number" __LINE__}

...
  PRINT_LOCATION;
...
  PRINT_LOCATION;
...

18
Makro PRINT_LOCATION geçen bir işlev printLocation çağırmak için yaygın bir hiledir DOSYASINI ve LINE parametreleri olarak. Bu, işlev gövdesi önemsiz olmadığında daha iyi hata ayıklayıcı / düzenleyici / vb davranışıyla sonuçlanabilir.
Steve Jessop

@Roddy Benim çözümüme bakın - sizin uzantınız ama daha kapsamlı ve genişletilebilir.
enthusiasticgeek

@SteveJessop Aşağıdaki çözümde listelediğim gibi bir şey?
enthusiasticgeek

3

Satır içi, statik satır içi ve harici satır içi durum karmaşıktır, çünkü gcc ve C99 davranışları için biraz farklı anlamlar tanımlamaktadır (ve muhtemelen C ++ da). Burada C'de yaptıklarıyla ilgili bazı yararlı ve ayrıntılı bilgileri bulabilirsiniz .


2

Satır içi işlevler yerine makrolar burada sizin seçiminizdir. Makroların satır içi işlevlere hükmettiği nadir bir durum. Aşağıdakileri deneyin: Bu "MAKRO MAGIC" kodunu yazdım ve çalışmalı! Gcc / g ++ Ubuntu 10.04'te test edildi

//(c) 2012 enthusiasticgeek (LOGGING example for StackOverflow)

#ifdef __cplusplus

#include <cstdio>
#include <cstring>

#else

#include <stdio.h>
#include <string.h>

#endif

//=========== MACRO MAGIC BEGINS ============

//Trim full file path
#define __SFILE__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/')+1 : __FILE__ )

#define STRINGIFY_N(x) #x
#define TOSTRING_N(x) STRINGIFY_N(x)
#define _LINE (TOSTRING_N(__LINE__))

#define LOG(x, s...) printf("(%s:%s:%s)"  x "\n" , __SFILE__, __func__, _LINE, ## s);

//=========== MACRO MAGIC ENDS ============

int main (int argc, char** argv) {

  LOG("Greetings StackOverflow! - from enthusiasticgeek\n");

  return 0;
}

Birden çok dosya için bu makroları, her c / cc / cxx / cpp dosyasında aynısını içeren ayrı bir başlık dosyasında tanımlayın. Mümkün olan yerlerde makrolar yerine lütfen satır içi işlevleri veya sabit tanımlayıcıları (durum gerektirdiğinde) tercih edin.


2

"Ne işe yarar?" Cevabını vermek yerine, "İstediğimi nasıl yaptırırım?" Hepsi GNU C89, standart C99 ve C ++ 'da bulunan 5 tür satır içi vardır:

adres alınmadığı sürece her zaman satır içi

__attribute__((always_inline))Herhangi bir beyanı ekleyin , ardından adresinin alınma olasılığını ele almak için aşağıdaki durumlardan birini kullanın.

Anlamına ihtiyaç duymadığınız sürece muhtemelen bunu asla kullanmamalısınız (örneğin, montajı belirli bir şekilde etkilemek veya kullanmak için alloca). Derleyici, buna değip değmeyeceğini genellikle sizden daha iyi bilir.

satır içi ve zayıf bir sembol yayar (C ++ gibi, diğer adıyla "çalışmasını sağla")

__attribute__((weak))
void foo(void);
inline void foo(void) { ... }

Bunun aynı kodun birkaç kopyasını ortalıkta bıraktığını ve bağlayıcının keyfi olarak birini seçtiğini unutmayın.

satır içi, ancak hiçbir zaman herhangi bir sembol yaymayın (harici referanslar bırakarak)

__attribute__((gnu_inline))
extern inline void foo(void) { ... }

her zaman yayar (bir TU için, öncekini çözmek için)

İpuçlu sürüm, C ++ 'da zayıf bir sembol yayar, ancak C'nin her iki lehçesinde de güçlü bir sembol:

void foo(void);
inline void foo(void) { ... }

Ya da her iki dilde de güçlü bir sembol yayan ipucu olmadan da yapabilirsiniz:

void foo(void) { ... }

Genel olarak, tanımları verirken TU'nuzun hangi dilde olduğunu bilirsiniz ve muhtemelen çok fazla satır içi yapmaya ihtiyacınız yoktur.

her OG'de satır içi ve yayma

static inline void foo(void) { ... }

staticBiri hariç tüm bunlar için void foo(void)yukarıya bir beyan ekleyebilirsiniz . Bu, temiz başlıklar yazmanın "en iyi uygulamasına" ve ardından #includesatır içi tanımlarla ayrı bir dosya oluşturmaya yardımcı olur . Daha sonra, C tarzı #definesatır içi kullanılıyorsa, satır dışı tanımları sağlamak için özel bir TU'da bazı makrolar farklı şekilde.

extern "C"Başlığın hem C hem de C ++ 'dan kullanılıp kullanılamayacağını unutmayın !


Eksik: MSVC ne olacak? Bazı C89 lehçe uzantıları var, ancak asla MSVC kullanmıyorum ve nmeşdeğerini nasıl çalıştıracağımı bilmiyorum .
o11c

Durum # 4 void foo(void); inline void foo(void) { ... }başlığı "her zaman yayınla (bir OG için, öncekini çözmek için)" dir. Diğer 4 tanesi "satır içi ...." ile başlar. 4. durumda: o da satır içi mi?
chux - Monica'yı yeniden etkinleştir

@ chux-ReinstateMonica Inlining, performanstan çok sembol davranışıyla ilgilidir. Derleyicinin, anahtar kelime bulunup bulunmadığına bakılmaksızın performans için satır içi olmasına izin verilir (yorumlama kurallarına tabidir). 4 numaralı durumun kritik kısmı, 3 numaralı durumun çözümlenmemiş sembolleriyle nasıl ilişkili olduğudur; çoğu zaman değildir o TU herhangi arayanlar zaten (m).
o11c

Benim yorumum daha çok 1,2,3,5 numaralı durumlarda bu cevabın başlık açıklamasında satır içi olduğu halde neden 4'ün olmadığı ile ilgiliydi . 4 kodun hiçbir zaman satır içi, her zaman satır içi, bazen satır içi olmaması durumunda mı öneriyorsunuz? Durum # 4 satır içi olasılıkları diğer 4'ten farklı mı?
chux - Monica'yı yeniden etkinleştir
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.