C'deki
size_t ile karıştırılmaya başladım, bunun
sizeof operatörü tarafından döndürüldüğünü biliyorum. Ama bu tam olarak nedir? Veri türü mü?
Diyelim ki
for döngü var:
for(i = 0; i < some_size; i++)
int i; veya
size_t i; kullanmalı mıyım?
1999 ISO C standardına göre. (C99),
size_tişaretsiz bir tamsayıdır. en az 16 bit yazın (bkz. bölüm 7.17 ve 7.18.3).
size_t, imzasız bir veri türüdür. birkaç C/C++ standardı ile tanımlanmıştır, Örneğin. C99 ISO/IEC 9899 standardı, bu
stddef.h. 1 içinde tanımlanabilir. .__ dahil edilerek daha fazla ithal edilebilir. Bu dosya olarak
stdlib.hdahili olarak alt
stddef.hiçerir.
Bu tip .__ temsil etmek için kullanılır. nesnenin boyutu. Kütüphane fonksiyonları almak veya iade boyutları onları bekliyorlar. türünün olması ya da geri dönüş türünün olması.
size_t. Dahası, çoğu. sık kullanılan derleyici tabanlı. işleç sizeof a. ile uyumlu sabit değer.
size_t.
Sonuç olarak,
size_t, herhangi bir dizi dizinini tutmayı garanti eden bir türdür.
size_t imzasız bir türdür. Bu nedenle, herhangi bir olumsuz değeri temsil edemez (<0). Bir şeyi sayırken kullanırsın ve negatif olamayacağından eminsin. Örneğin,
strlen() , bir
size_t değerini döndürür, çünkü dizgenin uzunluğu en az 0 olmalıdır.
Örneğinizde, döngü endeksiniz her zaman 0'dan büyük olacaksa,
size_t veya başka bir imzasız veri türünü kullanmak anlamlı olabilir.
Bir
size_t nesnesi kullandığınızda, kullanıldığı tüm bağlamlarda, aritmetik de dahil olmak üzere negatif olmayan değerler istediğinizden emin olmalısınız. Örneğin, diyelim ki:
size_t s1 = strlen(str1); size_t s2 = strlen(str2);
ve
str2 ve
str1 uzunluklarının farkını bulmak istiyorsunuz. Yapamazsın:
int diff = s2 - s1; /* bad */
Bunun nedeni,
diff öğesine atanan değerin
s2 < s1 olsa bile her zaman pozitif bir sayı olacağından, hesaplama imzasız türlerle yapıldığındandır. Bu durumda, kullanım durumunuzun ne olduğuna bağlı olarak,
long long ve
s1 için
int (veya
s2) kullanarak daha iyi olabilirsiniz.
C/POSIX'de
size_t kullanabilecek/kullanması gereken bazı işlevler vardır, ancak geçmiş nedenlerden ötürü kullanmazlar. Örneğin,
fgets öğesinin ikinci parametresi ideal olarak
size_t olmalıdır, ancak
int.
size_t, herhangi bir dizi dizinini tutabilen bir türdür.
Uygulamaya bağlı olarak, aşağıdakilerden herhangi biri olabilir:
unsigned char
unsigned short
unsigned int
unsigned long
unsigned long long
Makinemin
size_t dosyasında
stddef.h nasıl tanımlanır:
typedef unsigned long size_t;
Eğer ampirik bir türsünüz,
echo | gcc -E -xc -include 'stddef.h' - | grep size_t
Ubuntu 14.04 64-bit GCC 4.8 için çıktı:
typedef long unsigned int size_t;
stddef.h'nun GCC tarafından sağlandığını ve GCC 4.2'de
src/gcc/ginclude/stddef.h altında glibc'nin sağlanmadığını unutmayın.
İlginç C99 görünümleri
malloc,
size_t işlevini bir argüman olarak alır, bu nedenle atanabilecek maksimum boyutu belirler.
Ayrıca
sizeof tarafından döndürüldüğünden, herhangi bir dizinin maksimum boyutunu sınırladığını düşünüyorum.
Ayrıca bakınız: C'deki bir dizinin maksimum boyutu nedir?
types.h adlı kullanıcı sayfası:
size_t işaretsiz bir tamsayı türü olmalıdır
Kimse henüz bahsetmediğinden,
size_t dilinin asıl önemi
sizeof işlecinin bu tür bir değer vermesidir. Aynı şekilde,
ptrdiff_t 'nin birincil önemi, bir göstericiyi diğerinden çıkarmanın, bu tür bir değer vermesidir. Bunu kabul eden kütüphane işlevleri, bu tür işlevlerin, arayanların daha büyük tipteki sistemlerde "imzasız int" den daha büyük bir değerden geçen bir kodu boşa harcamasına zorlamadan, bu tür nesnelerin olabileceği sistemlerde boyutu UINT_MAX'ı aşan nesnelerle çalışmasına izin vereceği için yapar olası tüm nesneler için yeterli olacaktır.
size_t ve
int değiştirilemez. Örneğin 64-bit Linux'ta
size_t 64-bit boyutunda (yani
sizeof(void*)), ancak
int 32-bit'dir.
Ayrıca,
size_t dosyasının imzasız olduğuna dikkat edin. İmzalı bir sürüme ihtiyacınız varsa, o zaman bazı platformlarda
ssize_t vardır ve bu sizin örneğinizle daha alakalı olacaktır.
Genel bir kural olarak, çoğu genel durum için
int kullanılmasını öneririm ve yalnızca belirli bir ihtiyaç olduğunda (örneğin
mmap() ile)
size_t/
ssize_t kullanın.
Neden
size_t'un olması gerektiğine ve buraya nasıl geldiğimize karar vermek için:
Pratik açıdan,
size_t ve
ptrdiff_t adreslerinin 64 bit uygulamada 64 bit, 32 bit uygulamada 32 bit vb. Olduğu garantilidir. Var olan herhangi bir türü, her derleyicide eski kodları bozmadan kastetmeye zorlayamazlardı.
Bir
size_t veya
ptrdiff_t, mutlaka bir
intptr_t veya
uintptr_t ile aynı değildir. 80'lerin sonunda
size_t ve
ptrdiff_t Standarda eklendiğinde ve C99 birçok yeni tür eklediğinde (henüz 16 bit Windows gibi) kullanılmadığında eski hale geldiklerinde hala kullanılan bazı mimarilerde farklıydı. 16 bitlik korumalı moddaki x86, mümkün olan en büyük dizinin veya yapının yalnızca 65,536 bayt boyutunda olabileceği, ancak bir
far göstergesinin kayıtlardan daha geniş 32 bit olması gereken bölümlenmiş bir belleğe sahiptir. Bunlarda,
intptr_t 32 bit genişliğinde olabilirdi, ancak
size_t ve
ptrdiff_t 16 bit genişliğinde olabilir ve bir sicile sığabilir. Gelecekte ne tür bir işletim sisteminin yazılabileceğini kim bilebilirdi? Teoride, i386 mimarisi, hiçbir işletim sisteminin gerçekte kullanmadığı 48 bit işaretçilerle 32 bit segmentasyon modeli sunar.
Bir bellek kayması türü
long olamaz, çünkü çok fazla eski kod
long öğesinin tam olarak 32 bit genişliğinde olduğunu varsayar. Bu varsayım, UNIX ve Windows API'lerinde bile yapıldı. Ne yazık ki, pek çok başka eski kod da
long öğesinin bir işaretçiyi, dosya ofsetini, 1970'ten beri geçen saniye sayısını vb. Tutacak kadar geniş olduğunu varsaymıştır. POSIX şimdi bu ikinci varsayımı eskinin yerine doğru olmaya zorlamak için standart bir yol sağlar, ancak hiçbiri de taşınabilir bir varsayım değildir.
int olamazdı çünkü 90'larda sadece küçük bir avuç derleyici
int 64 bit genişliğinde yaptı. Sonra
long 32 bit genişliğinde tutarak çok tuhaflaştı. Standardın bir sonraki revizyonu
int'in
long'den daha geniş olduğunu yasadışı ilan etti, ancak
int çoğu 64-bit sistemde hala 32 bit.
32-bit sistemlerde bile en az 64 bit olacak şekilde oluşturulduğundan, daha sonra eklenmiş olan
long long int olamaz.
Bu yüzden yeni bir tip gerekliydi. Olmasa bile, diğer tüm türler bir dizi veya nesnedeki uzaklıktan başka bir şey ifade ediyordu. 32-64 bitlik göç fiyaskolarından bir ders çıkarsa, bir türün hangi özelliklere sahip olması gerektiği ve farklı programlarda farklı şeyler ifade eden bir özelliği kullanmama konusunda spesifik olması gerekirdi.
Genel olarak, 0'dan başlayıp yukarı doğru ilerliyorsanız, sizi negatif değer durumlarına sokan taşmayı önlemek için her zaman işaretsiz bir tür kullanın. Bu kritik öneme sahiptir, çünkü dizi sınırlarınız döngü sınırınızın maksimum değerinden daha azsa, ancak döngü maksimum değeriniz türünüzün maksimum değerinden daha büyük olursa, negatif sararsınız ve segmentasyon hatasıyla karşılaşabilirsiniz (SIGSEGV). Bu nedenle, genel olarak, 0'dan başlayan ve yukarı giden bir döngü için hiçbir zaman int kullanmayın. Bir imzasız kullanın.
size_t işaretsiz tamsayı veri türüdür. GNU C Kütüphanesini kullanan sistemlerde, bu işaretsiz int veya imzasız uzun int olacaktır. size_t genellikle dizi indeksleme ve döngü sayımı için kullanılır.
size_t veya herhangi bir imzasız tür, döngü değişkenleri tipik olarak 0'dan büyük veya ona eşit olduğundan döngü değişkeni olarak görülebilir.
Bir size_t object kullandığımız zaman, kullanıldığı tüm bağlamlarda, aritmetik de dahil olmak üzere sadece negatif olmayan değerler istediğimizden emin olmalıyız. Örneğin, aşağıdaki program kesinlikle beklenmeyen bir sonuç verecektir:
// C program to demonstrate that size_t or // any unsigned int type should be used // carefully when used in a loop #include<stdio.h> int main() { const size_t N = 10; int a[N]; // This is fine for (size_t n = 0; n < N; ++n) a[n] = n; // But reverse cycles are tricky for unsigned // types as can lead to infinite loop for (size_t n = N-1; n >= 0; --n) printf("%d ", a[n]); } Output Infinite loop and then segmentation fault