C dizeleri her zaman boş bırakılır mı yoksa platforma mı bağlı?


13

Şu anda gömülü sistemlerle çalışıyorum ve işletim sistemi olmayan bir mikroişlemcide dizeleri uygulamanın yollarını buluyorum. Şimdiye kadar ne yapıyorum sadece NULL sonlandırıldı karakter işaretçiler sahip ve onları NULL sonunu ifade dizeleri olarak muamele. Bunun oldukça yaygın olduğunu biliyorum, ama her zaman böyle olabilir mi?

Sormamın nedeni, belki de bir noktada gerçek zamanlı bir işletim sistemi kullanmayı düşünüyordum ve mevcut kodumu olabildiğince tekrar kullanmak istiyorum. Orada çeşitli seçenekler için, ben hemen hemen dizeleri aynı çalışmasını bekleyebilir miyim?

Benim durumum için daha spesifik olalım. Bir seri port üzerinden komutları alan ve işleyen bir sistem uyguluyorum. Komut işleme kodumu aynı tutabilir ve sonra RTOS (komutları içeren) üzerinde oluşturulan dize nesnelerinin NULL sonlanmasını bekleyebilir miyim? Veya işletim sistemine bağlı olarak farklı olur mu?

Güncelleme

Bu soruya bir göz atmanız tavsiye edildikten sonra sorduğum soruya tam olarak cevap vermediğini belirledim. Sorunun kendisi, sorduğumdan tamamen farklı olan bir dizenin uzunluğunun her zaman geçirilip geçirilmeyeceğini soruyor ve cevapların bazılarında yararlı bilgiler olmasına rağmen, tam olarak aradığım şey değiller. Cevaplar ya da neden nedenlerini vermek gibiydi orada değil bir null karakteri ile bir dize sonlandırmak için. Sorduğum şey arasındaki fark, farklı platformların doğuştan gelen dizelerinin, dışarı çıkıp mantıklıysa, her bir platformu denemek zorunda kalmadan kendi dizelerini boş olarak sonlandırmasını bekleyebilmemdir.


3
C'yi uzun zamandır kullanmadım, ancak NULL sonlandırılmış dizeleri kullanmayan bir uygulamaya girdiğim zamanı düşünemiyorum. Standart C'nin bir parçası, eğer doğru hatırlıyorsam (dediğim gibi, bir süredir ...)
MetalMikester

1
Ben C uzmanı değilim, ama bildiğim kadarıyla C tüm dizeleri char, null sonlandırılmış char dizileridir. Kendi dize türünüzü oluşturabilirsiniz, ancak tüm dize düzenleme işlevlerini kendiniz uygulamanız gerekir.
Machado


1
@MetalMikester Bu bilgilerin standart C spesifikasyonunda bulunabileceğini mi düşünüyorsunuz?
Snoop

3
@Snoopy Büyük olasılıkla, evet. Ama gerçekten, C'deki dizelerden bahsederken, sadece NULL ile biten bir karakter dizisidir ve bu, standart olmayan bir dize kütüphanesi kullanmadığınız sürece, ama yine de burada bahsettiğimiz şey değil. Özellikle C'nin güçlü yönlerinden birinin taşınabilirlik olmasıyla buna saygı duymayan bir platform bulacağınızdan şüpheliyim.
Metal

Yanıtlar:


42

"C dizeleri" olarak adlandırılan şeyler herhangi bir platformda geçersiz kılınacaktır. Standart C kitaplığı işlevleri bir dizenin sonunu bu şekilde belirler.

C dilinde, bir boş karakterle bitmeyen bir dizi karaktere sahip olmanızı engelleyen hiçbir şey yoktur. Ancak bir dizenin sonundan kaçmak için başka bir yöntem kullanmanız gerekecektir.


4
sadece eklemek için; genellikle dize uzunluğunu takip etmek için bir yerde bir tamsayı vardır ve daha sonra doğru yapmak için özel bir veri yapısı ile sonuçlanırsınız, Qt'deki QString sınıfı
Rudolf Olah

8
Vaka örneği: En az beş farklı dize biçimi kullanan bir C programı ile çalışıyorum: boş sonlandırılmış chardiziler, charilk baytta kodlanan uzunluktaki diziler (genellikle "Pascal dizeleri" olarak bilinir), wchar_ther ikisinin de tabanlı sürümleri ve charher iki yöntemi birleştiren diziler: ilk baytta kodlanmış uzunluk ve dizeyi sonlandıran boş karakter.
Mark

