Dasar-dasar Pointer Pemula

Pendahuluan


Saat ini, karena peningkatan dan murahnya teknologi, jumlah memori dan daya pemrosesan terus bertambah.

Menurut Hukum Moore:
Jumlah transistor yang ditempatkan pada chip sirkuit terintegrasi berlipat ganda setiap 24 bulan.
Perlu dicatat bahwa dua parameter diubah:

  • Jumlah transistor
  • Dimensi modul

Prinsip yang sama diproyeksikan pada jumlah RAM (DRAM).

Sekarang masalah memori tidak akut, karena jumlah memori selama 10 tahun terakhir telah meningkat 16 kali per mati.

Kebanyakan Bahasa Pemrograman Tingkat Tinggi (PL) sudah "tersembunyi" bersembunyi dengan memori dari programmer. Dan, karena pertanyaan ini sedang tidur, muncul kasta baru programmer yang tidak mengerti atau tidak ingin memahami bagaimana kerja dengan memori bekerja.

Dalam topik ini, kami akan mempertimbangkan poin utama bekerja dengan memori menggunakan contoh bahasa C ++, karena itu adalah salah satu dari beberapa bahasa imperatif yang mendukung kerja langsung dengan memori dan mendukung OOP.

Untuk apa ITU?


Perlu disebutkan di sini, artikel ini dirancang untuk orang-orang yang baru memulai perjalanan mereka di C ++ atau hanya ingin memiliki ide tentang memori dinamis.

Saat runtime, setiap program menyimpan sebagian memori untuk dirinya sendiri dalam DRAM. Semua ruang bebas DRAM lainnya disebut "Heap" (Bahasa Inggris "Heap"). Alokasi memori selama eksekusi untuk kebutuhan program terjadi tepat dari heap dan disebut alokasi memori dinamis.

Seluruh masalah adalah bahwa jika Anda tidak berhati-hati membersihkan memori yang dialokasikan ketika tidak lagi diperlukan, kebocoran memori yang disebut dapat terjadi, di mana sistem (program) Anda hanya hang. Mirip dengan mobil yang macet di tengah jalan karena seseorang lupa mengisi bahan bakar tepat waktu.

Apa yang harus Anda ketahui
Kebanyakan PL modern dilengkapi dengan pengumpul sampah dan membersihkan ingatan mereka sendiri.
Namun, C ++ telah memantapkan dirinya sebagai salah satu API berkinerja tercepat, sebagian karena semua pekerjaan dengan memori di dalamnya dilakukan secara manual.


baru dan hapus


Alokasi memori bisa statis dan dinamis. Alokasi memori statis disebut alokasi memori satu kali selama kompilasi program, dan jumlah memori statis tidak berubah saat runtime. Contoh klasik adalah deklarasi variabel atau array integer. Tetapi bagaimana jika programmer tidak tahu sebelumnya berapa banyak elemen yang dibutuhkan dalam wadah?
Penggunaan memori dinamis disarankan bila perlu mengatur alokasi memori untuk kebutuhan program sebagaimana diperlukan.
Operator baru bertanggung jawab untuk mengalokasikan memori dinamis dalam C ++, dan delete bertanggung jawab untuk menghapusnya.
Operator baru mengembalikan hasil operasi pointer ke instance baru kelas.
Sintaksnya adalah ini:

| pointer tipe data (T1) | * | nama penunjuk | = baru | tipe T1 |;

Setelah operator baru , Anda dapat menggunakan konstruktor, misalnya, untuk menginisialisasi bidang kelas.
Perlu dicatat bahwa kebocoran memori yang sama terjadi tepat ketika programmer kehilangan kendali atas alokasi.
Penting untuk diingat:
Jika Anda lupa tentang menghapus memori dinamis dari elemen-elemen yang "tidak perlu" dihabiskan, maka cepat atau lambat sebuah momen kritis akan datang ketika memori tidak akan diambil.

