Kaynak kodun sonuna tanım yazıldığında, C dilinde veri ve işlevlerin * bildirimi * neden gereklidir?


15

Aşağıdaki "C" kodunu göz önünde bulundurun:

#include<stdio.h>
main()
{   
  printf("func:%d",Func_i());   
}

Func_i()
{
  int i=3;
  return i;
}

Func_i()kaynak kodun sonunda tanımlanır ve kullanılmadan önce hiçbir bildirim yapılmaz main(). Derleyicinin gördüğü Func_i()anda main(), çıkar main()ve bulur Func_i(). Derleyici bir şekilde döndürülen değeri bulur Func_i()ve verir printf(). Ben de derleyici bulamıyorum biliyorum dönüş türü arasında Func_i(). Bu, varsayılan (tahmin?) Alır dönüş türü arasında Func_i()olmak int. Eğer kod o float Func_i()zaman derleyici hata verirdi: Çakışan türleri içinFunc_i() .

Yukarıdaki tartışmadan şunu görüyoruz:

  1. Derleyici döndürülen değeri bulabilir Func_i().

    • Derleyici , kaynak koddan Func_i()çıkıp main()kaynak kodunu arayarak döndürülen değeri bulabilirse, neden açıkça belirtilen Func_i () türünü bulamıyor ?
  2. Derleyici Func_i()float türü olduğunu bilmelidir - bu yüzden çakışan türlerde hata verir.

  • Derleyici bunun Func_ifloat türünde olduğunu biliyorsa, neden hala Func_i()int türünde olduğunu varsayıyor ve çakışan türlerde hata veriyor? Neden zorla Func_i()yüzer tip yapmak için yapmıyoruz .

Değişken beyanında da aynı şüphe var . Aşağıdaki "C" kodunu göz önünde bulundurun:

#include<stdio.h>
main()
{
  /* [extern int Data_i;]--omitted the declaration */
  printf("func:%d and Var:%d",Func_i(),Data_i);
}

 Func_i()
{
  int i=3;
  return i;
}
int Data_i=4;

Derleyici şu hatayı verir: 'Data_i' bildirilmemiş (bu işlevde ilk kullanım).

  • Derleyici gördüğünde Func_i(), Func_ () tarafından döndürülen değeri bulmak için kaynak koduna iner. Derleyici Data_i değişkeni için neden aynısını yapamıyor?

Düzenle:

Derleyicinin, montajcının, işlemcinin vb. İç işleyişinin ayrıntılarını bilmiyorum. Sorumun temel fikri, kullanımdan sonra, kaynak koddaki işlevin dönüş değerini söylersem (yazar) "C" dili bilgisayarın bu değeri herhangi bir hata vermeden bulmasını sağlar. Şimdi neden bilgisayar türü bulamıyor? Veri_i türü neden Func_i () 'nin dönüş değeri bulunamadığı için bulunamadı. İfadeyi kullansam bile extern data-type identifier;, bu tanımlayıcı (işlev / değişken) tarafından döndürülecek değeri söylemiyorum. Bilgisayar bu değeri bulabilirse, neden türü bulamıyor? Neden ileri bildirime ihtiyacımız var?

Teşekkür ederim.


7
Derleyici Func_i tarafından döndürülen değeri "bulamaz". Bu, yürütme zamanında yapılır.
James McLeod

26
Ben aşağı itmedim, ama soru derleyicilerin nasıl çalıştığına dair bazı yanlış yanlış anlamalara dayanıyor ve yorumlardaki yanıtlarınız hala üstesinden gelmek için bazı kavramsal engellere sahip olduğunuzu gösteriyor.
James McLeod

4
İlk örnek kodun son on beş yıldır geçerli, standartlara uygun kod olmadığını unutmayın; C99, işlev tanımlarında bir dönüş türünün bulunmamasını ve örtük olarak Func_igeçersiz beyanını yapmıştır . Tanımlanmamış değişkenleri dolaylı olarak bildirmek için hiçbir kural yoktu, bu nedenle ikinci parça her zaman hatalı biçimlendirildi. (Evet, derleyiciler ilk örneği kabul eder, çünkü özensiz ise, C89 / C90 altında geçerlidir.)
Jonathan Leffler