4
@Mark Çok sayıda 3. taraf bileşen / uygulama veya eski bir kod karmaşası ile arayüz mü?
Dan Is Fiddling By Firelight

2
@ DanNeely, yukarıdakilerin hepsi. Klasik MacOS ile arayüz için Pascal dizeleri, dahili kullanım ve Windows için C dizeleri, Unicode desteği eklemek için geniş dizeler ve piç dizeleri, çünkü birileri zeki olmaya ve aynı anda hem MacOS hem de Windows ile arayüz oluşturabilecek bir dize yapmaya çalıştı.
Mark

1
@Mark ... ve tabii ki hiç kimse teknik borcunu ödemek için para harcamak istemiyor çünkü klasik MacOS çoktan öldü ve piç dizeleri her dokunulmalarında bir çift clusterfrak. Benim sempatilerim.
Dan Is Fiddling By Firelight

22

Sonlandırma karakterinin belirlenmesi, değişmez değerler için derleyiciye ve genel olarak dizeler için standart kütüphanenin uygulanmasına bağlıdır. İşletim sistemi tarafından belirlenmez.

NULFesih sözleşmesi standart öncesi C'ye geri döner ve 30+ yıl içinde başka bir şey yapan bir ortama girdiğimi söyleyemem. Bu davranış C89'da kodlanmıştır ve C dili standardının bir parçası olmaya devam etmektedir (bağlantı C99 taslağına aittir):

  • Bölüm 6.4.5 , dize hazır bilgilerine NULa NULeklenmesini gerektirerek sonlandırılmış dize için sahne hazırlar .
  • Bölüm 7.1.1, bir dizeyi "ilk boş karakterle sonlandırılmış ve ilk boş karakter dahil olmak üzere bitişik karakter dizisi" olarak tanımlayarak standart kütüphanedeki işlevlere getirir .

Birisinin başka bir karakter tarafından sonlandırılan dizeleri işleyen işlevleri yazamamasının bir nedeni yoktur, ancak hedefiniz programcılara uymuyorsa, çoğu durumda yerleşik standardı satın almanın bir nedeni yoktur. :-)


2
Bunun bir nedeni, aynı dizenin sonunu defalarca bulmak zorunda kalmamaktır.
Paŭlo Ebermann

@ PaŭloEbermann Doğru. Bir yerine iki değer geçirme pahasına. Eğer olduğu gibi bir dize değişmezi geçerseniz hangi biraz rahatsız edici printf("string: \"%s\"\n", "my cool string"). Bu durumda dört parametre geçirmenin tek yolu (bir tür sonlandırma baytı dışında), bir dizeyi std::stringkendi sorunları ve sınırlamaları olan C ++ 'da olduğu gibi tanımlamak olacaktır .
cmaster

1
Bölüm 6.4.5 , bir dize hazır bilgisinin boş bir karakterle sonlandırılmasını gerektirmez . " Bir karakter dizesi değişmezinin bir dize olması gerekmez (bkz. 7.1.1), çünkü boş bir karakter bir \ 0 kaçış dizisi içine gömülebilir. "
bzeaman

1
@bzeaman Dipnot, 7.1.1'in bir dize tanımını karşılamayan bir dize hazır bilgisi oluşturabileceğinizi söylüyor, ancak ona atıfta bulunan cümle uyumlu derleyiciler diyor - NULne olursa olsun onları sonlandırıyor: "Çeviri aşamasında 7, bir bayt veya kod bir dizgi değişmezinden veya değişmez değerlerinden kaynaklanan her çok baytlı karakter dizisine sıfır değeri eklenir. " 7.1.1'in tanımını kullanan kütüphane fonksiyonları, NULbuldukları ilk anda durur ve ötesinde ek karakterlerin olduğunu bilmez veya umursamaz.
Blrfl

Ben düzeltilmiş duruyorum. 'Null' gibi çeşitli terimler aradım ama 'sıfır değerinden' bahseden 6.4.5.5'i kaçırdım.
09:38

3

Ben gömülü sistemlerle çalışıyorum ... işletim sistemi yok ... Ben ... NULL sonlandırılmış karakter işaretçileri olan ve onlara NULL sonunu ifade eden dizeler gibi davranma fikrini kullanıyorum. Bunun oldukça yaygın olduğunu biliyorum, ama her zaman böyle olabilir mi?

