Apa itu size_t dalam C?

626

Saya bingung dengan size_tC. Saya tahu dikembalikan oleh sizeofoperator. Tapi apa sebenarnya itu? Apakah ini tipe data?

Katakanlah saya memiliki forloop:

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

Haruskah saya menggunakan int i;atau size_t i;?

Vijay
sumber
11
Jika itu adalah satu-satunya pilihan Anda, gunakan intjika some_sizeditandatangani, size_tjika tidak ditandatangani.
Nate
8
@Nate Itu tidak benar. POSIX memiliki tipe ssize_t tetapi tipe sebenarnya yang benar untuk digunakan adalah ptrdiff_t.
Steven Stewart-Gallus
2
Jawabannya tidak sejelas dalam Pemrograman Tingkat Rendah: C, Assembly, dan Eksekusi Program pada Intel® 64 . Seperti yang dinyatakan dalam buku ini, menggunakan indeks int imungkin tidak cukup untuk mengatasi array besar. Jadi dengan menggunakan size_t iAnda dapat mengatasi lebih banyak indeks, bahkan jika Anda memiliki array besar yang seharusnya tidak menjadi masalah. size_tadalah tipe data: biasanya a unsigned long inttetapi ini tergantung pada sistem Anda.
Bruno

Jawaban:

461

Dari Wikipedia :

Menurut standar ISO C 1999 (C99), 1999 size_tadalah tipe integer yang tidak ditandai setidaknya 16 bit (lihat bagian 7.17 dan 7.18.3).

size_tadalah tipe data yang tidak ditandatangani yang didefinisikan oleh beberapa standar C / C ++, misalnya standar C99 ISO / IEC 9899, ​​yang didefinisikan dalam stddef.h. 1 Dapat diimpor lebih lanjut dengan memasukkan stdlib.hfile ini termasuk sub internal stddef.h.

Tipe ini digunakan untuk merepresentasikan ukuran suatu objek. Fungsi pustaka yang mengambil atau mengembalikan ukuran mengharapkannya bertipe atau memiliki tipe pengembalian size_t. Lebih lanjut, ukuran operator berbasis compiler yang paling sering digunakan harus dievaluasi dengan nilai konstan yang kompatibel dengannya size_t.

Implikasinya, size_tadalah jenis yang dijamin untuk menyimpan indeks array apa pun.

sblom
sumber
4
"Fungsi perpustakaan yang mengambil atau mengembalikan ukuran mengharapkannya bertipe ... size_t" Kecuali stat itu () menggunakan off_t untuk ukuran file
Draemon
64
@Raemon Komentar itu mencerminkan kebingungan mendasar. size_tadalah untuk objek dalam memori. Standar C bahkan tidak mendefinisikan stat()atau off_t(itu adalah definisi POSIX) atau ada hubungannya dengan disk atau sistem file - berhenti sendiri di FILEstream. Manajemen memori virtual benar-benar berbeda dari sistem file dan manajemen file sejauh persyaratan ukuran berjalan, sehingga menyebutkan off_ttidak relevan di sini.
jw013
3
@ jw013: Saya jarang menyebutnya kebingungan mendasar, tetapi Anda membuat poin yang menarik. Namun, teks yang dikutip tidak mengatakan "ukuran objek dalam memori", dan "offset" hampir bukan nama yang baik untuk jenis ukuran terlepas dari tempat penyimpanannya.
Draemon
30
@Raemon Poin bagus. Jawaban ini mengutip Wikipedia, yang dalam hal ini tidak memiliki penjelasan terbaik, menurut pendapat saya. Standar C itu sendiri jauh lebih jelas: itu mendefinisikan size_tsebagai jenis hasil dari sizeofoperator (tentang 7.17p2 <stddef.h>). Bagian 6.5 menjelaskan dengan tepat bagaimana ekspresi C bekerja (6.5.3.4 untuk sizeof). Karena Anda tidak dapat menerapkan sizeofke file disk (kebanyakan karena C bahkan tidak menentukan cara kerja disk dan file), tidak ada ruang untuk kebingungan. Dengan kata lain, salahkan Wikipedia (dan jawaban ini untuk mengutip Wikipedia dan bukan standar C aktual).
jw013
2
@Raemon - Saya juga setuju dengan penilaian "kebingungan mendasar". Jika Anda belum membaca standar C / C ++, Anda mungkin berpikir "objek" mengacu pada "pemrograman berorientasi objek," yang tidak. Baca standar C, yang tidak memiliki objek OOP tersebut, tetapi belum memiliki objek, dan cari tahu. Jawabannya mungkin mengejutkan Anda!
Heath Hunnicutt
220

