Düşünebileceğim bir avantaj yok (ancak alttaki JasonS notuna bakın), bir kod satırını bir işlev veya altyordam olarak tamamlıyor. Belki fonksiyonu "okunabilir" olarak adlandırabilirsiniz. Ancak bu satırı da yorumlayabilirsiniz. Ve bir kod satırını bir fonksiyona sarmak kod hafızasına, yığın alanına ve yürütme süresine mal olduğu için, bana çoğunlukla bunun karşı üretken olduğu görülüyor . Öğretme durumunda mı? Bir anlam ifade edebilir. Ancak bu, öğrencilerin sınıfına, önceden hazırlanmalarına, müfredata ve öğretmene bağlıdır. Çoğunlukla, bunun iyi bir fikir olmadığını düşünüyorum. Ama bu benim görüşüm.
Bu da bizi en alt düzeye getiriyor. Geniş soru alanınız onlarca yıldır tartışma konusu ve günümüzde tartışma konusu olmaya devam ediyor. Yani, en azından sorunuzu okuduğumda, bana fikir tabanlı bir soru gibi geliyor (sorduğunuz gibi).
Durum hakkında daha ayrıntılı olmanız ve birincil olarak tuttuğunuz hedefleri dikkatli bir şekilde tanımlamanız durumunda, görüşe dayalı olmaktan uzaklaşabilir. Ölçüm araçlarınızı ne kadar iyi tanımlarsanız, cevaplar o kadar objektif olabilir.
Genel olarak, herhangi bir kodlama için aşağıdakileri yapmak istersiniz . (Aşağıda, tüm hedeflere ulaşan farklı yaklaşımları karşılaştırdığımızı varsayacağım. Açıkçası, gerekli görevleri yerine getiremeyen herhangi bir kod, nasıl yazıldığından bağımsız olarak başarılı olan koddan daha kötüdür.)
- Yaklaşımınız hakkında tutarlı olun, böylece kodunuzu okuduğunuzda kodlama sürecinize nasıl yaklaştığınız konusunda bir anlayış geliştirilebilir. Tutarsız olmak, muhtemelen en kötü suçtur. Sadece başkaları için zorlaştırmakla kalmaz, aynı zamanda yıllar sonra koda geri dönmenizi de zorlaştırır.
- Mümkün olduğu ölçüde, çeşitli fonksiyonel bölümlerin başlatılmasını sipariş vermeden gerçekleştirilebilecek şekilde düzenlemeye çalışın. Siparişin gerekli olduğu durumlarda, yüksek derecede ilişkili iki alt fonksiyonun yakın kuplajından kaynaklanıyorsa, zarar vermeden yeniden sıralanabilmesi için her ikisi için tek bir başlatmayı düşünün. Bu mümkün değilse, başlatma siparişi gereksinimini belgeleyin.
- Mümkünse, bilgileri tek bir yerde kapsülleyin . Sabitler, kodun her yerinde çoğaltılmamalıdır. Bazı değişkenleri çözen denklemler tek bir yerde mevcut olmalıdır. Ve bunun gibi. Kendinizi çeşitli konumlarda gerekli davranışları gerçekleştiren bazı satırları kopyalayıp yapıştırırken bulursanız, bu bilgiyi tek bir yerde yakalamanın ve gerektiğinde kullanmanın bir yolunu düşünün. Belirli bir şekilde yürüdü gereken bir ağaç yapısı varsa Örneğin, do notağaç yürüme kodunu ağaç düğümleri arasında dolaşmanız gereken her yerde çoğaltın. Bunun yerine, ağaç yürüme yöntemini tek bir yerde yakalayın ve kullanın. Bu şekilde, ağaç değişirse ve yürüme yöntemi değişirse, endişelenecek tek bir yeriniz vardır ve kodun geri kalan kısmı "doğru çalışır."
- Tüm rutinlerinizi büyük, düz bir kağıda yayarsanız, okları diğer rutinler tarafından çağrıldığı gibi birbirine bağlarsanız, herhangi bir uygulamada çok sayıda ok içeren rutinlerin "kümeleri" olduğunu göreceksiniz kendi aralarında ama grup dışında sadece birkaç ok. Bu nedenle , yakından bağlı rutinlerin diğer grupları arasında sıkıca bağlı rutinlerin ve gevşek bağlı bağlantıların doğal sınırları olacaktır . Kodunuzu modüller halinde düzenlemek için bu gerçeği kullanın. Bu, kodunuzun görünür karmaşıklığını önemli ölçüde sınırlayacaktır.
Yukarıdakiler tüm kodlamalar için genellikle doğrudur. Parametrelerin, yerel veya statik küresel değişkenlerin vb. Kullanımını tartışmadım. Bunun nedeni, gömülü programlama için uygulama alanının genellikle aşırı ve çok önemli yeni kısıtlamalar koyması ve her gömülü uygulamayı tartışmadan hepsini tartışmak imkansızdır. Ve bu zaten burada değil.
Bu kısıtlamalar bunlardan herhangi biri (ve daha fazlası) olabilir:
- Küçük RAM içeren ve neredeyse hiç G / Ç pin sayımı olmayan son derece ilkel MCU'lar gerektiren ciddi maliyet sınırlamaları. Bunlar için yepyeni kurallar uygulanır. Örneğin, çok fazla kod alanı olmadığından montaj kodunu yazmanız gerekebilir. YALNIZCA statik değişkenleri kullanmanız gerekebilir, çünkü yerel değişkenlerin kullanımı çok maliyetli ve zaman alıcıdır. Alt yordamların aşırı kullanımından kaçınmanız gerekebilir, çünkü (örneğin, bazı Microchip PIC parçaları) alt yordam dönüş adreslerinin saklanacağı yalnızca 4 donanım kaydı vardır. Bu nedenle, kodunuzu önemli ölçüde "düzleştirmek" zorunda kalabilirsiniz. Vb.
- MCU'nun çoğunu başlatmak ve kapatmak için dikkatli bir şekilde hazırlanmış kod gerektiren ve tam hızda çalışırken kodun yürütme süresine ciddi sınırlamalar getiren ciddi güç sınırlamaları. Yine, bu bazen bir takım kodlama gerektirebilir.
- Ciddi zamanlama gereksinimleri. Örneğin, açık bir drenaj 0'ın iletiminin 1'in iletimi ile tam olarak aynı sayıda devir alması gerektiğinden emin olmam gereken zamanlar var. bu zamanlamaya tam bir bağıl faz ile. Bu, C'nin burada KULLANILAMAZ anlamına geliyordu. Bu garantiyi vermenin SADECE yolu montaj kodunu dikkatlice hazırlamaktır. (Ve o zaman bile, her zaman tüm ALU tasarımlarında değil.)
Ve bunun gibi. (Yaşam için kritik tıbbi cihazların kablolama kodunun da kendi dünyası vardır.)
Buradaki sonuç, gömülü kodlamanın genellikle herkes için ücretsiz olmadığı ve bir iş istasyonunda olduğu gibi kod yazabileceğinizdir. Çok çeşitli çok zor kısıtlamalar için genellikle ciddi, rekabetçi nedenler vardır. Ve bunlar daha geleneksel ve stok yanıtlarına şiddetle karşı çıkabilir .
Okunabilirlik ile ilgili olarak, okuduğumda öğrenebileceğim tutarlı bir şekilde yazılmışsa kodun okunabilir olduğunu görüyorum. Ve kasıtlı olarak kasıtlı gizleme girişimi olmadığında. Gerçekten daha fazlası gerekmez.
Okunabilir kod oldukça verimli olabilir ve daha önce bahsettiğim tüm yukarıdaki gereksinimleri karşılayabilir. Önemli olan, yazdığınız her kod satırının kodlama sırasında montaj veya makine düzeyinde ne ürettiğini tam olarak anlamanızdır. C ++ birçok durum vardır çünkü burada programcı üzerinde ciddi bir yük getirmektedir özdeş C ++ kodunun parçacıkları aslında üretmek farklı çok farklı performanslar var makine kod parçacıkları. Ancak C, genel olarak, çoğunlukla "gördüğünüz şey ne elde edersiniz" dilidir. Yani bu konuda daha güvenli.
JasonS başına DÜZENLE:
1978'den beri C ve 1987'den beri C ++ kullanıyorum ve hem ana bilgisayarlar, mini bilgisayarlar hem de (çoğunlukla) gömülü uygulamalar için çok fazla deneyim yaşadım.
Jason, 'satır içi'yi değiştirici olarak kullanma hakkında bir yorum getiriyor. (Benim bakış açımdan, bu nispeten "yeni" bir özelliktir, çünkü hayatımın belki de yarısı veya C ve C ++ kullanarak daha fazlası için mevcut değildi.) Satır içi işlevlerin kullanımı aslında bu tür çağrılar yapabilir (hatta kod) oldukça pratik. Ve mümkün olduğunda, derleyicinin uygulayabileceği yazma nedeniyle bir makro kullanmaktan çok daha iyidir.
Ancak sınırlamalar da var. Birincisi "ipucunu almak" için derleyiciye güvenemezsiniz. Olabilir ya da olmayabilir. Ve ipucunu almamak için iyi nedenler var. (Fonksiyonun adresi alınırsa bariz Örneğin, bu gerektirdiği fonksiyonun örnekleme ve ... bir çağrı gerektirecektir arama yapmak için adres kullanımını. Kod, daha sonra satır içine yerleştirilmiş olamaz.) Vardır diğer nedenler de. Derleyiciler, ipucunu nasıl ele alacağına karar verdikleri çok çeşitli kriterlere sahip olabilirler. Ve bir programcı olarak bu ,Derleyicinin bu yönünü öğrenmek için biraz zaman harcayın yoksa başka türlü kusurlu fikirlere dayalı kararlar verebilirsiniz. Bu nedenle, hem kodun yazarına hem de herhangi bir okuyucuya ve kodu başka bir derleyiciye taşımayı planlayan herkese bir yük ekler.
Ayrıca, C ve C ++ derleyicileri ayrı derlemeyi destekler. Bu, proje için ilgili herhangi bir kodu derlemeden bir parça C veya C ++ kodunu derleyebilecekleri anlamına gelir. Satır içi kod oluşturmak için, derleyicinin başka türlü yapmayı seçebileceğini varsayarsak, yalnızca "kapsamda" bildirimine sahip olmakla kalmaz, aynı zamanda tanımına da sahip olmalıdır. Genellikle, programcılar 'satır içi' kullanıyorlarsa, durumun böyle olduğundan emin olmak için çalışırlar. Ancak hataların içine girmesi kolaydır.
Genel olarak, uygun olduğunu düşündüğüm yerde satır içi de kullanırken, buna güvenemeyeceğimi varsayıyorum. Performans önemli bir gereklilikse ve bence OP daha açık bir şekilde "işlevsel" bir rotaya gittiklerinde önemli bir performans artışı olduğunu açıkça yazmışsa, o zaman kesinlikle satır içi bir kodlama uygulaması olarak güvenmekten kaçınmayı ve bunun yerine, biraz farklı ama tamamen tutarlı bir kod yazma modeli izler.
Ayrı bir derleme adımı için 'satır içi' ve tanımların "kapsamda" olduğuna dair son bir not. Bağlama aşamasında yapılacak işin (her zaman güvenilir olmadığı) mümkündür. Bu, yalnızca bir C / C ++ derleyicisinin nesne dosyalarına yeterli miktarda bağlayıcının bir bağlayıcının 'satır içi' istekleri üzerinde işlem yapmasına izin vermesi durumunda oluşabilir. Şahsen bu özelliği destekleyen bir bağlayıcı sistem (Microsoft'un dışında) yaşamadım. Ancak ortaya çıkabilir. Yine, buna güvenilip güvenilmeyeceği koşullara bağlı olacaktır. Ancak, aksi takdirde iyi kanıtlara dayanmadığını bilmedikçe, genellikle bağlayıcıya kürek çekilmediğini varsayıyorum. Ve eğer ona güvenirsem, önemli bir yerde belgelenecek.
C ++
İlgilenenler için, bugün hazır kullanılabilirliğine rağmen, gömülü uygulamaları kodlarken neden C ++ 'a karşı oldukça ihtiyatlı davrandığımın bir örneği. Tüm gömülü C ++ programcılarının soğuk bilmeleri gerektiğini düşündüğüm bazı terimleri atacağım :
- kısmi şablon uzmanlığı
- vtables
- sanal temel nesne
- aktivasyon çerçevesi
- aktivasyon çerçevesi çözme
- kurucularda akıllı işaretçilerin kullanımı ve neden
- dönüş değeri optimizasyonu
Bu sadece kısa bir liste. Bu terimler ve neden bunları listelediğimi (ve burada listelemediğim birçok şey) zaten bilmiyorsanız , proje için bir seçenek değilse, gömülü çalışma için C ++ kullanımına karşı tavsiyede bulunacağım. .
Sadece bir lezzet elde etmek için C ++ istisna semantiğine hızlı bir göz atalım.
Bir C ++ derleyicisi , ayrı derleme birimi ayrı olarak ve farklı bir zamanda derlenen ne tür bir istisna işlemesi gerekebileceğine dair hiçbir fikre sahip olmadığında derleme birimi için doğru kodu üretmelidir . BAB
Bazı derleme birimleri bazı işlevlerin bir parçası olarak bulunan bu kod sırasını alın :A
.
.
foo ();
String s;
foo ();
.
.
Tartışma amacıyla, derleme birimi kaynağında hiçbir yerde 'try..catch' kullanmaz . 'Atmak' da kullanmaz. Aslında, C ++ kitaplık desteğini kullanması ve String gibi nesneleri işleyebilmesi dışında, bir C derleyicisi tarafından derlenemeyen herhangi bir kaynak kullanmadığını varsayalım. Bu kod, String sınıfı gibi birkaç C ++ özelliğinden yararlanmak için biraz değiştirilmiş bir C kaynak kodu dosyası bile olabilir.A
Ayrıca, foo () ' nun derleme birimi bulunan harici bir prosedür olduğunu ve derleyicinin bunun için bir bildirimi olduğunu, ancak tanımını bilmediğini varsayın .B
C ++ derleyicisi, foo () öğesine yapılan ilk çağrıyı görür ve foo () bir istisna atarsa, normal bir etkinleştirme çerçevesinin gevşemesine izin verebilir. Başka bir deyişle, C ++ derleyicisi, istisna işlemede yer alan çerçeve çözme işlemini desteklemek için bu noktada fazladan bir kod gerekmediğini bilir.
Ancak String s oluşturulduktan sonra, daha sonra bir istisna oluşursa, C ++ derleyicisi bir çerçeve çözülmesine izin verilebilmesi için düzgün şekilde yok edilmesi gerektiğini bilir. Yani ikinci foo () çağrısı anlamsal olarak ilk çağrıdan farklıdır. 2. foo () çağrısı bir istisna atarsa (bunu yapabilir veya yapmayabilir), derleyici normal çerçevenin gevşemesine izin vermeden önce String'lerin imhasını işlemek için tasarlanmış kod yerleştirmiş olmalıdır. Bu, ilk foo () çağrısı için gereken koddan farklıdır .
( Bu sorunu sınırlamaya yardımcı olmak için C ++ 'a ek süslemeler eklemek mümkündür . Ancak gerçek şu ki, C ++ kullanan programcılar yazdıkları her kod satırının etkilerinden çok daha fazla haberdar olmalıdırlar.)
C'nin malloc'undan farklı olarak, C ++ 'ın yeni özellikleri ham bellek ayırma gerçekleştiremediğinde sinyal vermek için istisnalar kullanır. 'Dynamic_cast' de öyle. (C ++ 'daki standart istisnalar için Stroustrup'un 3. baskısı, C ++ Programlama Dili, sayfa 384 ve 385'e bakın.) Derleyiciler bu davranışın devre dışı bırakılmasına izin verebilir. Ancak genel olarak, istisnalar gerçekte gerçekleşmediğinde ve derlenen fonksiyonun herhangi bir istisna işleme bloğu olmasa bile, oluşturulan koddaki uygun şekilde oluşturulmuş istisna işleme prologları ve epilogları nedeniyle bir miktar ek yüke maruz kalacaksınız. (Stroustrup bunu açıkça ağıt yaktı.)
Kısmi şablon uzmanlığı olmadan (tüm C ++ derleyicileri bunu desteklemez), şablonların kullanımı gömülü programlama için felakete yol açabilir. Onsuz, kod patlaması, küçük belleğe gömülü bir projeyi bir flaşta öldürebilecek ciddi bir risktir.
Bir C ++ işlevi bir nesneyi döndürdüğünde, adlandırılmamış bir derleyici geçici oluşturulur ve yok edilir. Bazı C ++ derleyicileri, yerel bir nesne yerine return deyiminde bir nesne yapıcısı kullanılırsa, bir nesnenin inşaat ve imha ihtiyaçlarını azaltarak etkin kod sağlayabilir. Ancak her derleyici bunu yapmaz ve birçok C ++ programcısı bu "dönüş değeri optimizasyonunun" farkında bile değildir.
Bir nesne yapıcısına tek bir parametre türünün sağlanması, C ++ derleyicisinin programcıya tamamen beklenmedik yollarla iki tür arasında bir dönüşüm yolu bulmasına izin verebilir. Bu tür "akıllı" davranışlar C'nin bir parçası değildir.
Bir taban türünü belirten bir catch cümlesi, atılan nesne nesnenin "dinamik tipi" değil, catch deyiminin "statik türü" kullanılarak kopyalandığından, atılan türetilen bir nesneyi "dilimleyecektir". Nadir olmayan bir istisna sefaleti kaynağı (gömülü kodunuzda istisnalar bile alabileceğinizi düşündüğünüzde).
C ++ derleyicileri sizin için otomatik olarak yapıcıları, yıkıcıları, kopya oluşturucuları ve atama işleçlerini istenmeyen sonuçlarla oluşturabilir. Bunun detayları ile tesis kazanmak zaman alır.
Türetilmiş nesnelerin dizilerini temel nesnelerin dizilerini kabul eden bir işleve geçirmek, nadiren derleyici uyarıları oluşturur, ancak neredeyse her zaman yanlış davranış verir.
Nesne yapıcısında bir istisna meydana geldiğinde C ++ kısmen oluşturulmuş nesnelerin yıkıcısını çağırmadığından, bir istisna oluşursa yapıcıdaki yerleşik parçaların doğru şekilde yok edilmesini garanti etmek için kurucularda istisnaların işlenmesi genellikle "akıllı işaretçiler" i zorunlu kılar. . (Bkz. Stroustrup, sayfa 367 ve 368.) Bu, C ++ 'da iyi sınıflar yazmada yaygın bir konudur, ancak C'de inşaat ve yıkım anlambilimi bulunmadığından elbette C'de önlenir. Yapıyı işlemek için uygun kod yazma bir nesnenin içindeki alt nesneler, C ++ 'daki bu benzersiz semantik sorunla başa çıkması gereken kod yazmak anlamına gelir; diğer bir deyişle "yazarak" C ++ semantik davranışları.
C ++, nesne parametrelerine iletilen nesneleri kopyalayabilir. Örneğin, aşağıdaki parçalarda "rA (x);" C ++ derleyicisinin p parametresi için bir yapıcı çağırmasına neden olabilir, daha sonra kopya yapıcısını x nesnesini p parametresine aktarmak için çağırır, daha sonra rA işlevinin dönüş nesnesi (adsız geçici) için başka bir kurucu p parametresinden kopyalandı. Daha da kötüsü, eğer A sınıfı inşa edilmesi gereken kendi nesneleri varsa, bu disasterous teleskop olabilir. (AC programcısı, C programcılarının bu kadar kullanışlı bir sözdizimine sahip olmadıkları ve tüm ayrıntıları birer birer ifade etmeleri gerektiğinden, el optimizasyonundan kaçınacaktır.)
class A {...};
A rA (A p) { return p; }
// .....
{ A x; rA(x); }
Son olarak, C programcıları için kısa bir not. longjmp (), C ++ 'da taşınabilir bir davranışa sahip değildir. (Bazı C programcıları bunu bir tür "istisna" mekanizması olarak kullanır.) Bazı C ++ derleyicileri aslında longjmp alındığında temizlenecek şeyleri ayarlamaya çalışır, ancak bu davranış C ++ 'da taşınabilir değildir. Derleyici yapılandırılmış nesneleri temizlerse, taşınabilir değildir. Derleyici bunları temizlemezse, kod longjmp'nin sonucu olarak oluşturulan nesnelerin kapsamını terk ederse ve davranış geçersizse nesneler bozulmaz. (Eğer foo () içinde longjmp kullanımı bir kapsam bırakmazsa, davranış iyi olabilir.) Bu, C gömülü programcılar tarafından çok sık kullanılmaz, ancak kullanmadan önce bu sorunlardan haberdar olmalıdırlar.