C dilinde dize veri türü yoktur, ancak dize değişmez değerleri vardır .

Programınıza bir dize hazır bilgisi koyarsanız, genellikle NUL sonlandırılır (ancak aşağıdaki yorumlarda tartışılan özel duruma bakın.) Yani, "foobar"bir const char *değerin beklendiği bir yere koyarsanız , derleyici yayılacaktır foobar⊘programınızın const / code segmentine / bölümüne gidin ve ifadenin değeri, fkarakteri depoladığı adrese bir işaretçi olacaktır . (Not: NUL baytını belirtmek için kullanıyorum .)

C dilinin dizeleri olduğu diğer tek anlam, NUL sonlandırılmış karakter dizileri üzerinde çalışan bazı standart kütüphane rutinlerine sahip olmasıdır. Bu kütüphane rutinleri, siz kendiniz taşımadığınız sürece çıplak metal bir ortamda mevcut olmayacaktır.

Bunlar sadece kod --- kendiniz yazdığınız koddan farklı değil. Onları taşıdığınızda onları kırmazsanız, o zaman her zaman yaptıklarını yapacaklardır (örneğin, bir NUL'de durun).


2
Re: "Programınıza bir dize değişmezi koyarsanız, her zaman NUL sonlandırılacaktır": Bundan emin misiniz? Eminim (örneğin) char foo[4] = "abcd";dört karakterden oluşan bir boş olmayan sonlandırılmış dizi oluşturmak için geçerli bir yoldur.
ruakh

2
@ruakh, Hata! bu benim düşünmediğim bir durum. Ben bir char const * ifade beklenen bir yerde görünen bir dize hazır bilgi düşünüyordum . C başlatıcılarının bazen farklı kurallara uyabileceğini unutmuşum .
Solomon Slow

@ruakh Dize değişmezi NUL ile sonlandırıldı. Dizi değil.
jamesdlin

2
@ ruakh a char[4]. Bu bir dize değil , ancak birinden başlatıldı
Caleth

2
@Caleth, "birinden başlatılan" çalışma zamanında olması gereken bir şey değildir. Biz bir anahtar kelime eklemek ise staticRuakh örneğinde, daha sonra bir derleyici olabilir bir yayan sigara NULL Değişken program yükleyici tarafından başlatılır, böylece bir başlatılmış veri segmentine "ABCD" sona erdirildi. Yani, Ruakh haklıydı: Bir programda dize hazır bilgisinin görünümünün, derleyicinin NUL sonlu bir dize yaymasını gerektirmediği en az bir durum vardır. (ps, aslında gcc 5.4.0 ile örnek derledim ve derleyici NUL yaymadı.)
Solomon Slow

2

Diğerlerinin de belirttiği gibi, dizelerin geçersiz sonlandırılması C Standart Kütüphanesi'nin bir sözleşmesidir. Standart kitaplığı kullanmayacaksanız, dizeleri istediğiniz şekilde işleyebilirsiniz.

Bu, 'C' derleyicisine sahip herhangi bir işletim sistemi için geçerlidir ve sorunuzda belirtildiği gibi gerçek bir işletim sistemi altında çalışmayan 'C' programları da yazabilirsiniz. Bir örnek olarak, bir kez tasarladığım mürekkep püskürtmeli yazıcı için kontrolör verilebilir. Gömülü sistemlerde, bir işletim sisteminin bellek ek yükü gerekli olmayabilir.

Bellek dar durumlarda, örneğin işlemcinin talimat setine göre derleyicimin özelliklerine bakardım. Dizelerin çok işlendiği bir uygulamada, dize uzunluğu gibi tanımlayıcıların kullanılması istenebilir. CPU'nun kısa ofsetlerle ve / veya adres kayıtlarıyla göreli ofsetlerle çalışırken özellikle etkili olduğu bir durumu düşünüyorum.

Uygulamanızda hangisi daha önemlidir: kod boyutu ve verimliliği mi yoksa bir işletim sistemi veya Kütüphane ile uyumluluk mu? Başka bir husus, sürdürülebilirlik olabilir. Sözleşmeden ne kadar uzaklaşırsanız, başkasının bakımı o kadar zor olacaktır.


1

Diğerleri, C'de dizelerin büyük ölçüde onlardan ne yaptıkları sorununu ele aldı. Ancak, sorunuzda sonlandırıcının kendisi için bazı karışıklıklar var gibi görünüyor ve bir bakış açısından, pozisyonunuzdaki birinin endişe ettiği şey bu olabilir.

C dizeleri boş bırakılır. Yani, boş karakterle sonlandırılırlar NUL. NULLTamamen farklı bir amaca sahip tamamen farklı bir değer türü olan boş gösterici tarafından sonlandırılmazlar .

NUL, tamsayı değerinin sıfır olduğu garanti edilir. Dize içinde, genellikle 1 olacak şekilde altta yatan karakter türünün boyutuna da sahip olacaktır.

NULLbir tamsayı türüne sahip olması garanti edilmez. NULLbir işaretçi bağlamında kullanılmak üzere tasarlanmıştır ve genellikle derleyiciniz iyi ise bir karakter veya tamsayıya dönüştürülmemesi gereken bir işaretçi türüne sahip olması beklenir. Tanımı yaparken NULLglif içerir 0, aslında bu değeri [1] sahip olduğu garanti ve edilmediğini derleyici uygular sürece tek karakter olarak sabit #define(çünkü birçok, yapma NULL gerçekten olmayan bir anlamlı olmamalı işaretçi bağlamında), genişletilmiş kodun aslında sıfır değeri içerdiği garanti edilmez (kafa karıştırıcı bir şekilde sıfır glif içermesine rağmen).

Eğer NULLyazıldığında, o da 1 bir büyüklüğe (veya başka bir karakter boyutu) olması olası olacaktır. Bu, gerçek karakter sabitlerinin çoğunlukla karakter boyutuna sahip olmamasına rağmen, muhtemelen ek sorunlara neden olabilir.

Şimdi çoğu kişi bunu görecek ve "sıfır işaretçisi sıfırdan farklı bitler dışında ne saçmalık" diye düşünecektir - ancak bunun gibi varsayımlar yalnızca x86 gibi ortak platformlarda güvenlidir. Kodunuzu diğer platformları hedeflemeye açık bir şekilde ilgi duyduğunuzdan, kodunuzu işaretçiler ve tamsayılar arasındaki ilişkinin doğası hakkındaki varsayımlardan açıkça ayırdığınız için bu sorunu göz önünde bulundurmanız gerekir.

Bu nedenle, C dizeleri boş olarak sonlandırılırken, tarafından NULLdeğil NUL(genellikle yazılır '\0') tarafından sonlandırılır . Açıkça NULLbir dize sonlandırıcı olarak kullanılan kod , basit bir adres yapısına sahip platformlarda çalışacak ve hatta birçok derleyici ile derlenecektir, ancak kesinlikle doğru C değildir.


Bir okuduğunda [1] gerçek null işaretçi değeri derleyici tarafından sokulur 0 belirteç bir işaretçi türü dönüştürülebilir olan bir bağlamda. Bu, 0 tamsayı değerinden bir dönüşüm değildir 0ve bir değişkenin dinamik değeri gibi belirtecin kendisinden başka bir şey kullanılırsa tutulması garanti edilmez ; dönüşüm de tersine çevrilemez ve bir tamsayıya dönüştürüldüğünde bir boş gösterici 0 değerini vermek zorunda değildir.


Harika bir nokta. Bunu düzeltmek için bir düzenleme gönderdim.
Monty Harder

" NULtamsayı değerinin sıfır olduğu garanti edilmektedir." -> C tanımlamıyor NUL. Bunun yerine C, dizelerin son bir boş karaktere sahip olduğunu , tüm bitlerin 0 olarak ayarlandığı bir bayt olduğunu tanımlar.
chux - Monica

1

C dize kullanıyorum, boş sonlandırma ile karakter dizeleri denir anlamına gelir.

Baremetal veya Windows, Linux, RTOS: (FreeRTO, OSE) gibi herhangi bir işletim sisteminde kullandığınızda herhangi bir sorun yaşamayacak.

Gömülü dünyada boş sonlandırma, karakteri karakter dizisi olarak belirtmek için daha fazla yardımcı olur.

Güvenlik açısından kritik pek çok sistemde C gibi dizeler kullanıyorum.

Merak ediyor olabilirsiniz, aslında C'de dize nedir?

Diziler olan C stili dizeler, "this" gibi dizgi değişmezleri de vardır. Gerçekte, bu dize türlerinin her ikisi de sadece bellekte yan yana oturan karakter koleksiyonlarıdır.

Çift tırnak içine alınmış bir dize yazdığınızda, C otomatik olarak bizim için \ 0 karakteriyle sonlandırılmış bir dize içeren bir dizi karakter oluşturur.

Örneğin, bir karakter dizisi tanımlayıp tanımlayabilir ve bir dize sabitiyle başlatabilirsiniz:

char string[] = "Hello cruel world!";

Basit cevap: Boş sonlandırmalı karakterlerin kullanımı hakkında endişelenmenize gerek yoktur, bu çalışma herhangi bir platformdan bağımsızdır.


Teşekkürler, çift tırnak ile bildirildiğinde, a NULotomatik olarak eklendiğini bilmiyordum .
Snoop

1

Diğerlerinin söylediği gibi, sıfır sonlandırma standart C için hemen hemen evrenseldir. Ancak (diğerlerinin de işaret ettiği gibi)% 100 değil. (Başka) bir örnek için, VMS işletim sistemi genellikle "dize tanımlayıcıları" olarak adlandırdığı şeyi kullandı http://h41379.www4.hpe.com/commercial/c/docs/5492p012.html C'de #include <descrip.h >

Uygulama düzeyinde şeyler boş sonlandırma kullanabilir veya kullanamaz, ancak geliştirici uygun görür. Ancak düşük seviyeli VMS şeyler kesinlikle boş sonlandırma kullanmayan tanımlayıcılar gerektirir (ayrıntılar için yukarıdaki bağlantıya bakın). Bu, büyük ölçüde VMS içlerini kullanan tüm dillerin (C, montaj vb.) Onlarla ortak bir arayüze sahip olabilmeleri içindir.

Dolayısıyla, benzer bir durum bekliyorsanız, "evrensel sıfır sonlandırması" nın gerekebileceğinden biraz daha dikkatli olmak isteyebilirsiniz. Yaptığın şeyi yapsaydım daha dikkatli olurdum, ancak uygulama düzeyindeki işlerim için boş sonlandırma yapmak güvenlidir. Size aynı güvenlik düzeyini önermem. Kodunuzun gelecekteki bir noktada derleme ve / veya başka bir dil kodu ile arabirim oluşturması gerekebilir, bu da her zaman boş sonlandırılmış dizelerin C standardına uymayabilir.


Bugün, 0 fesih aslında oldukça sıra dışı. C ++ std :: string Java dize yapar yapmaz değil, Objective-C NSString yapar değil, Swift Dize bulunmaz - sonuç olarak NUL kodları ile her diller kütüphane destekler dizeleri içindeki C imkansızdır dize ( belirgin nedenlerle dizeler).
gnasher729

@ gnasher729 "... hemen hemen evrensel" i "standart C için hemen hemen evrensel" olarak değiştirdim, umarım herhangi bir belirsizliği ortadan kaldırır ve bugün doğru kalır (ve OP'nin konusuna ve sorusuna göre ne demek istediğimi).
John Forkosh

0

Yerleşik, güvenlik açısından kritik ve gerçek zamanlı sistemler konusundaki tecrübelerime göre, hem C hem de PASCAL dize kurallarını kullanmak, yani dizelerin uzunluğunu ilk karakter olarak sağlamak (uzunluğu 255 ile sınırlayan) ve NULkullanılabilir boyutu 254'e düşüren en az bir 0x00, ( ) içeren dize .

Bunun bir nedeni, ilk bayt alındıktan sonra ne kadar veri beklediğinizi bilmek ve bir diğeri, bu tür sistemlerde, mümkün olduğunda dinamik tampon boyutlarından kaçınılmasıdır - sabit bir 256 tampon boyutunun tahsis edilmesinin daha hızlı ve daha güvenli olması, (hayır mallocbaşarısız olup olmadığını kontrol etmeniz gerekir ). Bir diğeri, iletişim kurduğunuz diğer sistemlerin ANSI-C'ye yazılmamış olabilir.

Herhangi bir gömülü işte, dize formatları, endianness, tamsayı boyutları, vb. Dahil tüm iletişim yapılarınızı mümkün olan en kısa sürede ( ideal olarak başlamadan önce ) tanımlayan bir Arayüz Kontrol Belgesi (IDC) oluşturmak ve sürdürmek önemlidir , ve bu olmalı ve tüm ekipleri, kutsal kitabı ne zaman sistem yazma - birisi dilek yeni bir yapı tanıtmak veya biçimlendirmek için eğer gerekir orada belgelendirilmesi ilk , bilgili etkiledi olabileceğini ve herkes muhtemelen veto değişikliği için bir seçenek .

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.