size_tadalah tipe yang tidak ditandatangani. Jadi, itu tidak dapat mewakili nilai negatif apa pun (<0). Anda menggunakannya saat menghitung sesuatu, dan yakin itu tidak negatif. Misalnya, strlen()mengembalikan a size_tkarena panjang string harus minimal 0.

Dalam contoh Anda, jika indeks loop Anda akan selalu lebih besar dari 0, mungkin masuk akal untuk digunakan size_t, atau tipe data yang tidak ditandatangani lainnya.

Ketika Anda menggunakan size_tobjek, Anda harus memastikan bahwa dalam semua konteks itu digunakan, termasuk aritmatika, Anda menginginkan nilai-nilai non-negatif. Sebagai contoh, katakanlah Anda memiliki:

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

dan Anda ingin menemukan perbedaan panjang str2dan str1. Anda tidak dapat melakukan:

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

Ini karena nilai yang ditetapkan untuk diffselalu akan menjadi angka positif, bahkan ketika s2 < s1, karena perhitungan dilakukan dengan tipe yang tidak ditandatangani. Dalam hal ini, tergantung pada apa use case Anda, Anda mungkin lebih baik menggunakan int(atau long long) untuk s1dan s2.

Ada beberapa fungsi dalam C / POSIX yang bisa / harus digunakan size_t, tetapi tidak karena alasan historis. Sebagai contoh, parameter kedua fgetsseharusnya idealnya size_t, tetapi adalah int.

Alok Singhal
sumber
8
@Lok: Dua pertanyaan: 1) berapa ukuran size_t? 2) mengapa saya lebih suka size_tdaripada sesuatu seperti unsigned int?
Lazer
2
@Lazer: ukuran size_tyaitu sizeof(size_t). Standar C menjamin SIZE_MAXpaling tidak 65535. size_tadalah jenis yang dikembalikan oleh sizeofoperator, dan digunakan di perpustakaan standar (misalnya strlenpengembalian size_t). Seperti yang dikatakan Brendan, size_ttidak harus sama dengan unsigned int.
Alok Singhal
4
@ Lazer - ya, size_tdijamin menjadi tipe yang tidak ditandatangani.
Alok Singhal
2
@Celeritas tidak, maksud saya jenis yang tidak ditandatangani hanya dapat mewakili nilai-nilai non-negatif. Saya mungkin seharusnya mengatakan "Itu tidak bisa mewakili nilai negatif".
Alok Singhal
4
@JasonOster, komplemen dua bukanlah persyaratan dalam standar C. Jika nilai s2 - s1overflow int, perilaku tidak terdefinisi.
Alok Singhal
73

size_t adalah tipe yang bisa menampung indeks array apa pun.

Bergantung pada implementasinya, dapat berupa:

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

Inilah cara size_tdidefinisikan di stddef.hmesin saya:

typedef unsigned long size_t;
Arjun Sreedharan
sumber
4
Tentunya typedef unsigned long size_ttergantung pada kompiler. Atau apakah Anda menyarankan agar selalu demikian?
chux - Reinstate Monica
4
@ chux: Memang, hanya karena satu implementasi mendefinisikannya seperti itu tidak berarti semua melakukannya. Contoh kasus: Windows 64-bit. unsigned longadalah 32-bit, size_tadalah 64-bit.
Tim Čas
2
apa tujuan size_t tepatnya? Ketika saya bisa membuat variabel untuk diri saya sendiri seperti: "int mysize_t;" atau "long mysize_t" atau "unsigned long mysize_t". Mengapa seseorang harus membuat variabel ini untuk saya?
midkin
1
@midkin size_tbukan variabel. Ini adalah tipe yang dapat Anda gunakan saat Anda ingin merepresentasikan ukuran objek dalam memori.
Arjun Sreedharan
1
apakah benar bahwa size_tselalu 32bits pada mesin 32-bit, 64bits juga?
John Wu
70

Jika Anda adalah tipe empiris ,

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

Output untuk Ubuntu 14.04 64-bit GCC 4.8:

typedef long unsigned int size_t;

Catatan yang stddef.hdisediakan oleh GCC dan tidak glibc src/gcc/ginclude/stddef.hdi bawah dalam GCC 4.2.

