C de size_t nedir?


626

size_tC ile karışıyorum sizeof. Operatör tarafından iade edildiğini biliyorum . Ama bu tam olarak nedir? Bir veri türü mü?

Diyelim ki bir fordöngüm var:

for(i = 0; i < some_size; i++)

Kullanmalı mıyım int i;yoksa size_t i;?


11
O senin tek seçenek ise, kullanım intdurumunda some_size, imzalanır size_to imzasız ise.
Nate

8
@Doğru Yanlış. POSIX bir ssize_t tipine sahiptir, ancak gerçekte kullanılacak doğru tip ptrdiff_t'dir.
Steven Stewart-Gallus

2
Yanıtlar Düşük Seviye Programlamadaki kadar net değildir : Intel® 64'te C, Montaj ve Program Yürütme . Kitapta belirtildiği gibi, bir dizin kullanmak int ibüyük bir diziyi ele almak için yeterli olmayabilir. Böylece kullanarak size_t idaha fazla endeksleri ele alabilirsiniz, bu yüzden büyük bir dizi olsa bile bu bir sorun olmamalıdır. size_tbir veri türüdür: genellikle a unsigned long intama bu sisteminize bağlıdır.
bruno

Yanıtlar:


461

Wikipedia'dan :

1999 ISO C standardına (C99) göre, size_ten az 16 bitlik işaretsiz bir tamsayı türüdür (bkz. Bölüm 7.17 ve 7.18.3).

size_t"C99 ISO / IEC 9899 standardı" olarak tanımlanan birçok C / C ++ standardı tarafından tanımlanan imzasız bir veri türüdür stddef.h. 1stdlib.h Bu dosya dahili olarak alt içerildikçe daha fazla içe aktarılabilir stddef.h.

Bu tür, bir nesnenin boyutunu temsil etmek için kullanılır. Boyut alan veya döndüren kütüphane işlevleri, bunların türünde veya dönüş türünde olmasını bekler size_t. Ayrıca, en sık kullanılan derleyici tabanlı operatör sizeof ile uyumlu sabit bir değer değerlendirmelidir size_t.

Sonuç olarak, size_therhangi bir dizi dizinini tutması garanti edilen bir türdür.


4
"Boyut alan veya döndüren kütüphane işlevleri, bunların ...
boyut_t türünde olmasını bekler

64
@Draemon Bu yorum temel bir karışıklığı yansıtıyor. size_tbellekteki nesneler içindir. C standardı, diskler veya dosya sistemleriyle tanımlamaz stat()veya off_t(bunlar POSIX tanımlarıdır) veya herhangi bir şey yapmaz - FILEakışlarda kendini durdurur . Sanal bellek yönetimi, boyut gereksinimleri kadar dosya sistemlerinden ve dosya yönetiminden tamamen farklıdır, bu yüzden off_tburada bahsetmek önemli değildir.
jw013

3
@ jw013: Buna temel bir karışıklık diyorum, ama ilginç bir noktaya değindin. Yine de, alıntılanan metin "bellek içi nesnelerin boyutları" demez ve "ofset", nerede saklandığına bakılmaksızın boyut türü için pek iyi bir isim değildir.
Draemon

