Dizeleri dönen fonksiyonlar, iyi bir stil?


11

C programlarımda genellikle ADT'lerimin dize olarak temsilini yapmak için bir yola ihtiyacım var. Herhangi bir şekilde ekrana dizesini yazdırmak zorunda olmasam bile, hata ayıklama için böyle bir yöntem olması temiz. Yani bu tür bir işlev genellikle ortaya çıkar.

char * mytype_to_string( const mytype_t *t );

Aslında ben dize dönmek için bellek işlemek için burada (en az) üç seçenek olduğunu fark.

Alternatif 1: Dönüş dizesini işlevdeki statik karakter dizisinde saklamak. Dize her çağrıda üzerine yazılması dışında çok düşünmeye gerek yok. Bu bazı durumlarda bir sorun olabilir.

Alternatif 2: Öbek üzerindeki dizeyi işlevin içindeki malloc ile ayırın. Gerçekten temiz beri ben bir tampon veya üzerine yazma boyutunu düşünmek gerekmez. Ancak, bittiğinde dize free () hatırlamak zorunda, ve sonra ben de serbest bir şekilde geçici bir değişken atamak gerekir. ve sonra yığın tahsisi yığın tahsisinden çok daha yavaştır, bu nedenle bu döngüde tekrarlanırsa bir darboğaz olacaktır.

Alternatif 3: İşaretçiyi bir tampona iletin ve arayanın bu arabelleği ayırmasına izin verin. Sevmek:

char * mytype_to_string( const mytype_t *mt, char *buf, size_t buflen ); 

Bu, arayan kişiye daha fazla çaba getirir. Ayrıca bu alternatifin bana argümanların sırası konusunda başka bir seçenek sunduğunu fark ettim. İlk ve son olarak hangi tartışmam gerekir? (aslında altı olasılık)

Peki hangisini tercih etmeliyim? Herhangi bir nedenle? C geliştiricileri arasında bir tür yazılı olmayan standart var mı?


3
Sadece gözlemsel bir not, çoğu işletim sistemi seçenek 3'ü kullanır - arayan arabellek yine de ayırır; tampon işaretçisini ve kapasitesini söyler; callee arabelleği doldurur ve ayrıca tampon yetersizse gerçek dizgi uzunluğunu döndürür . Örnek: sysctlbynameOS X ve iOS'ta
rwong

Yanıtlar:


11

En çok gördüğüm yöntemler 2 ve 3'tür.

Kullanıcı tarafından sağlanan tamponun kullanımı oldukça basittir:

char[128] buffer;
mytype_to_string(mt, buffer, 128);

Yine de çoğu uygulama kullanılan tampon miktarını döndürecektir.

Seçenek 2 daha yavaş olacaktır ve farklı çalışma süreleri (ve farklı yığınlar) kullanabilecekleri dinamik olarak bağlı kütüphaneler kullanıldığında tehlikelidir. Böylece başka bir kütüphanede kötü amaçlı olanları özgür bırakamazsınız. Bu free_string(char*), bununla başa çıkmak için bir işlev gerektirir .


Teşekkürler! Alternatif 3'ü de en çok sevdiğimi düşünüyorum. Ancak ben gibi şeyler yapmak istiyorum: printf("MyType: %s\n", mytype_to_string( mt, buf, sizeof(buf));ve bu nedenle kullanılan uzunluğu değil, daha ziyade dize işaretçiye dönmek istiyorum. Dinamik kütüphane yorumu gerçekten önemlidir.
Øystein Schønning-Johansen

Bu sizeof(buffer) - 1, \0sonlandırıcıyı tatmin etmek değil mi?
Michael-O

1
@ Michael-O hayır null terimi arabellek boyutuna dahil edilmez, yani yerleştirilebilecek maksimum dizginin geçirilen boyuttan 1 küçük olması anlamına gelir. Bu, güvenli dizenin standart kütüphanede snprintfkullanım gibi çalıştığı modeldir .
cırcır ucube

@ratchetfreak Açıklama için teşekkürler. Cevabı bu bilgelikle genişletmek güzel olurdu.
Michael-O

0

# 3 için ek tasarım fikri

Mümkün olduğunda mytype, aynı .h dosyasında gereken maksimum boyutu da sağlayın mytype_to_string().

#define MYTYPE_TO_STRING_SIZE 256

Şimdi kullanıcı buna göre kodlama yapabilir.

char buf[MYTYPE_TO_STRING_SIZE];
puts(mytype_to_string(mt, buf, sizeof buf));

Sipariş

Dizilerin boyutu, ilk olarak VLA türlerine izin verir.

char * mytype_to_string( const mytype_t *mt, size_t bufsize, char *buf[bufsize]); 

Tek boyutla o kadar önemli değil, ama 2 veya daha fazla ile kullanışlı.

void matrix(size_t row, size_t col, double matrix[row][col]);

Ben ilk büyüklüğüne sahip okuma hatırlamak sonraki C tercih edilen bir deyim olduğunu. Bu referans bulmak gerekir ....


0

@ Ratchetfreak'in mükemmel cevabına ek olarak, # 3 numaralı alternatifin standart C kitaplığı işlevleriyle benzer bir paradigmayı / deseni izlediğine dikkat çekerim.

Örneğin strncpy,.

char * strncpy ( char * destination, const char * source, size_t num );

Aynı paradigmayı izlemek, işlevinizi kullanmaları gerektiğinde yeni geliştiricilerin (hatta gelecekteki kendinizin) bilişsel yükünü azaltmaya yardımcı olacaktır.

Yayınınızdaki tek fark destination, C kitaplıklarındaki bağımsız değişkenin ilk olarak bağımsız değişken listesinde listelenme eğiliminde olmasıdır. Yani:

char * mytype_to_string( char *buf, const mytype_t *mt, size_t buflen ); 

-2

Yapmayı teklif ettiğin şeyin kötü bir kod kokusu olmasının yanı sıra, alternatif 3 bana en iyi geliyor. Ayrıca @ gnasher729 gibi yanlış bir dil kullandığınızı düşünüyorum.


Kod kokusunu tam olarak ne düşünüyorsunuz? Lütfen detaylandırın.
Hulk

-3

Dürüst olmak gerekirse, bir dizeyi döndürmenin karmaşık, yoğun çalışma gerektiren ve hataya açık bir işlem olmadığı farklı bir dile geçmek isteyebilirsiniz.

Kodunuzun% 99'unu değiştirmeden bırakabileceğiniz C ++ veya Objective-C düşünebilirsiniz.

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.