Penampilan C99 yang menarik

  • mallocmengambil size_tsebagai argumen, sehingga menentukan ukuran maksimum yang mungkin dialokasikan.

    Dan karena itu juga dikembalikan oleh sizeof, saya pikir itu membatasi ukuran maksimum dari array apa pun.

    Lihat juga: Berapa ukuran maksimum array dalam C?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
1
Saya memiliki lingkungan yang sama, namun, saya telah mengujinya untuk 32 bit, melewati opsi "-m32" GCC, hasilnya adalah: "typedef unsigned int size_t". Terima kasih telah berbagi perintah yang luar biasa ini, Cirra, sangat membantu saya! :-)
silvioprog
2
Masalahnya sendiri tidak membingungkan. Pikiran yang membingungkanlah yang mencoba mengajukan banyak pertanyaan, dan memberikan banyak jawaban. Saya terkejut bahwa jawaban ini dan jawaban Arjun Sreedharan masih tidak menghentikan orang untuk bertanya dan menjawab.
biocyberman
1
Jawaban yang bagus, karena itu benar-benar memberitahu Anda apa yang size_tada , setidaknya pada distro Linux yang populer.
Andrey Portnoy
25

Halaman manual untuk types.h mengatakan:

size_t akan menjadi tipe integer yang tidak ditandatangani

codaddict
sumber
19

Karena belum ada yang menyebutkannya, makna linguistik utama size_tadalah bahwa sizeofoperator mengembalikan nilai jenis itu. Demikian juga, signifikansi utama dari ptrdiff_tpengurangan satu pointer dari yang lain akan menghasilkan nilai dari jenis itu. Fungsi pustaka yang menerimanya melakukannya karena itu akan memungkinkan fungsi tersebut untuk bekerja dengan objek yang ukurannya melebihi UINT_MAX pada sistem di mana objek tersebut dapat ada, tanpa memaksa penelepon membuang kode yang melewati nilai yang lebih besar dari "unsigned int" pada sistem di mana tipe yang lebih besar akan cukup untuk semua objek yang mungkin.