30
@Draemon İyi bir nokta. Bu cevap, bence bu durumda en iyi açıklamaya sahip olmayan Wikipedia'dan alıntı yapıyor. C standardının kendisi çok daha açıktır: operatörün size_tsonucunun türü olarak tanımlanır sizeof(yaklaşık 7.17p2 <stddef.h>). Bölüm 6.5, C ifadelerinin tam olarak nasıl çalıştığını açıklar (bunun için 6.5.3.4 sizeof). sizeofBir disk dosyasına başvuramayacağınız için (çoğunlukla C disklerin ve dosyaların nasıl çalıştığını tanımlamasa bile), karışıklığa yer yoktur. Başka bir deyişle, Wikipedia'yı suçlayın (ve bu cevabı Wikipedia'yı alıntılamak için değil, gerçek C standardını değil).
jw013

2
@Draemon - "Temel karışıklık" değerlendirmesine de katılıyorum. C / C ++ standartlarını okumadıysanız, "nesne" nin "nesne yönelimli programlamaya" atıfta bulunduğunu düşünebilirsiniz. Bu OOP nesnelerinden hiçbirine sahip olmayan, ancak henüz nesneleri olmayan C standardını okuyun ve öğrenin. Cevap sizi şaşırtabilir!
Heath Hunnicutt

220

size_timzasız bir türdür. Bu nedenle, herhangi bir negatif değeri (<0) temsil edemez. Bir şey sayarken kullanırsınız ve negatif olmadığından emin olursunuz. Örneğin, bir dizenin uzunluğunun en az 0 olması gerektiğinden a değerini strlen()döndürür size_t.

Örneğinizde, döngü dizininiz her zaman 0'dan büyük olacaksa, kullanımı size_tveya başka herhangi bir işaretsiz veri türü mantıklı olabilir .

Bir size_tnesneyi kullandığınızda, aritmetik de dahil olmak üzere tüm bağlamlarda kullanıldığından emin olmalısınız, negatif olmayan değerler istediğinizden emin olun. Örneğin, diyelim ki:

size_t s1 = strlen(str1);
size_t s2 = strlen(str2);

ve uzunluklarının farkını bulmak istiyor str2ve str1. Yapamazsın:

int diff = s2 - s1; /* bad */

Bunun nedeni, atanan değerin , hesaplama imzasız türlerle yapıldığından diff, her zaman pozitif bir sayı s2 < s1olacağıdır. Bu durumda, kullanım durumunuzun ne olduğuna bağlı olarak, ve için int(veya long long) kullanmaktan daha iyi olabilirsiniz .s1s2

C / POSIX'te kullanabileceği / kullanması gereken size_t, ancak tarihsel nedenlerden dolayı kullanmayan bazı fonksiyonlar vardır . Örneğin, ikinci parametre fgetsideal olarak olmalıdır size_t, ancak öyle olmalıdır int.


8
@Alok: İki soru: 1) büyüklüğü size_tnedir? 2) neden tercih etmeliyiz size_tgibi bir şey üzerinde unsigned int?
Lazer

2
@Lazer: boyutu size_tIS sizeof(size_t). En SIZE_MAXaz 65535 olacak C standardı garantileri . Operatör size_ttarafından döndürülen türdür sizeofve standart kütüphanede kullanılır (örneğin, strleniade size_t). Brendan'ın dediği size_tgibi, aynı olması gerekmez unsigned int.
Alok Singhal

4
@Lazer - evet, size_timzasız bir tip olması garanti edilir.
Alok Singhal

2
@Celeritas no, yani imzasız bir tür sadece negatif olmayan değerleri temsil edebilir. Muhtemelen "Olumsuz değerleri temsil edemez" demeliydim.
Alok Singhal

4
@ JasonOster, ikisinin tamamlayıcısı C standardında bir gereklilik değildir. s2 - s1Taşmalar değeri bir taşarsa int, davranış tanımsızdır.
Alok Singhal

73

size_t herhangi bir dizi dizinini tutabilen bir türdür.

Uygulamaya bağlı olarak, şunlardan herhangi biri olabilir:

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

Makinemde size_tşu şekilde tanımlanır stddef.h:

typedef unsigned long size_t;

4
Kesinlikle typedef unsigned long size_tderleyiciye bağımlıdır. Yoksa her zaman böyle olduğunu mu söylüyorsun?
chux - Monica'yı eski durumuna getir

4
@chux: Gerçekten de, bir uygulamanın bunu böyle tanımlaması herkesin yaptığı anlamına gelmez. Vaka: 64-bit Windows. unsigned long32 bit, size_t64 bit.
Tim Čas

2
size_t'ın amacı tam olarak nedir? Ne zaman kendim için bir değişken oluşturabilirim: "int mysize_t;" veya "long mysize_t" veya "unsigned long mysize_t". Neden birisi bu değişkeni benim için yaratmış olmalı?
midkin

1
@midkin size_tbir değişken değildir. Bellekteki bir nesnenin boyutunu temsil etmek istediğinizde kullanabileceğiniz bir türdür.
Arjun Sreedharan

1
size_t32 bit makinede her zaman 32 bit, aynı şekilde 64 bit olduğu doğru mu?
John Wu

70

Ampirik tipseniz ,

echo | gcc -E -xc -include 'stddef.h' - | grep size_t

Ubuntu 14.04 64 bit GCC 4.8 çıkışı:

typedef long unsigned int size_t;

Not stddef.haltında GCC tarafından değil, glibc sağlanır src/gcc/ginclude/stddef.hGCC 4.2.

İlginç C99 görünümleri

  • mallocsize_tbağımsız değişken olarak alır , bu nedenle ayrılabilecek maksimum boyutu belirler.

    Ve ayrıca geri döndüğünden sizeof, herhangi bir dizinin maksimum boyutunu sınırladığını düşünüyorum.

    Ayrıca bkz: C'deki bir dizinin maksimum boyutu nedir?