Contoh alokasi memori dan pembersihannya:
int main{ // ,       new int *ptr = new int(); //   cout<<*ptr<<endl; // ,     delete ptr; //  delete     ,         return 0; } 


Artikel ini tidak akan membahas apa yang disebut pointer "pintar", karena topiknya sangat luas, tetapi, singkatnya: "pointer Smart sebagian mengotomatiskan proses pembersihan memori untuk programmer."

Pointer


Pointer bertanggung jawab untuk bekerja dengan memori dinamis di C ++. Ini adalah topik di mana nafsu makan memanjakan pemula.

Anda dapat mendeklarasikan pointer menggunakan operator * . Secara default, ini akan menunjuk ke beberapa wilayah memori acak. Agar kita dapat mengakses area memori yang kita butuhkan, kita perlu meneruskan tautan (operator & ) ke variabel yang diinginkan.

Pointer itu sendiri hanyalah alamat sel memori, dan untuk mengakses data yang disimpan dalam sel ini, ia harus ditinjau ulang.

Retret penting


Jika Anda mencoba menampilkan pointer tanpa dereferencing, maka alih-alih nilai dari area memori yang ditunjuknya, alamat area memori ini ditampilkan.
Untuk melakukan dereferensi pointer, cukup letakkan operator * di depan namanya.


 int main() { // ,          int* pNum= new int(1) ; cout<<*pNum<<endl; //    ,        ,       (   int   ) pNum++; cout<<*pNum<<endl; // ,         return 0; } 



Melihat contoh-contoh seperti itu, saya ingin bertanya: "Mengapa ini bahkan perlu jika Anda dapat langsung mengambil variabel?"

Contoh lain:

Kami memiliki kelas Pemrogram yang menjelaskan anggota tim pemrogram yang tidak tahu tentang petunjuk.

  class Programmers{ public: Programmers(){} Programmers(int iWeight, int iAge){ this->weight = iWeight; this->age = iAge; } int weight; int age; }; int main() { //     Programmers int size = 9; Programmers *prog [size]; //  Programmers Programmers *ptr = nullptr; //     Programmers       //          for (int i =0;i<size;i++) { ptr=new Programmers(i+100,i); prog[i]=ptr; } return 0; } 

Dengan cara ini, memori dapat dimanipulasi sesuka kita. Dan itulah sebabnya, ketika bekerja dengan ingatan, Anda dapat "menembak diri Anda sendiri." Perlu dicatat bahwa bekerja dengan pointer jauh lebih cepat, karena nilai itu sendiri tidak disalin, tetapi hanya tautan ke alamat tertentu yang diberikan padanya.

Ngomong-ngomong, kata kunci yang begitu populer ini memberikan pointer ke objek kelas saat ini. Petunjuk ini ada di mana-mana.

Contoh pointer dalam kehidupan sehari-hari:

Bayangkan sebuah situasi ketika Anda memesan hidangan di restoran. Untuk melakukan pemesanan, Anda hanya perlu menunjuk ke hidangan di menu dan Anda akan siap. Dengan cara yang sama, pengunjung lain ke restoran menunjukkan item yang diinginkan dalam menu. Dengan demikian, setiap baris dalam menu adalah pointer ke fungsi memasak hidangan, dan pointer ini dibuat pada tahap desain menu ini sendiri.

Contoh Function Pointer
 //      void Chicken(){ cout<<"Wait 5 min...Chicken is cooking"<<endl; } void JustWater(){ cout<<"Take your water"<<endl; } int main() { //    void   void (*ptr)(); ptr = Chicken; ptr(); ptr=JustWater; ptr(); return 0; } 



Kembali ke programer kami. Misalkan sekarang kita perlu membawa bidang kelas ke bagian pribadi , sebagaimana layaknya prinsip enkapsulasi dari OOP, maka kita perlu rajin rajin mendapatkan akses baca ke bidang ini. Tapi bayangkan kita tidak memiliki 2 bidang, tetapi 100, dan untuk ini kita perlu menulis accessor kita sendiri untuk masing-masing?

Spoiler
Yah, tentu saja tidak, saya bahkan tidak mengerti mengapa Anda membuka spoiler ini.

Untuk melakukan ini, kita akan membuat "accessor" dari tipe void dan meneruskan argumen dengan referensi. Arti melewati argumen dengan referensi adalah bahwa nilai argumen tidak disalin, tetapi hanya alamat dari argumen sebenarnya yang dikirimkan. Jadi, ketika mengubah nilai argumen semacam itu, data dalam sel memori argumen saat ini juga akan berubah.
Ini juga mempengaruhi kinerja secara keseluruhan, karena melewati argumen dengan referensi lebih cepat daripada melewati nilai. Dan ini belum lagi koleksi besar elemen.

Misalnya, metode getParams di dalam akan mengubah argumen yang masuk dan mereka akan mengubah nilainya, termasuk dalam ruang lingkup, dari tempat ia dipanggil.
Pointer akan membantu kami menavigasi array. Dari teori struktur data, kita tahu bahwa array adalah wilayah memori yang kontinu yang unsur-unsurnya diatur satu demi satu.
Ini berarti bahwa jika Anda mengubah nilai pointer ke jumlah byte yang menempati elemen dalam array, Anda dapat mencapai setiap elemen hingga pointer melampaui batas array.
Buat pointer lain yang akan menunjuk ke elemen pertama dari array programmer .

 class Programmers{ public: Programmers(){} Programmers(int iWeight, int iAge){ this->weight = iWeight; this->age = iAge; } //    ,   main     void getParams(int &w, int &a){ w=weight; a=age; } private: int weight; int age; }; int main() { int size = 9; Programmers *prog [size]; Programmers *ptr=nullptr; for (int i =0;i<size;i++) { ptr=new Programmers(i+100,i); prog[i]=ptr; } int w,a; int count = 9; //    //        Programmers **iter = prog; for (int i=0;i<count;i++) { ptr = *iter++; ptr->getParams(w,a); if(*(iter-1) != nullptr){ delete *(iter-1); ptr = nullptr; } cout<<w<<"\t"<<a<<endl; } return 0; } 



Dalam contoh ini, saya ingin menyampaikan kepada Anda inti dari fakta bahwa ketika Anda mengubah nilai alamat penunjuk, Anda dapat mengakses area memori lain.

Struktur data seperti daftar, vektor, dll. berdasarkan pointer, dan karenanya disebut struktur data dinamis. Dan untuk mengulanginya lebih tepat menggunakan iterator. Iterator adalah penunjuk ke elemen struktur data dan menyediakan akses ke elemen kontainer.

Kesimpulannya


Setelah memahami topik petunjuk, bekerja dengan memori menjadi bagian pemrograman yang menyenangkan, dan secara keseluruhan pemahaman terperinci tentang bagaimana mesin bekerja dengan memori dan cara mengelolanya muncul. Dalam arti tertentu, ada filosofi di balik konsep "Bekerja dengan ingatan". Di ujung jari Anda, Anda mengubah muatan pada pelat kapasitor yang sangat kecil.

Source: https://habr.com/ru/post/id456318/


All Articles