19
@ user31782: Sorunun alt satırı: X dili neden Y gerektirir / gerektirir? Çünkü tasarımcıların yaptığı seçim bu. Şimdiye kadarki en başarılı dillerden birinin tasarımcılarının, bu seçimleri yapıldıkları bağlamda anlamaya çalışmak yerine onlarca yıl önce farklı seçimler yapmaları gerektiğini savunuyorsunuz. Sorunuzun cevabı: Neden ileri bildirime ihtiyacımız var? verildi: Çünkü C tek geçişli bir derleyici kullanıyor. Takip sorularınızın çoğuna en basit cevap Çünkü tek geçişli bir derleyici olmaz.
Mr.Mindor

4
@ user31782 Gerçekten, gerçekten derleyicilerin ve işlemcilerin gerçekte nasıl çalıştığını anlamak için ejderhanın kitabını okumak istiyorsunuz - gerekli tüm bilgileri tek bir SO cevabına (hatta 100'e) damlatmak imkansız. Derleyicilere ilgi duyan herkes için harika bir kitap.
Voo

Yanıtlar:


26

Çünkü C tek geçişli , statik olarak yazılmış , zayıf yazılmış , derlenmiş bir dildir.

  1. Tek geçiş , derleyicinin bir işlev veya değişkenin tanımını görmek için ileriye bakmadığı anlamına gelir. Derleyici ileriye bakmadığından, bir işlevin bildirimi işlevin kullanılmasından önce gelmelidir, aksi takdirde derleyici tür imzasının ne olduğunu bilmez. Ancak, işlevin tanımı daha sonra aynı dosyada veya hatta farklı bir dosyada olabilir. Bakınız nokta # 4.

    Tek istisna, kayıt dışı işlevlerin ve değişkenlerin "int" türünde olduğu varsayılan tarihsel yapıdır. Modern uygulama, işlevleri ve değişkenleri her zaman açıkça bildirerek örtük yazımdan kaçınmaktır.

  2. Statik olarak yazılan , tüm tür bilgilerinin derleme zamanında hesaplandığı anlamına gelir. Bu bilgiler daha sonra çalışma zamanında yürütülen makine kodunu oluşturmak için kullanılır. C'de çalışma zamanı yazmanın bir konsepti yoktur. Bir zamanlar int, hep int, bir kez bir float, her zaman bir float. Bununla birlikte, bu gerçek bir sonraki nokta tarafından biraz gizlenmiştir.

  3. Zayıf yazılmış , C derleyicisinin programcıların dönüştürme işlemlerini açıkça belirtmesini gerektirmeden sayısal türler arasında dönüştürmek için otomatik olarak kod ürettiği anlamına gelir. Statik yazım nedeniyle, aynı dönüşüm her zaman program boyunca aynı şekilde gerçekleştirilir. Bir kayan nokta değeri kodda belirli bir noktada bir int değerine dönüştürülürse, bir kayan nokta değeri her zaman koddaki o noktada bir int değerine dönüştürülür. Bu, çalışma zamanında değiştirilemez. Değerin kendisi, programın bir yürütülmesinden diğerine değişebilir ve koşullu ifadeler hangi kod bölümlerinin hangi sırayla çalıştırılacağını değiştirebilir, ancak işlev çağrıları veya koşullu olmayan belirli bir kod bölümü her zaman tam olarak gerçekleşir her çalıştırıldığında aynı işlemler yapılır.

  4. Derlenmiş , insan tarafından okunabilen kaynak kodunu analiz etme ve makine tarafından okunabilir talimatlara dönüştürme işleminin, program çalışmadan önce tam olarak gerçekleştirildiği anlamına gelir. Derleyici bir işlevi derlerken, belirli bir kaynak dosyada ne ile karşılaşacağı konusunda hiçbir bilgisi yoktur. Ancak, derleme (ve birleştirme, bağlama, vb.) Tamamlandıktan sonra, bitmiş yürütülebilir dosyadaki her işlev çalıştırıldığında çağıracağı işlevlere sayısal işaretçiler içerir. Bu nedenle main (), kaynak dosyada bir işlevi daha aşağı çağırabilir. Main () gerçekten çalıştırıldığında, Func_i () adresine bir işaretçi içerecektir.

    Makine kodu çok, çok özeldir. İki tamsayı (3 + 2) ekleme kodu, iki kayan nokta (3.0 + 2.0) ekleme kodundan farklıdır. Her ikisi de bir kayan noktaya int (3 + 2.0) eklemekten farklıdır. Derleyici, bir işlevdeki her nokta için o noktada tam olarak hangi işlemin yapılması gerektiğini belirler ve tam olarak bu işlemi gerçekleştiren kodu üretir. Bu yapıldıktan sonra, işlevi yeniden derlemeden değiştirilemez.

Tüm bu kavramları bir araya getirerek, main () işlevinin Func_i () türünü belirlemek için daha fazla "görememesinin" nedeni, tür analizinin derleme sürecinin en başında gerçekleşmesidir. Bu noktada, kaynak dosyanın main () tanımına kadar olan kısmı okundu ve analiz edildi ve Func_i () tanımı henüz derleyici tarafından bilinmiyor.

Func_i () için olduğu) (ana "görmek" anlamına nedeni çağrı bunu montaj zaten tüm dönüştürülmüş olan, derleme zaten tanımlayıcılar bütün isimleri ve türleri iyileştikten sonra arayarak, çalışma zamanında olur olmasıdır işlevini makine koduna bağlar ve bağlantı, çağrıldığı her yere her işlevin doğru adresini girmiştir.

Tabii ki, kanlı ayrıntıların çoğunu dışarıda bıraktım. Gerçek süreç çok, çok daha karmaşıktır. Umarım sorularınızı cevaplamak için yeterince üst düzey bir genel bakış sunmuş olurum.

Ayrıca, lütfen yukarıda yazdıklarımın özellikle C için geçerli olduğunu unutmayın.

Diğer dillerde, derleyici kaynak kodundan birden fazla geçiş yapabilir ve bu nedenle derleyici önceden tahmin edilmeden Func_i () tanımını alabilir.

Diğer dillerde, işlevler ve / veya değişkenler dinamik olarak yazılabilir, böylece tek bir değişken tutabilir veya farklı zamanlarda tek bir işlev iletilebilir veya geri döndürülebilir, bir tam sayı, bir kayan nokta, bir dize, bir dizi veya bir nesne olabilir.

Diğer dillerde, yazma daha güçlü olabilir ve kayan noktadan tam sayıya dönüşümün açıkça belirtilmesi gerekir. Yine diğer dillerde, "3.0" dizesinden kayan 3.0'a tamsayı 3'e dönüşümün otomatik olarak gerçekleştirilmesine izin vererek, yazma daha zayıf olabilir.

Ve diğer dillerde, kod bir seferde bir satır olarak yorumlanabilir veya bayt koduna derlenebilir ve daha sonra yorumlanabilir veya tam zamanında derlenebilir veya çok çeşitli diğer yürütme şemalarından geçirilebilir.


1
Hepsi bir arada bir yanıt için teşekkür ederim. Senin ve nikie'nin cevabı bilmek istediğim şeydi. Örn Func_()+1: Burada derleme zamanında derleyici vardır türünü bilmek Func_i(), uygun makine kodu oluşturmak üzere. Belki de derleme için çalışma zamanında türü çağırarak işlemek mümkün değildir Func_()+1ya da bunu yapmak mümkündür ama çalışma zamanında programı yavaşlatacaktır. Sanırım şimdilik benim için yeterli.
user106313

1
C'nin dolaylı olarak bildirilen işlevlerinin önemli ayrıntıları: Tür olarak kabul edilirler int func(...)... yani değişken bir argüman listesi alırlar. Bu, bir işlevi tanımlayıp tanımlamayı int putc(char)unutursanız, bunun yerine olarak adlandırılacağı anlamına gelir int putc(int)(çünkü varyasyonlu bir bağımsız değişken listesinden geçen karakter yükseltilir int). Dolayısıyla OP'nin örneği işe yaradı, çünkü imzası örtülü bildirimle eşleşti, ancak bu davranışın neden cesaret kırıldığı (ve uygun uyarılar eklendi) anlaşılabilir.
uliwitness

37

C dilinin tasarım kısıtı, tek geçişli bir derleyici tarafından derlenmesi gerektiği ve bu da onu çok bellek kısıtlı sistemler için uygun kıldığıydı. Bu nedenle, derleyici herhangi bir noktada sadece daha önce bahsedilen şeyleri bilir. Derleyici, işlev bildirimi bulmak için kaynakta ileri atlayamaz ve sonra bu işleve bir çağrı derlemek için geri dönemez. Bu nedenle, tüm semboller kullanılmadan önce bildirilmelidir. Aşağıdaki gibi bir işlevi önceden bildirebilirsiniz:

int Func_i();

Derleyiciye yardım etmek için üstte veya başlık dosyasında.

Örneklerinizde, C dilinin kaçınılması gereken iki şüpheli özelliğini kullanıyorsunuz:

  1. Bir işlev düzgün bir şekilde bildirilmeden önce kullanılırsa, bu bir “örtülü bildirim” olarak kullanılır. Derleyici, işlev imzasını anlamak için hemen bağlamı kullanır. Derleyici, gerçek bildirimin ne olduğunu bulmak için kodun geri kalanını taramayacaktır.

  2. Bir şey tür olmadan bildirilirse, tür olarak alınır int. Bu, örneğin statik değişkenler veya işlev döndürme türleri için geçerlidir.

Dolayısıyla printf("func:%d",Func_i()), örtük bir bildirimimiz var int Func_i(). Derleyici işlev tanımına ulaştığında Func_i() { ... }, bu türle uyumludur. Ancak float Func_i() { ... }bu noktada yazdıysanız , ima int Func_i()ve açık bir şekilde beyan edersiniz float Func_i(). İki bildirim eşleşmediğinden, derleyici size bir hata verir.

Bazı yanlış anlamaları gidermek

  • Derleyici döndürülen değeri bulamıyor Func_i. Açık bir türün olmaması, dönüş türünün intvarsayılan olarak olduğu anlamına gelir . Bunu yapsanız bile:

    Func_i() {
        float f = 42.3;
        return f;
    }

    sonra tür olur int Func_i()ve dönüş değeri sessizce kesilir!

  • Derleyici sonunda gerçek türünü tanıyacak Func_i, ancak örtülü bildirim sırasında gerçek türü bilmiyor. Ancak daha sonra gerçek bildirime ulaştığında, dolaylı olarak bildirilen türün doğru olup olmadığını öğrenebilir. Ancak bu noktada, işlev çağrısı derlemesi zaten yazılmış olabilir ve C derleme modelinde değiştirilemez.


3
@ user31782: Kodun sırası derleme zamanında önemlidir, ancak çalışma zamanında önemli değildir. Program çalıştığında derleyici resim dışındadır. Çalışma zamanı geldiğinde, işlev bir araya getirilip bağlanmış olacak, adresi çözülmüş ve çağrının adres yer tutucusuna yapışmış olacaktır. (Bundan biraz daha karmaşık, ancak temel fikir bu.) İşlemci ileri veya geri dallanabilir.
Blrfl

20
@ user31782: derleyici gelmez değerini yazdırmak. Derleyiciniz programı çalıştırmıyor !!
Monica ile Hafiflik Yarışları

1
@LightnessRacesinOrbit Bunu biliyorum. Yukarıdaki yorumda yanlışlıkla derleyici yazdım çünkü isim işlemcisini unuttum .
user106313

3
@Carcigenicate C, sadece tek bir türe sahip olan B dilinden büyük ölçüde etkilenmiştir: işaretçiler için de kullanılabilen bir kelime genişliği integral sayısal türü. C başlangıçta bu davranışı kopyaladı, ancak şimdi C99 standardından beri tamamen yasadışı. Unitbir tür teorisi bakış açısından güzel bir varsayılan tür yapar, ancak B ve sonra C için tasarlanan metal sistemleri programlamasına yakın pratikliği başarısız olur.
amon

2
@ user31782: Derleyici, işlemci için doğru montajı oluşturmak için değişkenin türünü bilmelidir. Derleyici örtük bulduğunda Func_i(), bu hemen oluşturur ve işlemci devam daha sonra, başka bir yere atlamak için bazı tamsayı almak ve kodunu kaydeder. Derleyici daha sonra Func_itanımı bulduğunda, imzaların eşleştiğinden emin olur ve eğer varsa, derlemeyi Func_i()bu adrese yerleştirir ve bir tamsayı döndürmesini söyler. Programı çalıştırdığınızda, işlemci bu yönergeleri bu değerle izler 3.
Mooing Duck

10

İlk olarak, programlar C90 standardı için geçerlidir, ancak aşağıdakiler için geçerli değildir. örtük int (bir işlevi kendi dönüş türünü vermeden bildirmeye izin verir) ve örtük işlev bildirimi (bir işlevi bildirmeden kullanmaya izin verir) artık geçerli değildir.

İkincisi, düşündüğünüz gibi çalışmıyor.

  1. Sonuç türü C90'da isteğe bağlıdır, bir intsonuç vermez . O da değişken bildirim için de geçerlidir (ancak bir depolama sınıfı vermek zorunda, o staticya extern).

  2. Derleyicinin gördüğü zaman Func_iönceki bir bildirimi olmadan denir, bir bildirimi olduğunu varsayar.

    extern int Func_i();

    ne kadar etkili bir Func_işekilde bildirildiğini görmek için kodda daha fazla görünmüyor . Eğer Func_ibeyan veya tanımlanmamış derlerken, derleyici onun davranışını değiştirmek olmaz main. Örtük bildirim yalnızca işlev içindir, değişken için yoktur.

    Bildirideki boş parametre listesinin işlevin parametre almadığı anlamına gelmediğini ( (void)bunun için belirtmeniz gerekir ), derleyicinin parametre türlerini kontrol etmek zorunda olmadığı ve aynı olacağı anlamına geldiğini unutmayın. varyasyon işlevlerine iletilen bağımsız değişkenlere uygulanan örtük dönüşümler.


Derleyici, main () 'den çıkıp kaynak kodunu arayarak Func_i () tarafından döndürülen değeri bulabilirse, neden açıkça belirtilen Func_i () türünü bulamıyor?
user106313

1
@ user31782 Daha önce Func_i bildirimi yoksa, Func_i'nin bir çağrı ifadesinde kullanıldığını gördüğünüzde, sanki bir tane varmış gibi davranır extern int Func_i(). Hiçbir yere bakmıyor.
AProgrammer

1
@ user31782, derleyici hiçbir yere atlamaz. Bu işlevi çağırmak için kod yayacaktır; döndürülen değer çalışma zamanında belirlenir. Aynı derleme biriminde bulunan bu kadar basit bir işlev durumunda, optimizasyon aşaması işlevi satır içine alabilir, ancak bu, dilin kurallarını düşünürken düşünmeniz gereken bir şey değildir, bir optimizasyondur.
AProgrammer

10
@ user31782'de, programların nasıl çalıştığı hakkında ciddi yanlış anlamalarınız var. O kadar ciddi ki p.se onları düzeltmek için iyi bir yer olduğunu sanmıyorum (belki de sohbet, ama yapmaya çalışmayacağım).
AProgrammer

1
@ user31782: Küçük bir snippet yazmak ve derlemek -S(eğer kullanıyorsanız gcc), derleyici tarafından oluşturulan montaj koduna bakmanıza izin verir. Daha sonra, dönüş değerlerinin çalışma zamanında nasıl işlendiğine dair bir fikriniz olabilir (normalde bir işlemci kaydı veya program yığınında bir miktar boşluk kullanılır).
Giorgio

7

Bir yorum yazdınız:

Yürütme satır satır yapılır. Func_i () tarafından döndürülen değeri bulmanın tek yolu ana menüden atlamaktır

Bu bir yanlış anlamadır: İcra satır satır değil. Derleme satır satır yapılır ve derleme sırasında ad çözümlemesi yapılır ve değerleri döndürmez, yalnızca adları çözümler.

Yararlı bir kavramsal model şudur: Derleyici satırı okuduğunda:

  printf("func:%d",Func_i());

şuna eşdeğer bir kod yayar:

  1. call "function #2" and put the return value on the stack
  2. put the constant string "func:%d" on the stack
  3. call "function #1"

Derleyici ayrıca bazı iç tablolarda function #2henüz adlandırılmamış bir işlev olan Func_i, belirtilmemiş sayıda bağımsız değişken alan ve int (varsayılan) döndüren bir not oluşturur.

Daha sonra, bunu ayrıştırdığında:

 int Func_i() { ...

derleyici Func_iyukarıda belirtilen tabloya bakar ve parametrelerin ve dönüş tipinin eşleşip eşleşmediğini kontrol eder. Yoksa, bir hata mesajı ile durur. Varsa, geçerli adresi dahili işlev tablosuna ekler ve sonraki satıra gider.

Böylece, derleyici Func_iilk başvuruyu ayrıştırdığında "aramadı" . Basitçe bazı tabloda bir not aldı, bir sonraki satırı ayrıştırmaya devam etti. Ve dosyanın sonunda, bir nesne dosyası ve atlama adresleri listesi vardır.

Daha sonra, bağlayıcı tüm bunları alır ve gerçek atlama adresiyle "işlev # 2" ye ilişkin tüm işaretçileri değiştirir, böylece şöyle bir şey yayar:

  call 0x0001215 and put the result on the stack
  put constant ... on the stack
  call ...
...
[at offset 0x0001215 in the file, compiled result of Func_i]:
  put 3 on the stack
  return top of the stack

Çok daha sonra, yürütülebilir dosya çalıştırıldığında, atlama adresi zaten çözülmüş ve bilgisayar 0x1215 adresine atlayabilir. İsim araması gerekmez.

Feragatname : Dediğim gibi, bu kavramsal bir model ve gerçek dünya daha karmaşık. Derleyiciler ve bağlayıcılar bugün her türlü çılgın optimizasyonu yapıyor. Hatta belki aramaya "Bir aşağı yukarı atlamak" Func_iSanmam rağmen. Ama C dilleri bir şekilde tanımlanır olabilir böyle son derece basit bir derleyici yazmak. Bu yüzden çoğu zaman çok kullanışlı bir model.


Cevabınız için teşekkür ederim. Derleyici kodu yayınlayamıyor:1. call "function #2", put the return-type onto the stack and put the return value on the stack?
user106313

1
(Devam.) Ayrıca: Eğer yazarsam ne printf(..., Func_i()+1);- derleyici vardır türünü bilmek Func_ibir yayma eğer karar böylece, add integerya bir add floattalimat. Derleyicinin tür bilgisi olmadan devam edebileceği bazı özel durumlar bulabilirsiniz , ancak derleyicinin tüm durumlar için çalışması gerekir .
nikie

4
@ user31782: Makine talimatları, kural olarak, çok basittir: İki adet 32 ​​bit tamsayı kaydı ekleyin. 16 bitlik bir tam sayı kaydına bir bellek adresi yükleyin. Bir adrese atla. Ayrıca, hiçbir türü yoktur : 32 bitlik bir tam sayı yazmacına 32 bit kayan sayı temsil eden bir bellek konumunu mutlu bir şekilde yükleyebilir ve onunla bazı aritmetik yapabilirsiniz. (Nadiren mantıklı geliyor.) Hayır, doğrudan makine kodu yayamazsınız. Yığın üzerinde çalışma zamanı denetimleri ve ekstra tür verileri ile tüm bunları yapan bir derleyici yazabilirsiniz. Ama bu bir C derleyicisi olmazdı.
nikie

1
@ user31782: Bağlıdır, IIRC. floatdeğerler bir FPU kaydında yaşayabilir - o zaman hiçbir talimat olmazdı. Derleyici sadece derleme sırasında hangi kaydın hangi kayıtta saklandığını izler ve "FP kayıt X'e sabit 1 ekle" gibi şeyler yayınlar. Veya ücretsiz kayıt yoksa yığın üzerinde yaşayabilir. Sonra "yığın işaretçisini 4 arttır" talimatı olur ve değere "yığın işaretçisi - 4" gibi bir şey olarak "atıf yapılır". Ancak tüm bu şeyler yalnızca yığın üzerindeki tüm değişkenlerin boyutları (öncesi ve sonrası) derleme zamanında biliniyorsa işe yarar.
nikie

1
Bu anlayışa ulaştığım tüm tartışmalardan: Derleyicinin, Func_i()veya dahil olmak üzere herhangi bir ifade için makul bir montaj kodu yapması Data_ive türlerini belirlemesi gerekir; montaj dilinde veri türüne çağrı yapmak mümkün değildir. Emin olmak için işleri ayrıntılı olarak incelemem gerekiyor.
user106313

5

İşlemci süresi ve hafızanın pahalı olduğu bir dönemde C ve deklarasyon gerektiren başka diller de tasarlandı. C ve Unix gelişimi oldukça zaman elele gitti ve 3BSD işe ekstra odaya olmadan 1979 yılında ortaya çıktı dek ikincisi sanal bellek yoktu, derleyiciler olma eğilimi tek geçişli yapmadılar çünkü işler tüm dosyanın bir temsilini bir kerede bellekte tutma yeteneğini gerektirir.

Tek geçişli derleyiciler, bizim gibi, geleceği görememe konusunda üzülüyor. Bu, kesin olarak bildikleri şeylerin, derlenilen kod satırından önce açıkça söylendikleri şey olduğu anlamına gelir. İkimiz için de Func_i()açıktır ve kaynak dosyada daha sonra bildirilmiştir, ancak bir seferde küçük bir kod parçasında çalışan derleyicinin hiçbir ipucu yoktur.

Erken C'de (AT&T, K&R, C89), foo()bildirimden önce bir fonksiyonun kullanılması fiili veya fiili bir beyan ile sonuçlanmıştır int foo(). Örneğiniz , derleyicinin sizin için bildirdiği ile eşleştiği Func_i()için bildirildiğinde çalışır int. Başka bir türle değiştirmek, bir anlaşmazlığa neden olur çünkü artık derleyici açık bir bildirim yokken seçtikleriyle eşleşmez. Bu davranış, bildirilmemiş bir işlev kullanımının hata haline geldiği C99'da kaldırılmıştır.

Peki dönüş türleri ne olacak?

Çoğu ortamda nesne kodu çağırma kuralı, yalnızca derlenenler ve bağlayıcılar ile başa çıkmak için nispeten kolay olan çağrılan işlevin adresini bilmeyi gerektirir. Yürütme işlevin başlangıcına atlar ve geri döndüğünde geri gelir. Başka bir şey, özellikle de argümanların geçilmesi ve bir dönüş değeri, tamamen arayan tarafından belirlenir ve çağrı kuralı olarak adlandırılan bir düzenlemede çağırılır . Her ikisi de aynı kurallar dizisini paylaştığı sürece, bir programın bu kuralları paylaşan herhangi bir dilde derlenmiş olsun, diğer nesne dosyalarındaki işlevleri çağırması mümkün olur. (Bilimsel hesaplamada, FORTRAN'ı çağıran ve bunun tersini yapan birçok C ile karşılaşırsınız ve bunu yapabilme yeteneği, bir çağrı kuralına sahip olmaktan gelir.)

Erken C'nin diğer bir özelliği, şimdi bildiğimiz prototiplerin mevcut olmamasıydı. Bir işlevin dönüş türünü (örn. int foo()) Bildirebilirsiniz , ancak bağımsız değişkenlerini (ör int foo(int bar). Bir seçenek değildi) bildirebilirsiniz. Bu, yukarıda belirtildiği gibi, program her zaman argümanlar tarafından belirlenebilecek bir çağrı kuralına bağlı kaldığı için vardı. Yanlış türde argümanlara sahip bir işlev çağırdıysanız, bu bir çöp içeri, çöp dışarı durumuydu.

Nesne kodu bir dönüş kavramına sahip olduğundan, bir dönüş türü olmadığından, bir derleyici döndürülen değerle başa çıkmak için dönüş türünü bilmek zorundadır. Makine talimatlarını çalıştırırken, hepsi sadece bitler ve işlemci, karşılaştırmaya çalıştığınız belleğin doublegerçekten içinde olup olmadığını umursamıyor int. Sadece sorduğunuz şeyi yapar ve eğer kırırsanız, her iki parçaya da sahipsiniz.

Şu kod parçalarını düşünün:

double foo();         double foo();
double x;             int x;
x = foo();            x = foo();

Soldaki kod, bir çağrıya kadar derlenir ve foo()ardından çağrı / iade kuralı aracılığıyla sağlanan sonucu xdepolanan herhangi bir yere kopyalar . Bu kolay durum.

Sağdaki kod bir tür dönüşümü gösterir ve derleyicilerin bir işlevin dönüş türünü bilmesi gerekir. Kayan nokta sayıları, diğer kodların görmeyi beklediği yerlerde belleğe dökülemez intçünkü gerçekleşen sihirli bir dönüşüm yoktur. Sonuçta bir tamsayı olması gerekiyorsa, işlemciyi depolamadan önce dönüştürme işlemi için yönlendiren yönergeler olmalıdır. foo()Önceden dönen türünü bilmeden, derleyicinin dönüşüm kodunun gerekli olduğu hakkında hiçbir fikri yoktur.

Çoklu geçiş derleyicileri, her şeyden birini etkinleştirir, bunlardan biri ilk kez kullanıldıktan sonra değişkenleri, işlevleri ve yöntemleri bildirme yeteneğidir. Bu, derleyici kodu derlemeye geldiğinde geleceği zaten gördüğü ve ne yapacağını bildiği anlamına gelir. Örneğin Java, sözdiziminin kullanımdan sonra bildirime izin vermesi nedeniyle çoklu geçişi zorunlu kılar.


Cevabınız için teşekkür ederim (+1). Derleyicinin, montajcının, işlemcinin vb. bu fonksiyonun ardından dil, bilgisayarın herhangi bir hata vermeden bu değeri bulmasını sağlar . Şimdi neden bilgisayar türü bulamıyor ? Neden Data_i türününFunc_i() dönüş değeri bulunamadığı bulunamadı.
user106313 23:14

Hala tatmin olmadım. double foo(); int x; x = foo();sadece hatayı verir. Bunu yapamayacağımızı biliyorum. Benim sorum fonksiyon çağrısında işlemci yalnızca dönüş değerini bulur; neden dönüş tipini de bulamıyor?
user106313

1
@ user31782: Olmamalı. Bunun için bir prototip var foo(), bu yüzden derleyici onunla ne yapacağını biliyor.
Blrfl

2
@ user31782: İşlemcilerin herhangi bir geri dönüş kavramı yok.
Blrfl

1
@ user31782 Derleme zamanı sorusu için: Tüm bu tür analizlerin derleme zamanında yapılabileceği bir dil yazmak mümkündür. C öyle bir dil değil. C derleyicisi bunu yapamaz çünkü bunu yapmak için tasarlanmamıştır. Farklı tasarlanmış olabilir mi? Elbette, ancak bunu yapmak için çok daha fazla işlem gücü ve bellek gerekir. Sonuçta öyle değildi. Günün bilgisayarlarının en iyi şekilde başa çıkabileceği şekilde tasarlanmıştır.
Mr.Mindor
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.