2011-11-10 12 views
6

böyle bakıyor bellekte dizeleri temsil etmek bir yapıya sahiptir:C'nin printf bir dize kesinliğini belirtmek için size_t kullanma

typedef struct { 
    size_t l; 
    char *s; 
} str_t; 
Ben size_t kullanarak inanıyorum

bir karakter dizesinin uzunluğu belirtmek için mantıklı . Ayrıca bu dize printf("%.*s\n", str.l, str.s) kullanarak yazdırmak istiyorum. Ancak, * kesinlik, size_t değil, int bağımsız değişkeni bekler. Bununla ilgili hiçbir şey bulamadım. Bu yapıyı numaralı aramaya int no'lu döküm olmadan doğru şekilde kullanmak için bir yer var mı?

+0

'char * 'öğesini sonlandırmayı planlamıyor musunuz? Eğer null feshedilirse, uzunluğun doğruluğunu ayarlamanın sizi ilk etapta ne getireceğini anlamıyorum. Bunun ötesinde, seçimlerinizin bence olduğunu düşünüyorum: "size_t" ifadenizi bir "int" 'e dönüştürün veya değiştirmeyin ve hiçbir zaman negatif olmasına izin vermeyin. –

+2

@EvanTeran Dizeler aslında daha büyük bir arabellek içindeki parçalara işaret eder, bu yüzden "null" karakteri bir yerlerde daha ileridedir. Dizge genel olarak en fazla birkaç yüz bayt olduğu için, bir "int" nin boyutu bir sorun değildir. Beni rahatsız eden şey, 'size_t' bu amaca çok iyi uysa bile, C99 'size_t' argümanını kabul eden özel bir hassasiyet değiştirici tanımlamaz. Fwrite öneri için –

cevap

3

Bir makro

#define STR2(STR) (int const){ (STR).l }, (char const*const){ (STR).s } 

yapacak ve ardından printf("%.*s\n", STR2(str)) olarak kullanabiliriz. Bu, STR'u iki kez değerlendirdiğinden emin olun, bu nedenle yan etkilerle dikkatli olun, ancak muhtemelen bunu zaten biliyordunuz.

Düzenleme: bu örtük dönüşümler olduklarını

kullanıyorum bileşik ilklendiriciler böyle. Eğer işler ters giderse, derleyicinin sizi açık bir oyuncudan daha fazla uyandırması olasılığı daha yüksektir. STR göstericisidir bir alan .l olduğunu ve bunlardan yalnızca int bir döküm koyardım eğer

Örn

, tüm derleyiciler mutlu int bu işaretçi dönüştürmek olacaktır. .s alanı için benzer, bu gerçekten bir char* veya uyumlu bir şey karşılık gelmelidir, aksi takdirde bir uyarı veya hata görürsünüz.

+0

, ama her adımda döküm daha iyi görünüyor; (boyutu veya int' 'büyüklüğü aynı olabilir de olmayabilir de. Bu imzasız olması gerekir). Merak ediyorum, bu bileşik başlatıcısı tam olarak nasıl bir kadrodan farklı? GCC'nin dokümanları şunları söylemektedir: "Skaler tipler ve birleştirme türleri için bileşik hazırlıklar da kullanılabilir, ancak daha sonra bileşik literal bir oyuncuya eşdeğerdir." (Http://gcc.gnu.org/onlinedocs/gcc/Compound-Literals.html) –

+0

@LuciStanescu herhangi bir döküm ben bahsedilen gibi bir kapalı dönüştürme. Tüm yayınlar dönüşümlerdir, ancak tüm dönüşümler yayınlanmaz. Fark, bir ". STR" olan bir alan ".l" iletirseniz, bir tamsayı türü değil, bir işaretçi olur. Eğer bir oyuncu olsaydı, tüm derleyiciler mutlu bir şekilde dönüşürdü. Bir dönüşüm için çoğu derleyici sizi uyarır. BTW aynısı, .s' alanı için geçerli, gönderimi düzenleyeceğim. –

+0

Gerçekten de, GCC'nin el kitabında, skaler tipler için, bileşik başlatıcının dökümle eşdeğer olduğu belirtilmekle birlikte, interger olmayan bir türün geçtiğinden şikayetçi olmaktadır. –

5
printf("%.*s\n", (int)str.l, str.s) 
//    ^^^^^ use a type cast 

Düzenleme

Tamam, soruyu düzgün okumadım. Bir oyuncu kadrosu kullanmak istemezsiniz, ama bence, bu durumda: zor.

Ya öyle ya da sadece Orada hiçbir garantisi size_t bir int olduğunu, ya da bir int içindeki temsil edilebileceğini

fwrite(str.s, str.l, 1, stdout); 
printf("\n"); 
+1

+1. Her ne kadar muhtemelen “fwrite (str.s, 1, str.l, stdout)” ile gidip, kaç karakterin gerçekten yazıldığını görmek için geri dönüş değerini kontrol etmeme rağmen. – Nemo

+0

Verdiğim printf() 'çağrısı, gerçekten sadece bir örnekti ve soru, amacın bir hassasiyet belirtmek için size_t işlevini kullanmak olduğunu açıkça belirtti. Eğer merak ediyorsanız neden 'fwrite' öneriyi beğenmedi: daha karmaşık sabit dizeleri, çoklu str_t dizeleri, sayılar olan başlamak ve bir mesaj oluşturmak için' snprintf' kullanmak kez, bu biçimlendirmeyi kullanmamaya çok can sıkıcı olur fonksiyonlar. Bu yüzden cevabınızdan :-) "zorlu" öneriyle sıkıştığımı düşünüyorum. Ama şerefe! –

+0

@nemo: Bunu da düşündüm ama sonra benim sürümü ile oysa kısa dönüş sayısı> 0 ile uğraşmak zorunda, dönüş değeri 1 (başarılı) veya 0 (hata) ya olduğunu. PC'mdeki (Mac OS X) 'fwrite()' deki belgelere göre, sadece yazma hatası üzerinde kısa bir sayıma sahip olursunuz, bu yüzden ne olursa olsun durmak isteyebilirsiniz. – JeremyP

1

fwrite kullanın. Bir int'nin tam boyutunu tanımlamaktaki C'nin mirasının bir parçası, büyük boyutlu alanların (bunların içinde MAX_INT değerinden daha fazlasına sahip olanlar) ele alınması için boyutunun uygulanmasının gerekebileceği endişeleriyle birleşir.

Size_t ile ilgili en yaygın hata, imzasız int'ye eşdeğer olduğunu varsaymaktır. Bu tür eski hatalar yaygındı ve kişisel deneyimlerden, bu varsayımı geri almanız gerektiğinden, 32-bit'ten 64-bitlik bir mimariye doğru bir acı çekiyor.

En iyi ihtimalle, bir döküm kullanabilirsiniz. Oyuncu kadrosundan gerçekten kurtulmak istiyorsanız, alternatif olarak size size_t ifadesini kullanabilirsiniz.

+1

Aslında, size 'size_t' asla int 'ile eşdeğer olmayacağının bir garantisi var; Yine de, imzasız int'ye eşdeğer olabilir. Hala bir döküm –

İlgili konular