Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Penunjuk adalah jenis variabel. Ini menyimpan alamat objek dalam memori, dan digunakan untuk mengakses objek tersebut. Pointer mentah adalah pointer yang masa pakainya tidak dikontrol oleh objek enkapsulasi, seperti penunjuk cerdas. Pointer mentah dapat ditetapkan alamat variabel non-pointer lain, atau dapat diberi nilai nullptr
. Penunjuk yang belum diberi nilai berisi data acak.
Pointer juga dapat didereferensikan untuk mengambil nilai objek yang ditunjukkannya. Operator akses anggota menyediakan akses ke anggota objek.
int* p = nullptr; // declare pointer and initialize it
// so that it doesn't store a random address
int i = 5;
p = &i; // assign pointer to address of object
int j = *p; // dereference p to retrieve the value at its address
Penunjuk dapat menunjuk ke objek yang ditik atau ke void
. Ketika program mengalokasikan objek pada timbunan dalam memori, program menerima alamat objek tersebut dalam bentuk penunjuk. Pointer seperti itu disebut memiliki pointer. Penunjuk pemilik (atau salinannya) harus digunakan untuk secara eksplisit membebaskan objek yang dialokasikan timbunan saat tidak lagi diperlukan. Kegagalan untuk membebaskan memori menghasilkan kebocoran memori, dan merender bahwa lokasi memori tidak tersedia untuk program lain pada komputer. Memori yang dialokasikan menggunakan new
harus dibebaskan dengan menggunakan delete
(atau delete[]
). Untuk informasi selengkapnya, lihat new
dan delete
operator.
MyClass* mc = new MyClass(); // allocate object on the heap
mc->print(); // access class member
delete mc; // delete object (please don't forget!)
Penunjuk (jika tidak dinyatakan sebagai const
) dapat dinaikkan atau didekorasi untuk menunjuk ke lokasi lain dalam memori. Operasi ini disebut aritmatika penunjuk. Ini digunakan dalam pemrograman gaya C untuk melakukan iterasi pada elemen dalam array atau struktur data lainnya. const
Penunjuk tidak dapat dibuat untuk menunjuk ke lokasi memori yang berbeda, dan dalam hal ini mirip dengan referensi. Untuk informasi selengkapnya, lihat const
dan volatile
penunjuk.
// declare a C-style string. Compiler adds terminating '\0'.
const char* str = "Hello world";
const int c = 1;
const int* pconst = &c; // declare a non-const pointer to const int
const int c2 = 2;
pconst = &c2; // OK pconst itself isn't const
const int* const pconst2 = &c;
// pconst2 = &c2; // Error! pconst2 is const.
Pada sistem operasi 64-bit, pointer memiliki ukuran 64 bit. Ukuran penunjuk sistem menentukan berapa banyak memori yang dapat diatasi. Semua salinan titik penunjuk ke lokasi memori yang sama. Pointer (bersama dengan referensi) digunakan secara ekstensif di C++ untuk meneruskan objek yang lebih besar ke dan dari fungsi. Seringkali lebih efisien untuk menyalin alamat objek daripada menyalin seluruh objek. Saat menentukan fungsi, tentukan parameter penunjuk sebagai const
kecuali Anda berniat untuk memodifikasi objek. Secara umum, const
referensi adalah cara yang lebih disukai untuk meneruskan objek ke fungsi kecuali nilai objek mungkin bisa .nullptr
Penunjuk ke fungsi memungkinkan fungsi diteruskan ke fungsi lain. Mereka digunakan untuk "panggilan balik" dalam pemrograman gaya C. C++ modern menggunakan ekspresi lambda untuk tujuan ini.
Inisialisasi dan akses anggota
Contoh berikut menunjukkan cara mendeklarasikan, menginisialisasi, dan menggunakan pointer mentah. Ini diinisialisasi menggunakan new
untuk mengarahkan objek yang dialokasikan pada timbunan, yang harus Anda eksplisit delete
. Contohnya juga menunjukkan beberapa bahaya yang terkait dengan pointer mentah. (Ingat, contoh ini adalah pemrograman gaya C dan bukan C++!)
#include <iostream>
#include <string>
class MyClass
{
public:
int num;
std::string name;
void print() { std::cout << name << ":" << num << std::endl; }
};
// Accepts a MyClass pointer
void func_A(MyClass* mc)
{
// Modify the object that mc points to.
// All copies of the pointer will point to
// the same modified object.
mc->num = 3;
}
// Accepts a MyClass object
void func_B(MyClass mc)
{
// mc here is a regular object, not a pointer.
// Use the "." operator to access members.
// This statement modifies only the local copy of mc.
mc.num = 21;
std::cout << "Local copy of mc:";
mc.print(); // "Erika, 21"
}
int main()
{
// Use the * operator to declare a pointer type
// Use new to allocate and initialize memory
MyClass* pmc = new MyClass{ 108, "Nick" };
// Prints the memory address. Usually not what you want.
std:: cout << pmc << std::endl;
// Copy the pointed-to object by dereferencing the pointer
// to access the contents of the memory location.
// mc is a separate object, allocated here on the stack
MyClass mc = *pmc;
// Declare a pointer that points to mc using the addressof operator
MyClass* pcopy = &mc;
// Use the -> operator to access the object's public members
pmc->print(); // "Nick, 108"
// Copy the pointer. Now pmc and pmc2 point to same object!
MyClass* pmc2 = pmc;
// Use copied pointer to modify the original object
pmc2->name = "Erika";
pmc->print(); // "Erika, 108"
pmc2->print(); // "Erika, 108"
// Pass the pointer to a function.
func_A(pmc);
pmc->print(); // "Erika, 3"
pmc2->print(); // "Erika, 3"
// Dereference the pointer and pass a copy
// of the pointed-to object to a function
func_B(*pmc);
pmc->print(); // "Erika, 3" (original not modified by function)
delete(pmc); // don't forget to give memory back to operating system!
// delete(pmc2); //crash! memory location was already deleted
}
Aritmatika dan array penunjuk
Pointer dan array terkait erat. Saat array diteruskan berdasarkan nilai ke fungsi, array diteruskan sebagai penunjuk ke elemen pertama. Contoh berikut menunjukkan properti penting pointer dan array berikut:
- Operator
sizeof
mengembalikan ukuran total dalam byte array - Untuk menentukan jumlah elemen, bagi total byte dengan ukuran satu elemen
- Ketika array diteruskan ke fungsi, array membusuk ke jenis penunjuk
sizeof
Saat operator diterapkan ke penunjuk, operator mengembalikan ukuran pointer, misalnya, 4 byte pada x86 atau 8 byte pada x64
#include <iostream>
void func(int arr[], int length)
{
// returns pointer size. not useful here.
size_t test = sizeof(arr);
for(int i = 0; i < length; ++i)
{
std::cout << arr[i] << " ";
}
}
int main()
{
int i[5]{ 1,2,3,4,5 };
// sizeof(i) = total bytes
int j = sizeof(i) / sizeof(i[0]);
func(i,j);
}
Operasi aritmatika tertentu dapat digunakan pada non-pointerconst
untuk membuatnya menunjuk ke lokasi memori lain. Pointer dinaikkan dan direkrementasi menggunakan ++
operator , , -=
+=
dan --
. Teknik ini dapat digunakan dalam array dan sangat berguna dalam buffer data yang tidak diketik. Akan void*
bertambah bertahas dengan ukuran char
(1 byte). Penunjuk yang ditik akan bertambah berdasarkan ukuran jenis yang ditunjukkannya.
Contoh berikut menunjukkan bagaimana aritmatika penunjuk dapat digunakan untuk mengakses piksel individual dalam bitmap di Windows. Perhatikan penggunaan new
dan delete
, dan operator dereferensi.
#include <Windows.h>
#include <fstream>
using namespace std;
int main()
{
BITMAPINFOHEADER header;
header.biHeight = 100; // Multiple of 4 for simplicity.
header.biWidth = 100;
header.biBitCount = 24;
header.biPlanes = 1;
header.biCompression = BI_RGB;
header.biSize = sizeof(BITMAPINFOHEADER);
constexpr int bufferSize = 30000;
unsigned char* buffer = new unsigned char[bufferSize];
BITMAPFILEHEADER bf;
bf.bfType = 0x4D42;
bf.bfSize = header.biSize + 14 + bufferSize;
bf.bfReserved1 = 0;
bf.bfReserved2 = 0;
bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); //54
// Create a gray square with a 2-pixel wide outline.
unsigned char* begin = &buffer[0];
unsigned char* end = &buffer[0] + bufferSize;
unsigned char* p = begin;
constexpr int pixelWidth = 3;
constexpr int borderWidth = 2;
while (p < end)
{
// Is top or bottom edge?
if ((p < begin + header.biWidth * pixelWidth * borderWidth)
|| (p > end - header.biWidth * pixelWidth * borderWidth)
// Is left or right edge?
|| (p - begin) % (header.biWidth * pixelWidth) < (borderWidth * pixelWidth)
|| (p - begin) % (header.biWidth * pixelWidth) > ((header.biWidth - borderWidth) * pixelWidth))
{
*p = 0x0; // Black
}
else
{
*p = 0xC3; // Gray
}
p++; // Increment one byte sizeof(unsigned char).
}
ofstream wf(R"(box.bmp)", ios::out | ios::binary);
wf.write(reinterpret_cast<char*>(&bf), sizeof(bf));
wf.write(reinterpret_cast<char*>(&header), sizeof(header));
wf.write(reinterpret_cast<char*>(begin), bufferSize);
delete[] buffer; // Return memory to the OS.
wf.close();
}
void*
Petunjuk
Pointer untuk void
hanya menunjuk ke lokasi memori mentah. Terkadang perlu menggunakan void*
pointer, misalnya saat meneruskan antara kode C++ dan fungsi C.
Saat penunjuk yang diketik ditransmisikan ke void
penunjuk, konten lokasi memori tidak berubah. Namun, informasi jenis hilang, sehingga Anda tidak dapat melakukan operasi kenaikan atau penurunan. Lokasi memori dapat dilemparkan, misalnya, dari MyClass*
ke void*
dan kembali lagi ke MyClass*
. Operasi tersebut secara inheren rawan kesalahan dan memerlukan perhatian besar untuk menghindari kesalahan. C++ modern mencegah penggunaan void
pointer dalam hampir semua keadaan.
//func.c
void func(void* data, int length)
{
char* c = (char*)(data);
// fill in the buffer with data
for (int i = 0; i < length; ++i)
{
*c = 0x41;
++c;
}
}
// main.cpp
#include <iostream>
extern "C"
{
void func(void* data, int length);
}
class MyClass
{
public:
int num;
std::string name;
void print() { std::cout << name << ":" << num << std::endl; }
};
int main()
{
MyClass* mc = new MyClass{10, "Marian"};
void* p = static_cast<void*>(mc);
MyClass* mc2 = static_cast<MyClass*>(p);
std::cout << mc2->name << std::endl; // "Marian"
delete(mc);
// use operator new to allocate untyped memory block
void* pvoid = operator new(1000);
char* pchar = static_cast<char*>(pvoid);
for(char* c = pchar; c < pchar + 1000; ++c)
{
*c = 0x00;
}
func(pvoid, 1000);
char ch = static_cast<char*>(pvoid)[0];
std::cout << ch << std::endl; // 'A'
operator delete(pvoid);
}
Penunjuk ke fungsi
Dalam pemrograman gaya C, penunjuk fungsi digunakan terutama untuk meneruskan fungsi ke fungsi lain. Teknik ini memungkinkan pemanggil untuk menyesuaikan perilaku fungsi tanpa memodifikasinya. Dalam C++modern, ekspresi lambda memberikan kemampuan yang sama dengan keamanan jenis yang lebih besar dan keuntungan lainnya.
Deklarasi penunjuk fungsi menentukan tanda tangan yang harus dimiliki fungsi pointed-to:
// Declare pointer to any function that...
// ...accepts a string and returns a string
string (*g)(string a);
// has no return value and no parameters
void (*x)();
// ...returns an int and takes three parameters
// of the specified types
int (*i)(int i, string s, double d);
Contoh berikut menunjukkan fungsi combine
yang mengambil sebagai parameter fungsi apa pun yang menerima std::string
dan mengembalikan std::string
. Bergantung pada fungsi yang diteruskan ke combine
, fungsi tersebut menambahkan atau menambahkan string.
#include <iostream>
#include <string>
using namespace std;
string base {"hello world"};
string append(string s)
{
return base.append(" ").append(s);
}
string prepend(string s)
{
return s.append(" ").append(base);
}
string combine(string s, string(*g)(string a))
{
return (*g)(s);
}
int main()
{
cout << combine("from MSVC", append) << "\n";
cout << combine("Good morning and", prepend) << "\n";
}
Lihat juga
Operator Tidak Langsung PenunjukCerdas: *
Alamat Operator: &
Selamat datang kembali ke C++