1
Ben aynı ortam var, ancak, GCC'nin "-m32" seçeneğini geçen 32 bit için test ettik, sonuç: "typedef unsigned int size_t". @Ciro'daki bu harika komutu paylaştığın için teşekkürler, bana çok yardımcı oldu! :-)
silvioprog

2
Maddenin kendisi kafa karıştırıcı değil. Birçok soru sormaya ve birçok cevap vermeye çalışan kafa karıştırıcı zihindir. Bu cevap ve Arjun Sreedharan'ın cevabının hala insanların sormasını ve cevaplamasını engellemediğine şaşırdım.
biocyberman

1
Harika cevap, çünkü aslında en azından popüler bir Linux dağıtımında ne size_tolduğunu söylüyor .
Andrey Portnoy


19

Henüz kimse bundan bahsetmediği için, birincil dilsel önemi size_t, sizeofoperatörün bu tür bir değer döndürmesidir. Benzer şekilde, asıl önemi ptrdiff_tbir işaretçiyi diğerinden çıkarmanın o tipte bir değer vermesidir. Bunu kabul eden kütüphane işlevleri, bu tür işlevlerin boyutu, bu tür nesnelerin var olabileceği sistemlerde UINT_MAX değerini aşan nesnelerle çalışmasına izin vereceğinden, arayanları, daha büyük türdeki sistemlerde "imzasız int" den daha büyük bir değeri geçen kodu atmaya zorlamadan olası tüm nesneler için yeterli olacaktır.


Sorum her zaman oldu: Eğer sizeof hiç mevcut değilse, size_t için bir ihtiyaç olabilir mi?
Dean P

