2008-11-17 14 views
6

Üzerinde çalıştığım uygulamada, günlük kaydı, dosyaya yazılan metni biçimlendirmek için sprintf kullanır. Yani, böyle bir şey: gönderilen alır ileti el ayrılan tampon için çok büyük olduğundaElle ayrılmış arabelleği olmayan sprintf'i kullanma

char buffer[512]; 
sprintf(buffer, ...); 

Bu bazen sorunlara neden olmaktadır.

Bu şekilde el ile bellek ayırmak zorunda kalmadan sprintf davranışı almanın bir yolu var mı?

DÜZENLEME: sprintf C operasyonu yaparken, ben (! Vardır varsa) bana bu tür davranışlara ...

cevap

10

Hayır için sprintf() kullanamazsınız gibi sprintf daha güvenli sürümlerinden birini kullanmanız gerekir üzerine gelen tampon önlemek için

yeterli bellek ayırın. Alternatifler arasında: mesaj kesecek şekilde

  • kullanım snprintf() - tam sorunu çözmek, ama taşması konuyu
  • çift (ya da üçlü ya ...) tampon engellemez - Bir olmadıkça çevreyi
  • kullanımı C kısıtlı ++ std::string ve ostringstream - ama printf biçimini kaybedersiniz, bir, printf benzeri% operatörü ile birlikte gelir < < operatöre
  • kullanım Boost Format kullanmak gerekecektir
+4

snprintf(), ne kadar alana ihtiyacınız olduğunu söyler, bu nedenle ihtiyacınız olduğunda iki kez kullanabilirsiniz, ilk kez gösterildiği gibi bir statik tampon ve ikinci kez dinamik olarak ayrılmış bir arabelleğe sahip olabilirsiniz. İkinci öneri için –

+3

-1 (arabellek boyutunu ikiye katlama. Maksimum dize uzunluğunu bilmediğinizden, bir milyar ile çarpsanız bile taşmayacağını garanti edemezsiniz.) – finnw

3

almak için "günlük tesis kullanan tip çözümleri C++ arıyorum "

fprintf(), dosya boyutuna hiçbir boyut sınırlaması getirmez. Metni doğrudan dosyaya yazabiliyorsanız, bunu yapın! Bununla birlikte, bir miktar ara işlem aşaması olduğunu varsayalım. Ne kadar alan gerektiğini biliyorsanız, o kadar yer ayırmak için malloc()'u kullanabilirsiniz.

Bu gibi zamanlarda bir teknik, makul boyutlu bir arabellek ayırmaktır (bu, zamanın% 99'u kadar büyük olacaktır) ve eğer yeterince büyük değilse, verileri tek tek işlediğiniz parçalara ayırın.

+1

dosya erişimi bellek erişiminden çok daha yavaştır.Bu bir ara sprintf'in kötü bir fikir olmaması için bir sebep. – xtofl

+0

Bu avantajın çoğunu, varsa dosyayı arabelleğe alarak alabilirsiniz. Hala havai fişek var ama en azından diske vurmuyorsun. –

1

Sprintf'in vanilya sürümünde, verilerin, arabelleğe geçirilen arabelleğin üzerine yazılmasını engellemenin bir yolu yoktur. Bu, belleğin el ile ayrılmış veya yığında ayrıştırılmış olmasına bakılmaksızın geçerlidir. Eğer sprintf_s (pencerelerin sadece)

http://msdn.microsoft.com/en-us/library/ybk95axf.aspx

+0

Vanilya sprintf() ile tampon taşmalarına karşı koruma sağlamak mümkündür, örn. sprintf (buf, "%. * s", buf_size - 1, input_string); Ancak bu sadece bilinen, sabit formatlı dizeler için çalışır. – finnw

18

Önbelleği ayırmanıza gerek kalmaması için arabelleği ayıran asprintf (3) (not: standart dışı) kullanabilirsiniz.

+0

Giden - asprintf() kullanışlıdır Sisteminizde varsa. –

+0

Lütfen farklı sistemlerde bu fonksiyonun kullanılabilirliğinden söz ediniz, çünkü standart C lib veya POSIX değildir. – qrdl

+0

Bu Linux çoğunlukla - muhtemelen BSD de. Bir de vazprintf() var. –

5

Ayrıca, ayırmayı önleyen bir sürümü de bilmiyorum, ancak C99 sprintfs dizesi NULL işaretçisine izin veriyorsa. Çok verimli değil, ancak bu, taşma riski olmadan tam bir dize (yeterli bellek olduğu sürece) verir:

length = snprintf(NULL, ...); 
str = malloc(length+1); 
snprintf(str, ...); 
+1

C99 kullanıyorsanız, sadece değişken uzunluklu bir dizi kullanabilirsiniz, bu yüzden str mallocing yerine "char str [length + 1];" snprintf çağrısı sonrası. Yığın üzerinde olduğu gibi, daha sonra onu ayırmaya gerek yok. –

+2

MSVC çalışma zamanını kullanıyorsanız, snprintf'nin gerekli uzunluk yerine negatif bir değer döndürdüğünü görürsünüz. , bu nedenle, MSVC altında gereken uzunluğu istiyorsanız, farklı bir işlev kullanmanız gerekir, _vscprintf [http://msdn.microsoft.com/en-us/library/w05tbk72.aspx] – Hasturkun