supercat
sumber
Pertanyaan saya selalu: Jika sizeof tidak pernah ada, apakah akan ada kebutuhan untuk size_t?
Dean P
@DeanP: Mungkin tidak, meskipun kemudian akan ada pertanyaan tentang jenis argumen apa yang harus digunakan untuk hal-hal seperti malloc(). Secara pribadi, saya ingin melihat versi yang mengambil argumen tipe int,, longdan long long, dengan beberapa implementasi yang mempromosikan tipe yang lebih pendek dan implementasi lainnya misalnya lmalloc(long n) {return (n < 0 || n > 32767) ? 0 : imalloc(n);}[pada beberapa platform, panggilan ke imalloc(123)akan lebih murah daripada menelepon lmalloc(123);, dan bahkan pada platform di mana size_t16 bit, kode yang ingin mengalokasikan ukuran yang dihitung dalam nilai `long` ...
supercat
... harus dapat mengandalkan alokasi yang gagal jika nilainya lebih besar daripada yang dapat ditangani oleh pengalokasi.
supercat
11

Untuk size_tmengetahui mengapa perlu ada dan bagaimana kita sampai di sini:

Dalam istilah pragmatis, size_tdan ptrdiff_tdijamin selebar 64 bit pada implementasi 64-bit, lebar 32 bit pada implementasi 32-bit, dan seterusnya. Mereka tidak bisa memaksa tipe apa pun yang ada berarti bahwa, pada setiap kompiler, tanpa melanggar kode warisan.

A size_tatau ptrdiff_ttidak harus sama dengan intptr_tatau uintptr_t. Mereka berbeda pada arsitektur tertentu yang masih digunakan kapan size_tdan ptrdiff_tditambahkan ke Standar pada akhir 80-an, dan menjadi usang ketika C99 menambahkan banyak jenis baru tetapi belum hilang (seperti Windows 16-bit). X86 dalam mode terproteksi 16-bit memiliki memori tersegmentasi di mana susunan atau struktur terbesar yang mungkin hanya berukuran 65.536 byte, tetapi farpenunjuk harus lebar 32 bit, lebih lebar dari register. Pada mereka, intptr_takan menjadi lebar 32 bit tetapi size_tdanptrdiff_tbisa 16 bit lebar dan muat dalam register. Dan siapa yang tahu sistem operasi apa yang mungkin ditulis di masa depan? Secara teori, arsitektur i386 menawarkan model segmentasi 32-bit dengan pointer 48-bit yang tidak pernah digunakan oleh sistem operasi.

Jenis offset memori tidak mungkin longkarena terlalu banyak kode lama mengasumsikan bahwa longpersis 32 bit. Asumsi ini bahkan dibangun ke dalam UNIX dan Windows API. Sayangnya, banyak kode lawas lainnya juga berasumsi bahwa a longcukup lebar untuk menampung sebuah pointer, sebuah file offset, jumlah detik yang telah berlalu sejak tahun 1970, dan seterusnya. POSIX sekarang menyediakan cara terstandardisasi untuk memaksakan asumsi yang terakhir menjadi benar, bukan yang sebelumnya, tetapi tidak ada asumsi portabel untuk dibuat.

Itu tidak mungkin intkarena hanya segelintir kompiler di tahun 90-an yang membuat intlebar 64 bit. Kemudian mereka benar-benar aneh dengan menjaga longlebar 32 bit. Revisi Standar berikutnya menyatakan ilegal untuk intmenjadi lebih luas daripada long, tetapi intmasih lebar 32 bit pada kebanyakan sistem 64-bit.

Tidak mungkin long long int, yang bagaimanapun ditambahkan kemudian, karena itu dibuat setidaknya 64 bit lebar bahkan pada sistem 32-bit.

Jadi, diperlukan tipe baru. Bahkan jika tidak, semua tipe lainnya berarti sesuatu selain offset dalam array atau objek. Dan jika ada satu pelajaran dari kegagalan migrasi 32-ke-64-bit, itu harus spesifik tentang properti jenis apa yang perlu dimiliki, dan tidak menggunakan satu yang berarti berbeda dalam program yang berbeda.

Davislor
sumber
Tidak setuju dengan " size_tdan ptrdiff_tdijamin selebar 64 bit pada implementasi 64-bit", dll. Jaminan ini dilebih-lebihkan. Kisaran size_tini terutama didorong oleh kapasitas memori implementasi. "implementasi n-bit" terutama lebar prosesor asli dari bilangan bulat. Tentu saja banyak implementasi menggunakan memori dengan ukuran yang sama dan lebar bus prosesor, tetapi integer asli yang lebar dengan memori yang sedikit atau prosesor yang sempit dengan banyak memori ada dan mendorong kedua properti implementasi ini secara terpisah.
chux
8

size_tdan inttidak bisa dipertukarkan. Misalnya pada 64-bit Linux size_tberukuran 64-bit (yaitu sizeof(void*)) tetapi int32-bit.

Perhatikan juga bahwa size_ttidak ditandatangani. Jika Anda perlu versi yang ditandatangani maka ada ssize_tpada beberapa platform dan itu akan lebih relevan dengan contoh Anda.

Sebagai aturan umum, saya akan menyarankan menggunakan intuntuk sebagian besar kasus umum dan hanya menggunakan size_t/ ssize_tketika ada kebutuhan khusus untuk itu (dengan mmap()misalnya).

dtoux
sumber
3

Secara umum, jika Anda mulai dari 0 dan naik, selalu gunakan tipe yang tidak ditandatangani untuk menghindari overflow yang membawa Anda ke situasi nilai negatif. Ini sangat penting, karena jika batas array Anda kurang dari maks dari loop Anda, tetapi max loop Anda lebih besar dari max tipe Anda, Anda akan membungkus negatif dan Anda mungkin mengalami kesalahan segmentasi (SIGSEGV ). Jadi, secara umum, jangan pernah gunakan int untuk loop mulai dari 0 dan naik. Gunakan tanda tangan.

Menandai
sumber
3
Saya tidak dapat menerima argumentasi Anda. Anda mengatakan bahwa lebih baik bug meluap diam-diam mengarah pada mengakses data yang valid dalam array Anda?
maf-soft
1
@ maf-lunak benar. jika kesalahan tidak terdeteksi itu membuatnya lebih buruk daripada crash program. mengapa jawaban ini mendapat suara terbalik?
yoyo_fun
Jika mengakses data yang valid di array Anda maka itu bukan bug karena tipe yang tidak ditandatangani tidak akan meluap pada batas yang ditandatangani tipe yang akan. Logika apa ini? Katakanlah karena alasan tertentu Anda menggunakan char untuk beralih lebih dari 256 elemen array ... ditandatangani akan meluap pada 127 dan elemen 128 akan sigsegv, tetapi jika Anda menggunakan unsigned, maka ia akan melalui seluruh array sebagaimana dimaksud. Kemudian lagi, ketika Anda menggunakan int, array Anda tidak akan benar-benar lebih besar dari 2 miliar elemen jadi apa pun itu tidak masalah ...
Es Ungu
1
Saya tidak bisa membayangkan situasi di mana integer overflow bukan bug, apakah itu membungkus positif atau negatif. Hanya karena Anda tidak mendapatkan segfault bukan berarti Anda melihat perilaku yang benar! Dan Anda dapat mengalami kesalahan segmentasi, atau tidak, apakah offset Anda positif atau negatif; itu semua tergantung pada tata letak memori Anda. @PurpleIce, saya tidak berpikir Anda mengatakan hal yang sama dengan jawaban ini; argumen Anda tampaknya adalah bahwa Anda harus memilih tipe data yang cukup besar untuk menampung nilai terbesar yang ingin Anda masukkan ke dalamnya, yang merupakan akal sehat biasa.
Soren Bjornstad
Yang mengatakan, saya lebih suka menggunakan tipe unsigned untuk indeks loop secara semantik ; jika variabel Anda tidak akan menjadi negatif, maka Anda mungkin juga menunjukkan bahwa dalam tipe yang Anda pilih. Itu juga bisa memungkinkan kompiler untuk menemukan bug di mana nilainya berakhir negatif, meskipun GCC setidaknya cukup mengerikan dalam menemukan kesalahan khusus ini (pada satu kesempatan saya menginisialisasi unsigned ke -1 dan tidak mendapatkan peringatan). Demikian pula, size_t secara semantik cocok untuk indeks array.
Soren Bjornstad
3

size_t adalah tipe data integer yang tidak ditandatangani. Pada sistem yang menggunakan Perpustakaan C GNU, ini akan menjadi unsigned int atau unsigned long int. size_t umumnya digunakan untuk pengindeksan array dan penghitungan loop.

Pangeran
sumber
1

size_t atau tipe yang tidak ditandatangani mungkin terlihat digunakan sebagai variabel loop karena variabel loop biasanya lebih besar dari atau sama dengan 0.

Ketika kita menggunakan objek size_t , kita harus memastikan bahwa dalam semua konteks yang digunakan, termasuk aritmatika, kita hanya menginginkan nilai-nilai non-negatif. Misalnya, program berikut pasti akan memberikan hasil yang tidak terduga:

// 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
bishwas pokharel
sumber
1

size_tadalah tipe data integer yang tidak ditandatangani yang hanya dapat menetapkan 0 dan lebih besar dari 0 nilai integer. Ini mengukur byte dari ukuran objek apa pun dan dikembalikan oleh sizeofoperator. constadalah representasi sintaks size_t, tetapi tanpa constAnda dapat menjalankan program.

const size_t number;

size_tsecara teratur digunakan untuk pengindeksan array dan penghitungan loop. Jika kompiler 32-bititu akan bekerja unsigned int. Jika kompiler 64-bititu akan berfungsi unsigned long long intjuga. Ada untuk ukuran maksimum size_ttergantung pada jenis kompiler.

size_tsudah menentukan pada <stdio.h>file header, tetapi juga dapat menentukan oleh <stddef.h>, <stdlib.h>, <string.h>, <time.h>, <wchar.h>header.

  • Contoh (dengan 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);
}

Output -: size = 800


  • Contoh (tanpa 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);
}

Output -: size = 800

Kalana
sumber
-3

Dari pemahaman saya, size_tadalah unsignedbilangan bulat yang ukuran bitnya cukup besar untuk menampung pointer dari arsitektur asli.

Begitu:

sizeof(size_t) >= sizeof(void*)
David Zechiel
sumber
16
Tidak benar. Ukuran pointer bisa lebih besar dari size_t. Beberapa contoh: C compiler pada mode real x86 dapat memiliki 32 bit FARatau HUGEpointer tetapi size_t masih 16 bit. Contoh lain: Watcom C dulu memiliki penunjuk lemak khusus untuk memori tambahan yang lebar 48 bit, tetapi size_ttidak. Pada pengontrol tertanam dengan arsitektur Harvard, Anda tidak memiliki korelasi juga, karena keduanya menyangkut ruang alamat yang berbeda.
Patrick Schlüter
1
Dan pada stackoverflow.com/questions/1572099/... ada lebih banyak contoh AS / 400 dengan 128 bit pointer dan 32 bitsize_t
Patrick Schlüter
Ini sangat salah. Namun, mari kita simpan di sini
Antti Haapala