@DeanP: Belki hayır, ancak böyle şeyler için hangi argüman türünün kullanılması gerektiğine dair bir soru olurdu malloc(). Şahsen ben Çeşidi bağımsız değişken alır görülen sürümlerine sahip isterdi int, longve long longörneğin uygulanması daha kısa türleri ve diğerlerini teşvik bazı uygulamalarına, lmalloc(long n) {return (n < 0 || n > 32767) ? 0 : imalloc(n);}[bazı platformlarda, çağırmaya imalloc(123)çağırmaktan daha ucuz olacağını lmalloc(123);ve hatta bir platformda size_t16 bit, `` uzun '' bir değerde hesaplanan boyutu tahsis etmek isteyen kod ...
supercat

... değer, ayırıcı tarafından işlenenden daha büyükse, ayırmanın başarısız olmasına güvenebilmelidir.
supercat

11

Neden size_tvar olmamız gerektiğine ve buraya nasıl geldiğimize bakmak için:

Pragmatik olarak size_tve ptrdiff_t64 bitlik bir uygulamada 64 bit genişlik, 32 bitlik bir uygulamada 32 bit genişlik ve benzeri garanti edilir. Mevcut herhangi bir türü, her derleyicide eski kodu bozmadan anlamına gelmeye zorlayamazlardı.

A size_tveya ptrdiff_tmutlaka bir intptr_tveya ile aynı değildir uintptr_t. Onlar hala kullanımda iken belirli mimarileri farklı idi size_tve ptrdiff_t(örneğin 16 bit Windows gibi) 80'lerin sonlarına Standard ilave edildi ve C99 birçok yeni türü eklendiğinde modası geçmekte ancak henüz gitmiş değil. 16 bit korumalı modda x86, mümkün olan en büyük dizi veya yapının yalnızca 65.536 bayt boyutunda olabileceği bölümlere ayrılmış bir belleğe sahipti, ancak bir farişaretçinin kayıtlardan daha geniş, 32 bit genişliğinde olması gerekiyordu. Bunlarda, intptr_t32 bit genişliğinde olurdu size_tveptrdiff_t16 bit genişliğinde ve bir sicile sığabilir. Ve gelecekte ne tür bir işletim sisteminin yazılabileceğini kim bilebilirdi? Teorik olarak, i386 mimarisi, hiçbir işletim sisteminin gerçekte kullanmadığı 48 bitlik işaretçilerle 32 bitlik bir segmentasyon modeli sunar.

Bellek ofsetinin türü, longçok fazla eski kodun longtam olarak 32 bit genişlikte olduğunu varsaydığı için olamaz . Bu varsayım UNIX ve Windows API'larında da oluşturulmuştur. Ne yazık ki, diğer birçok eski kod da a'nın longbir işaretçi, bir dosya ofseti, 1970'ten bu yana geçen saniye sayısı ve daha fazlasını tutacak kadar geniş olduğunu varsaymıştır . POSIX şimdi, ikinci varsayımı öncekinin yerine doğru olmaya zorlamak için standart bir yol sunmaktadır, ancak bunların hiçbiri taşınabilir bir varsayım değildir.

Bunun intnedeni, 90'larda sadece küçük bir avuç derleyici int64 bit genişliğinde yapıldı. Sonra long32 bit geniş tutarak tuhaflaştılar . Standardın bir sonraki revizyonu, intdaha geniş olduğu için yasadışı ilan etti long, ancak intçoğu 64 bit sistemde hala 32 bit genişliğinde.

long long int32 bitlik sistemlerde bile en az 64 bit genişliğinde yaratıldığı için daha sonra eklenmiş olamazdı .

Yani yeni bir tipe ihtiyaç vardı. Olmasa bile, diğer tüm türler bir dizi veya nesne içindeki bir ofsetten başka bir şey ifade ediyordu. Ve 32-64 bit geçişinin fiyaskosundan bir ders olsaydı, bir türün hangi özelliklere sahip olması gerektiği konusunda spesifik olmalı ve farklı programlarda farklı şeyler ifade eden bir özellik kullanmamalıydı.


İle Katılmıyorum " size_tve ptrdiff_t64-bit uygulaması 64 bitlik genişlikte olmasını garanti altına alınmıştır" vb garanti abartılı. Aralığı size_t, öncelikle uygulamanın bellek kapasitesinden kaynaklanır. "n-bit uygulaması" esas olarak tamsayıların yerel işlemci genişliğidir. Kesinlikle birçok uygulama benzer boyutta bir bellek ve işlemci veri yolu genişliği kullanır, ancak yetersiz belleğe sahip geniş yerel tamsayılar veya çok fazla belleğe sahip dar işlemciler mevcuttur ve bu iki uygulama özelliğini birbirinden ayırır.
chux - Monica'yı

8

size_tve intbirbirinin yerine kullanılamaz. Örneğin 64 bit Linux'ta size_t64 bit boyuttadır (yani sizeof(void*)) int32 bittir.

Ayrıca size_timzasız olduğunu da unutmayın . İmzalı sürüme ihtiyacınız varsa, ssize_tbazı platformlarda vardır ve örneğinizle daha alakalı olacaktır.

Genel bir kural olarak int, çoğu genel durum için kullanılmasını öneririm ve sadece size_t/ ssize_tiçin özel bir ihtiyaç olduğunda ( mmap()örneğin) kullanın.


3

Genel olarak, 0'dan başlayıp yukarı doğru gidiyorsanız, taşmanın sizi negatif değer durumuna sokmasını önlemek için daima imzasız bir tür kullanın. Bu kritik öneme sahiptir, çünkü dizi sınırlarınız döngünüzün maksimum değerinden daha az olursa, ancak döngü maksimumunuz türünüzün maksimum değerinden büyük olursa, negatifin etrafına sarılırsınız ve bir segmentasyon hatası (SIGSEGV) yaşayabilirsiniz ). Bu nedenle, genel olarak, 0'dan başlayıp yukarı doğru bir döngü için asla int kullanmayın. İmzasız kullanın.


3
Tartışmanızı kabul edemem. Taşma hatasını sessizce dizinizdeki geçerli verilere erişmenin daha iyi olduğunu mu söylüyorsunuz?
maf-soft

1
@ maf-soft doğrudur. hata algılanmazsa, bir program çökmesinden daha kötü yapar. bu cevap neden oy aldı?
yoyo_fun

Dizinizdeki geçerli verilere erişirse, imzasız tür, işaretli tür sınırında taşmayacağından bir hata değildir. Bu mantık nedir çocuklar? Diyelim ki 256'dan fazla eleman dizisini yinelemek için char kullanıyorsunuz ... imzalı 127'de taşacak ve 128. eleman sigsegv olacak, ancak imzasız kullanırsanız, amaçlandığı gibi tüm diziyi geçecektir. Daha sonra, bir int kullandığınızda, dizileriniz gerçekten 2 milyar elemandan daha büyük olmayacaktır, bu yüzden her iki şekilde de önemli değil ...
Purple Ice

1
Tamsayı taşmasının bir hata olmadığı, pozitif veya negatif olup olmadığını düşünemiyorum. Segfault almamanız doğru davranış gördüğünüz anlamına gelmez! Ofsetiniz pozitif veya negatif olsun, bir segmentasyon hatası yaşayabilirsiniz; her şey bellek düzeninize bağlıdır. @ Morit, bu cevapla aynı şeyi söylediğini sanmıyorum; argümanınız, içine koymak istediğiniz en büyük değeri tutacak kadar büyük bir veri türü seçmeniz gerektiği gibi görünüyor, ki bu sadece sağduyu.
Soren Bjornstad

Bununla birlikte, semantik olarak döngü indeksleri için işaretsiz bir tip kullanmayı tercih ederim ; değişkeniniz asla negatif olmayacaksa, seçtiğiniz türde bunu da belirtebilirsiniz. Ayrıca, derleyicinin değerin negatif olduğu bir hatayı tespit etmesine izin verebilir, ancak GCC en azından bu belirli hatayı tespit etmede oldukça korkunçtur (bir keresinde imzasız -1'e başladım ve bir uyarı alamadım). Benzer şekilde, size_t bir dizi dizini için anlamsal olarak uygundur.
Soren Bjornstad

3

size_t, işaretsiz tam sayı veri türüdür. GNU C Kütüphanesi kullanan sistemlerde, bu imzasız int veya imzasız long int olacaktır. size_t genellikle dizi indeksleme ve döngü sayımı için kullanılır.


1

döngü değişkenleri tipik olarak 0'dan büyük veya ona eşit olduğundan, boyut_t veya işaretsiz herhangi bir tür döngü değişkeni olarak kullanılabilir.

Bir size_t nesnesi kullandığımızda, aritmetik de dahil olmak üzere tüm bağlamlarda kullanıldığından emin olmak zorundayız, sadece negatif olmayan değerler istiyoruz. Örneğin, aşağıdaki program kesinlikle beklenmedik 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

1

size_tyalnızca 0 ve 0'dan büyük tamsayı değerleri atayabilen, işaretsiz bir tamsayı veri türüdür. Herhangi bir nesnenin boyutundaki baytları ölçer ve sizeofoperatör tarafından döndürülür . constsözdizimi temsilidir size_t, ancak constsiz olmadan programı çalıştırabilirsiniz.

const size_t number;

size_tdizi indeksleme ve döngü sayımı için düzenli olarak kullanılır. Derleyici ise 32-bitçalışır unsigned int. Derleyici ise, 64-bitüzerinde unsigned long long intde çalışır. size_tDerleyici türüne bağlı olarak maksimum boyut için vardır .

size_tzaten tanımlayan <stdio.h>başlık dosyasına fakat aynı zamanda iki şekilde tanımlayabilir <stddef.h>, <stdlib.h>, <string.h>, <time.h>, <wchar.h>başlıklarını.

  • Örnek (ile const)
#include <stdio.h>

int main()
{
    const size_t value = 200;
    size_t i;
    int arr[value];

    for (i = 0 ; i < value ; ++i)
    {
        arr[i] = i;
    }

    size_t size = sizeof(arr);
    printf("size = %zu\n", size);
}

Çıktı -: size = 800


  • Örnek (yok const)
#include <stdio.h>

int main()
{
    size_t value = 200;
    size_t i;
    int arr[value];

    for (i = 0 ; i < value ; ++i)
    {
        arr[i] = i;
    }

    size_t size = sizeof(arr);
    printf("size = %zu\n", size);
}

Çıktı -: size = 800


-3

Anladığım kadarıyla, bit boyutu yerel mimarinin bir işaretçisini tutacak kadar büyük size_tbir unsignedtam sayı.

Yani:

sizeof(size_t) >= sizeof(void*)

16
Doğru değil. İşaretçi boyutu size_t. Birkaç örnek: x86 gerçek modundaki C derleyicilerin 32 bit FARveya HUGEişaretçileri olabilir, ancak size_t hala 16 bittir. Başka bir örnek: Watcom C, genişletilmiş bellek için 48 bit genişliğinde, ancak size_tolmayan özel bir yağ işaretçisine sahipti . Harvard mimarisine sahip gömülü denetleyicide, hiçbir ilişkiniz yoktur, çünkü her ikisi de farklı adres alanlarıyla ilgilidir.
Patrick Schlüter

1
Ve bu stackoverflow.com/questions/1572099/… 128 bit işaretçiler ve 32 bit ile daha fazla örnek AS / 400 varsize_t
Patrick Schlüter 26:30 '

Bu açıkça yanlıştır. Ancak, burada
saklayalım
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.