Bagikan melalui


Pointer mentah (C++)

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++