Saya memiliki program yang membaca daftar "mentah" entitas dalam game, dan saya bermaksud membuat array yang menyimpan nomor indeks (int) dari jumlah entitas yang tidak ditentukan, untuk memproses berbagai hal. Saya ingin menghindari penggunaan terlalu banyak memori atau CPU untuk menjaga indeks seperti itu ...
Solusi cepat dan kotor yang saya gunakan sejauh ini adalah mendeklarasikan, dalam fungsi pemrosesan utama (fokus lokal) array dengan ukuran entitas game maksimum, dan integer lain untuk melacak berapa banyak yang telah ditambahkan ke daftar. Ini tidak memuaskan, karena setiap daftar memiliki 3000+ array, yang tidak terlalu banyak, tetapi terasa seperti sia-sia, karena saya mungkin akan menggunakan solusi untuk 6-7 daftar untuk berbagai fungsi.
Saya belum menemukan solusi spesifik C (bukan C ++ atau C #) untuk mencapai hal ini. Saya dapat menggunakan pointer, tetapi saya agak takut menggunakannya (kecuali hanya itu cara yang mungkin).
Array tidak meninggalkan ruang lingkup fungsi lokal (mereka akan diteruskan ke fungsi, kemudian dibuang), jika hal-hal berubah.
Jika pointer adalah satu-satunya solusi, bagaimana saya bisa melacak mereka untuk menghindari kebocoran?
sumber
Jawaban:
Jika Anda membutuhkan array dinamis, Anda tidak bisa lepas dari pointer. Mengapa kamu takut? Mereka tidak akan menggigit (asalkan Anda berhati-hati, itu). Tidak ada larik dinamis bawaan di C, Anda hanya perlu menulis sendiri. Di C ++, Anda bisa menggunakan
std::vector
kelas bawaan. C # dan hampir semua bahasa tingkat tinggi lainnya juga memiliki beberapa kelas serupa yang mengelola array dinamis untuk Anda.Jika Anda berencana untuk menulis sendiri, berikut ini adalah sesuatu untuk Anda mulai: sebagian besar implementasi array dinamis bekerja dengan memulai dengan array beberapa ukuran standar (kecil), lalu setiap kali Anda kehabisan ruang saat menambahkan elemen baru, gandakan ukuran array. Seperti yang Anda lihat pada contoh di bawah ini, sama sekali tidak sulit: (Saya telah menghilangkan pemeriksaan keamanan untuk singkatnya)
Menggunakannya sama mudahnya:
sumber
removeArray
metode yang menghilangkan elemen terakhir juga akan rapi. Jika Anda mengizinkannya, saya akan menambahkannya ke sampel kode Anda.Ada beberapa opsi yang bisa saya pikirkan.
array[100]
tanpa harus berjalan1-99
terlebih dahulu. Dan mungkin tidak berguna bagi Anda untuk menggunakannya.Sulit mengatakan opsi apa yang terbaik untuk situasi Anda. Cukup membuat array besar tentu saja salah satu solusi termudah dan seharusnya tidak memberi Anda banyak masalah kecuali itu benar-benar besar.
sumber
realloc
dengan # 3 - mengalokasikan array ukuran normal, dan kemudian menumbuhkannya setiap kali Anda kehabisan.realloc
akan menangani penyalinan data Anda jika perlu. Sedangkan untuk pertanyaan OP tentang manajemen memori, Anda hanya perlumalloc
sekali di awal,free
sekali di akhir, danrealloc
setiap kali Anda kehabisan ruang. Itu tidak terlalu buruk.7 * 3264 * 32 bit
terdengar seperti91.39 kilobytes
. Tidak banyak dengan standar apa pun hari ini;)realloc
kembaliNULL
:a->array = (int *)realloc(a->array, a->size * sizeof(int));
... Mungkin sebaiknya ditulis sebagai:int *temp = realloc(a->array, a->size * sizeof *a->array); a->array = temp;
... Dengan cara itu akan jelas bahwa apa pun yang terjadi perlu terjadi sebelum yangNULL
nilai ditugaskan untuka->array
(jika sama sekali).Seperti segala sesuatu yang tampak lebih menakutkan pada awalnya daripada kemudian, cara terbaik untuk mengatasi rasa takut awal adalah dengan membenamkan diri Anda ke dalam ketidaknyamanan yang tidak diketahui ! Pada saat-saat seperti itulah yang paling banyak kita pelajari.
Sayangnya, ada batasannya. Saat Anda masih belajar untuk menggunakan suatu fungsi, Anda tidak seharusnya mengambil peran sebagai guru, misalnya. Saya sering membaca jawaban dari mereka yang tampaknya tidak tahu cara menggunakan
realloc
(yaitu jawaban yang saat ini diterima! ) Memberi tahu orang lain bagaimana menggunakannya secara salah, kadang-kadang dengan kedok bahwa mereka telah menghilangkan penanganan kesalahan , meskipun ini adalah perangkap umum yang perlu disebutkan. Inilah jawaban yang menjelaskan cara menggunakanrealloc
dengan benar . Perhatikan bahwa jawabannya menyimpan nilai balik ke variabel yang berbeda untuk melakukan pengecekan kesalahan.Setiap kali Anda memanggil fungsi, dan setiap kali Anda menggunakan array, Anda menggunakan pointer. Konversi ini terjadi secara implisit, yang jika sesuatu harus lebih menakutkan, karena hal-hal yang tidak kita lihat yang paling sering menyebabkan masalah. Misalnya, kebocoran memori ...
Operator array adalah operator pointer.
array[x]
benar-benar jalan pintas untuk*(array + x)
, yang dapat dipecah menjadi:*
dan(array + x)
. Kemungkinan besar itulah*
yang membingungkan Anda. Kami lebih lanjut dapat menghilangkan penambahan dari masalah dengan mengasumsikanx
menjadi0
, dengan demikian,array[0]
menjadi*array
karena menambahkan0
tidak akan mengubah nilai ...... dan dengan demikian kita dapat melihat bahwa
*array
itu setara denganarray[0]
. Anda dapat menggunakan satu di mana Anda ingin menggunakan yang lain, dan sebaliknya. Operator array adalah operator pointer.malloc
,realloc
dan teman tidak menemukan konsep pointer yang telah Anda gunakan selama ini; mereka hanya menggunakan ini untuk mengimplementasikan beberapa fitur lain, yang merupakan bentuk berbeda dari durasi penyimpanan, paling cocok ketika Anda menginginkan perubahan ukuran yang drastis dan dinamis .Sangat memalukan bahwa jawaban yang saat ini diterima juga bertentangan dengan saran-saran lain yang sangat beralasan tentang StackOverflow , dan pada saat yang sama, kehilangan kesempatan untuk memperkenalkan fitur yang tidak banyak diketahui yang menyinari dengan tepat usecase ini: array fleksibel anggota! Itu sebenarnya jawaban yang cukup rusak ... :(
Ketika Anda mendefinisikan Anda
struct
, mendeklarasikan array Anda di akhir struktur, tanpa batas atas. Sebagai contoh:Ini akan memungkinkan Anda untuk menyatukan array Anda
int
ke dalam alokasi yang sama dengan Andacount
, dan mengikat mereka seperti ini bisa sangat berguna !sizeof (struct int_list)
akan bertindak seolah-olahvalue
memiliki ukuran 0, jadi itu akan memberi tahu Anda ukuran struktur dengan daftar kosong . Anda masih perlu menambahkan ukuran yang diteruskan kerealloc
untuk menentukan ukuran daftar Anda.Tip berguna lainnya adalah untuk mengingat yang
realloc(NULL, x)
setara denganmalloc(x)
, dan kita dapat menggunakan ini untuk menyederhanakan kode kita. Sebagai contoh:Alasan saya memilih untuk menggunakan
struct int_list **
sebagai argumen pertama mungkin tidak langsung tampak jelas, tetapi jika Anda berpikir tentang argumen kedua, setiap perubahan yang dibuatvalue
dari dalampush_back
tidak akan terlihat oleh fungsi yang kami panggil, kan? Hal yang sama berlaku untuk argumen pertama, dan kita harus dapat memodifikasi kitaarray
, tidak hanya di sini tetapi mungkin juga dalam fungsi lain / s kita berikan kepada ...array
mulai menunjuk apa-apa; ini adalah daftar kosong. Menginisialisasi itu sama dengan menambahkannya. Sebagai contoh:PS Ingat
free(array);
ketika Anda selesai dengan itu!sumber
array[x]
Benar-benar jalan pintas untuk*(array + x)
, [...]" Apakah Anda yakin tentang itu ???? Lihat eksposisi perilaku mereka yang berbeda: eli.thegreenplace.net/2009/10/21/… .array[index]
sebenarnyaptr[index]
menyamar ... "Definisi operator subskrip[]
adalah yangE1[E2]
identik dengan(*((E1)+(E2)))
" Anda tidak dapat menyangkal stdint main(void) { unsigned char lower[] = "abcdefghijklmnopqrstuvwxyz"; for (size_t x = 0; x < sizeof lower - 1; x++) { putchar(x[lower]); } }
... Anda mungkin perlu#include <stdio.h>
dan<stddef.h>
... Apakah Anda melihat bagaimana saya menulisx[lower]
(denganx
menjadi tipe integer) daripadalower[x]
? Kompiler C tidak peduli, karena*(lower + x)
memiliki nilai yang sama dengan*(x + lower)
, danlower[x]
merupakan yang pertama di mana-sepertix[lower]
yang terakhir. Semua ungkapan ini setara. Cobalah mereka ... lihat sendiri, jika Anda tidak dapat menerima kata-kata saya ...gcc
atauclang
untuk semua kompilasi C Anda, karena Anda akan menemukan ada begitu banyak paket yang telah mengadopsi fitur C99 ...Membangun desain Matteo Furlans , ketika ia mengatakan " implementasi array paling dinamis bekerja dengan memulai dengan array beberapa ukuran standar (kecil), lalu setiap kali Anda kehabisan ruang saat menambahkan elemen baru, gandakan ukuran array ". Perbedaan dalam " pekerjaan dalam proses " di bawah ini adalah bahwa ia tidak berlipat ganda dalam ukuran, itu bertujuan hanya menggunakan apa yang diperlukan. Saya juga menghilangkan pemeriksaan keamanan untuk kesederhanaan ... Juga membangun ide brimboriums , saya telah mencoba menambahkan fungsi hapus ke kode ...
File storage.h terlihat seperti ini ...
File storage.c terlihat seperti ini ...
Main.c terlihat seperti ini ...
Nantikan kritik konstruktif untuk diikuti ...
sumber
malloc()
sebelum mencoba menggunakan alokasi. Dalam nada yang sama, adalah kesalahan untuk secara langsung menetapkan hasilrealloc()
ke pointer ke memori asli yang dialokasikan kembali; jikarealloc()
gagal,NULL
dikembalikan, dan kode dibiarkan dengan kebocoran memori. Jauh lebih efisien untuk menggandakan memori saat mengubah ukuran daripada menambah 1 ruang pada satu waktu: lebih sedikit panggilan kerealloc()
.int
, dll.) Sekaligus . Menggandakan adalah solusi khas, tetapi saya tidak berpikir bahwa ada solusi optimal yang cocok untuk semua keadaan. Inilah mengapa penggandaan adalah ide yang bagus (beberapa faktor lain seperti 1,5 juga akan baik-baik saja): jika Anda mulai dengan alokasi yang masuk akal, Anda mungkin tidak perlu realokasi sama sekali. Saat dibutuhkan lebih banyak memori, alokasi yang masuk akal digandakan, dan seterusnya. Dengan cara ini Anda mungkin hanya perlu satu atau dua panggilan kerealloc()
.Ketika Anda mengatakannya
Anda pada dasarnya mengatakan Anda menggunakan "pointer", tetapi yang merupakan pointer lokal array-lebar, bukan pointer memory-wide. Karena Anda secara konseptual sudah menggunakan "pointer" (yaitu nomor id yang merujuk ke elemen dalam array), mengapa Anda tidak menggunakan pointer biasa (mis. Angka id yang merujuk ke elemen dalam array terbesar: seluruh memori ).
Alih-alih objek Anda menyimpan nomor id sumber daya, Anda dapat membuatnya menyimpan pointer. Pada dasarnya hal yang sama, tetapi jauh lebih efisien karena kita menghindari mengubah "array + index" menjadi "pointer".
Pointer tidak menakutkan jika Anda menganggapnya sebagai indeks array untuk seluruh memori (yang sebenarnya)
sumber
Untuk membuat berbagai item tanpa batas dari jenis apa pun:
dan cara menggunakannya:
Vektor / array ini dapat menampung semua jenis barang dan ukurannya benar-benar dinamis.
sumber
Yah, saya kira jika Anda perlu menghapus elemen, Anda akan membuat salinan array yang membenci elemen yang akan dikecualikan.
Asumsikan itu
getElement2BRemove()
,copy2TempVector( void* ...)
danfillFromTempVector(...)
merupakan metode bantu untuk menangani vektor temp.sumber