Mengapa pointer tidak diinisialisasi dengan NULL secara default?


118

Bisakah seseorang menjelaskan mengapa pointer tidak diinisialisasi NULL?
Contoh:

  void test(){
     char *buf;
     if (!buf)
        // whatever
  }

Program tidak akan masuk ke dalam jika karena buf bukan null.

Saya ingin tahu mengapa, dalam hal apa kita memerlukan variabel dengan tempat sampah aktif, khususnya penunjuk yang menangani sampah di memori?


13
Nah, karena tipe fundamental dibiarkan tidak dimulai. Jadi saya berasumsi pertanyaan "sebenarnya" Anda: mengapa tipe fundamental tidak diinisialisasi?
GManNickG

11
"Programm tidak akan masuk ke dalam if karena buf bukan null". Itu tidak benar. Karena Anda tidak tahu apa buf adalah , Anda tidak bisa tahu apa itu tidak .
Drew Dormann

Berbeda dengan Java, C ++ memberikan lebih banyak tanggung jawab kepada pengembang.
Rishi

integers, pointer, default ke 0 jika Anda menggunakan () konstruktor.
Erik Aronesty

Karena asumsi bahwa seseorang yang menggunakan C ++ mengetahui apa yang mereka lakukan, terlebih lagi seseorang yang menggunakan pointer mentah di atas pointer pintar tahu (terlebih lagi) apa yang mereka lakukan!
Lofty Lion

Jawaban:


161

Kita semua menyadari bahwa pointer (dan jenis POD lainnya) harus diinisialisasi.
Pertanyaannya kemudian menjadi 'siapa yang harus menginisialisasi mereka'.

Pada dasarnya ada dua metode:

  • Kompilator menginisialisasi mereka.
  • Pengembang menginisialisasi mereka.

Mari kita asumsikan bahwa kompilator menginisialisasi variabel apa pun yang tidak diinisialisasi secara eksplisit oleh pengembang. Kemudian kami mengalami situasi di mana menginisialisasi variabel tidak sepele dan alasan pengembang tidak melakukannya pada titik deklarasi adalah dia perlu melakukan beberapa operasi dan kemudian menetapkan.

Jadi sekarang kita memiliki situasi dimana kompilator telah menambahkan instruksi tambahan ke kode yang menginisialisasi variabel ke NULL kemudian kode pengembang ditambahkan untuk melakukan inisialisasi yang benar. Atau dalam kondisi lain variabel tersebut berpotensi tidak pernah digunakan. Banyak pengembang C ++ akan menjerit di bawah kedua kondisi dengan mengorbankan instruksi tambahan itu.

Ini bukan hanya tentang waktu. Tapi juga ruang. Ada banyak lingkungan di mana kedua sumber daya memiliki harga premium dan pengembang juga tidak ingin menyerah.

TAPI : Anda dapat mensimulasikan efek dari memaksa inisialisasi. Sebagian besar kompiler akan memperingatkan Anda tentang variabel yang tidak diinisialisasi. Jadi saya selalu mengubah level peringatan saya ke level tertinggi. Kemudian beri tahu kompilator untuk memperlakukan semua peringatan sebagai kesalahan. Dalam kondisi ini, sebagian besar kompiler akan menghasilkan kesalahan untuk variabel yang tidak diinisialisasi tetapi digunakan dan dengan demikian akan mencegah pembuatan kode.


5
Bob Tabor berkata, “Terlalu banyak orang yang belum cukup memikirkan inisialisasi!” Ini 'ramah' untuk menginisialisasi semua variabel secara otomatis, tetapi butuh waktu, dan program yang lambat 'tidak bersahabat'. Spreadsheet atau editor yang menunjukkan malloc sampah acak yang ditemukan tidak dapat diterima. C, alat tajam untuk pengguna terlatih (berbahaya jika disalahgunakan) tidak perlu waktu menginisialisasi variabel otomatis. Makro roda pelatihan untuk variabel init bisa jadi, tetapi banyak yang berpikir lebih baik untuk berdiri, berhati-hati, dan sedikit berdarah. Dalam keadaan darurat, Anda bekerja dengan cara Anda berlatih. Jadi berlatihlah seperti yang Anda inginkan.
Bill IV

2
Anda akan terkejut melihat banyaknya bug yang dapat dihindari hanya dengan seseorang yang memperbaiki semua inisialisasi mereka. Ini akan menjadi pekerjaan yang membosankan jika bukan karena peringatan kompiler.
Jonathan Henson

4
@ Loki, saya mengalami kesulitan mengikuti maksud Anda. Saya hanya mencoba memuji jawaban Anda sebagai membantu, saya harap Anda mengumpulkannya. Jika tidak, saya minta maaf.
Jonathan Henson

3
Jika pointer pertama kali disetel ke NULL dan kemudian disetel ke nilai apa pun, kompiler harus dapat mendeteksi ini dan mengoptimalkan inisialisasi NULL pertama, bukan?
Korchkidu

1
@Coretan: Kadang-kadang. Salah satu masalah utama adalah bahwa tidak ada cara untuk memperingatkan Anda bahwa Anda lupa melakukan inisialisasi Anda, karena tidak dapat mengetahui default tidak sempurna untuk Anda gunakan.
Deduplicator

41

Mengutip Bjarne Stroustrup di TC ++ PL (Edisi Khusus p. 22):

Implementasi fitur tidak boleh membebankan biaya tambahan yang signifikan pada program yang tidak membutuhkannya.


dan jangan berikan pilihan. Tampaknya
Jonathan

8
@ Jonathan tidak ada yang mencegah Anda untuk menginisialisasi pointer ke null - atau ke 0 seperti standar di C ++.
stefanB

8
Ya, tapi Stroustrup bisa saja membuat sintaks default mendukung ketepatan program daripada kinerja dengan menginisialisasi pointer nol, dan membuat programmer harus secara eksplisit meminta pointer untuk tidak diinisialisasi. Lagi pula, kebanyakan orang lebih memilih benar-tetapi-lambat daripada cepat-tetapi-salah, dengan alasan bahwa umumnya lebih mudah untuk mengoptimalkan sejumlah kecil kode daripada memperbaiki bug di seluruh program. Terutama ketika sebagian besar dapat dilakukan oleh kompiler yang layak.
Robert Tuck

1
Itu tidak merusak kompatibilitas. Gagasan telah dipertimbangkan bersama dengan "int * x = __uninitialized" - keamanan secara default, kecepatan menurut maksud.
MSalters

4
Saya suka apa D. Jika Anda tidak ingin inisialisasi, gunakan sintaks ini float f = void;atau int* ptr = void;. Sekarang ini diinisialisasi secara default tetapi jika Anda benar-benar perlu, Anda dapat menghentikan kompilator untuk melakukannya.
deft_code

23

Karena inisialisasi membutuhkan waktu. Dan di C ++, hal pertama yang harus Anda lakukan dengan variabel apa pun adalah menginisialisasinya secara eksplisit:

int * p = & some_int;

atau:

int * p = 0;

atau:

class A {
   public:
     A() : p( 0 ) {}  // initialise via constructor
   private:
     int * p;
};

1
k, jika inisialisasi membutuhkan waktu dan saya masih menginginkannya, apakah ada cara untuk membuat pointer saya menjadi null tanpa mengaturnya secara manual? lihat, bukan karena saya tidak ingin memperbaikinya, karena sepertinya saya tidak akan pernah menggunakan pointer unitilia dengan sampah di alamat mereka
Jonathan

1
Anda menginisialisasi anggota kelas di konstruktor kelas - begitulah cara kerja C ++.

3
@ Jonathan: tapi null juga sampah. Anda tidak dapat melakukan apa pun yang berguna dengan pointer nol. Dereferensi satu sama lain adalah kesalahan. Buat pointer dengan nilai yang tepat, bukan null.
DrPizza

2
Menginisialisasi apointer ke Nnull bisa menjadi hal yang masuk akal untuk dilakukan. Dan ada beberapa operasi yang dapat Anda lakukan pada pointer nol - Anda dapat mengujinya dan Anda dapat memanggil delete pada mereka.

4
Jika Anda tidak akan pernah menggunakan pointer tanpa menginisialisasi secara eksplisit, tidak masalah apa yang ada di dalamnya sebelum Anda memberinya nilai, dan di bawah prinsip C dan C ++ membayar hanya untuk apa yang Anda gunakan, itu tidak dilakukan. secara otomatis. Jika ada nilai default yang dapat diterima (biasanya penunjuk nol), Anda harus menginisialisasinya. Anda dapat memulainya atau membiarkannya tidak dimulai, pilihan Anda.
David Thornley

20

Karena salah satu semboyan dari C ++ adalah:


Anda tidak membayar untuk apa yang tidak Anda gunakan


Untuk alasan ini, operator[]dari vectorkelas tidak memeriksa apakah indeks di luar batas, misalnya.


12

Untuk alasan sejarah, terutama karena ini adalah bagaimana hal itu dilakukan di C. Mengapa dilakukan seperti itu di C, adalah pertanyaan lain, tapi saya pikir prinsip overhead nol terlibat entah bagaimana dalam keputusan desain ini.


Saya kira karena C dianggap bahasa tingkat rendah dengan akses mudah ke memori (alias pointer) sehingga memberi Anda kebebasan melakukan apa yang Anda inginkan dan tidak membebankan biaya overhead dengan menginisialisasi semuanya. BTW Saya pikir itu tergantung pada platform karena saya bekerja pada platform seluler berbasis linux yang menginisialisasi semua memorinya ke 0 sebelum digunakan sehingga semua variabel akan disetel ke 0.
stefanB

8

Selain itu, kami memiliki peringatan ketika Anda meledakkannya: "mungkin digunakan sebelum diberi nilai" atau verbage serupa tergantung pada kompiler Anda.

Anda melakukan kompilasi dengan peringatan, bukan?


Dan itu hanya mungkin sebagai pengakuan bahwa penelusuran kompiler mungkin salah.
Deduplicator

6

Hanya ada sedikit situasi di mana masuk akal untuk variabel tidak diinisialisasi, dan inisialisasi default memiliki biaya yang kecil, jadi mengapa melakukannya?

C ++ bukan C89. Sial, bahkan C bukanlah C89. Anda dapat mencampur deklarasi dan kode, jadi Anda harus menunda deklarasi sampai Anda memiliki nilai yang sesuai untuk diinisialisasi.


2
Kemudian setiap nilai perlu ditulis dua kali - sekali oleh rutinitas penyiapan kompiler, dan sekali lagi oleh program pengguna. Biasanya tidak menjadi masalah besar, tetapi bertambah (misalnya jika Anda membuat array dari 1 juta item). Jika Anda menginginkan inisialisasi otomatis, Anda selalu dapat membuat jenis Anda sendiri yang melakukannya; tetapi dengan cara ini Anda tidak dipaksa untuk menerima pengeluaran tambahan yang tidak perlu jika Anda tidak mau.
Jeremy Friesner

3

Pointer hanyalah tipe lain. Jika Anda membuat int, charatau jenis POD lainnya, itu tidak diinisialisasi ke nol, jadi mengapa harus ada pointer? Ini bisa dianggap overhead yang tidak perlu bagi seseorang yang menulis program seperti ini.

char* pBuf;
if (condition)
{
    pBuf = new char[50];
}
else
{
    pBuf = m_myMember->buf();
}

Jika Anda tahu Anda akan memulainya, mengapa program harus mengeluarkan biaya saat Anda pertama kali membuat pBufdi bagian atas metode? Ini adalah prinsip overhead nol.


1
di sisi lain Anda bisa melakukan char * pBuf = condition? karakter baru [50]: m_myMember-> buf (); Ini lebih seperti masalah sintaksis daripada efisiensi tetapi saya tetap setuju dengan Anda.
the_drow

1
@ the_drow: Ya, seseorang dapat membuatnya lebih kompleks sehingga penulisan ulang seperti itu tidak mungkin dilakukan.
Deduplicator

2

Jika Anda menginginkan pointer yang selalu diinisialisasi ke NULL, Anda dapat menggunakan template C ++ untuk meniru fungsionalitas itu:

template<typename T> class InitializedPointer
{
public:
    typedef T       TObj;
    typedef TObj    *PObj;
protected:
    PObj        m_pPointer;

public:
    // Constructors / Destructor
    inline InitializedPointer() { m_pPointer=0; }
    inline InitializedPointer(PObj InPointer) { m_pPointer = InPointer; }
    inline InitializedPointer(const InitializedPointer& oCopy)
    { m_pPointer = oCopy.m_pPointer; }
    inline ~InitializedPointer() { m_pPointer=0; }

    inline PObj GetPointer() const  { return (m_pPointer); }
    inline void SetPointer(PObj InPtr)  { m_pPointer = InPtr; }

    // Operator Overloads
    inline InitializedPointer& operator = (PObj InPtr)
    { SetPointer(InPtr); return(*this); }
    inline InitializedPointer& operator = (const InitializedPointer& InPtr)
    { SetPointer(InPtr.m_pPointer); return(*this); }
    inline PObj operator ->() const { return (m_pPointer); }
    inline TObj &operator *() const { return (*m_pPointer); }

    inline bool operator!=(PObj pOther) const
    { return(m_pPointer!=pOther); }
    inline bool operator==(PObj pOther) const
    { return(m_pPointer==pOther); }
    inline bool operator!=(const InitializedPointer& InPtr) const
    { return(m_pPointer!=InPtr.m_pPointer); }
    inline bool operator==(const InitializedPointer& InPtr) const
    { return(m_pPointer==InPtr.m_pPointer); }

    inline bool operator<=(PObj pOther) const
    { return(m_pPointer<=pOther); }
    inline bool operator>=(PObj pOther) const
    { return(m_pPointer>=pOther); }
    inline bool operator<=(const InitializedPointer& InPtr) const
    { return(m_pPointer<=InPtr.m_pPointer); }
    inline bool operator>=(const InitializedPointer& InPtr) const
    { return(m_pPointer>=InPtr.m_pPointer); }

    inline bool operator<(PObj pOther) const
    { return(m_pPointer<pOther); }
    inline bool operator>(PObj pOther) const
    { return(m_pPointer>pOther); }
    inline bool operator<(const InitializedPointer& InPtr) const
    { return(m_pPointer<InPtr.m_pPointer); }
    inline bool operator>(const InitializedPointer& InPtr) const
    { return(m_pPointer>InPtr.m_pPointer); }
};

1
Jika saya menerapkan ini, saya tidak akan repot-repot dengan te copy ctor atau op penugasan - defaultnya cukup OK. Dan destruktor Anda tidak ada gunanya. Anda tentunya juga dapat menguji pointer menggunakan less than operater et all) dalam beberapa situasi) sehingga Anda harus menyediakannya.

OK, kurang dari yang sepele untuk diterapkan. Saya memiliki destruktor sehingga jika objek keluar dari ruang lingkup (yaitu lokal didefinisikan dalam subskop suatu fungsi) tetapi masih mengambil ruang di tumpukan, memori tidak dibiarkan sebagai penunjuk yang menggantung ke sampah. Tapi bung serius, saya menulis ini dalam waktu kurang dari 5 menit. Itu tidak dimaksudkan untuk menjadi sempurna.
Adisak

OK menambahkan semua operator perbandingan. Penimpaan default mungkin berlebihan tetapi ada di sini secara eksplisit karena ini adalah contoh.
Adisak

1
Saya tidak mengerti bagaimana ini akan membuat semua pointer null tanpa mengaturnya secara manual, bisakah Anda menjelaskan apa yang Anda lakukan di sini?
Jonathan

1
@Jonathan: Ini pada dasarnya adalah "penunjuk cerdas" yang tidak melakukan apa pun selain mengatur penunjuk ke null. IE alih-alih Foo *a, Anda menggunakan InitializedPointer<Foo> a- Latihan akademis murni karena Foo *a=0kurang mengetik. Namun kode di atas sangat berguna dari sudut pandang pendidikan. Dengan sedikit modifikasi (ke "placeholding" ctor / dtor dan operasi penugasan), itu dapat dengan mudah diperluas ke berbagai jenis smart pointer termasuk scoped-pointers (yang bebas pada destruktor) dan referensi-counted-pointer dengan menambahkan inc / dec operasi ketika m_pPointer diatur atau dihapus.
Adisak

2

Perhatikan bahwa data statis diinisialisasi ke 0 (kecuali Anda mengatakan sebaliknya).

Dan ya, Anda harus selalu mendeklarasikan variabel Anda selambat mungkin dan dengan nilai awal. Kode seperti

int j;
char *foo;

harus membunyikan bel alarm saat Anda membacanya. Saya tidak tahu apakah ada serat yang dapat dibujuk untuk mempermasalahkannya karena 100% legal.


apakah itu DIJAMIN, atau hanya praktik umum yang digunakan oleh penyusun saat ini?
gha.

1
variabel statis diinisialisasi ke 0, yang juga melakukan hal yang benar untuk pointer (misalnya, menyetelnya ke NULL, tidak semua bit 0). Perilaku ini dijamin oleh standar.
Alok Singhal

1
inisialisasi data statis ke nol dijamin oleh standar C dan C ++, ini bukan hanya praktik umum
groovingandi

1
mungkin karena beberapa orang ingin memastikan bahwa tumpukan mereka selaras dengan baik, mereka mendeklarasikan sebelumnya semua variabel di bagian atas fungsi? Mungkin mereka menulis dengan dialek ac yang MEMBUTUHKAN ini?
KitsuneYMG

1

Alasan lain yang mungkin mengapa, adalah bahwa pada penunjuk waktu tautan diberi alamat, tetapi pengalamatan tidak langsung / de-referensi penunjuk adalah tanggung jawab programmer. Biasanya, compiler tidak peduli, tetapi beban diteruskan ke programmer untuk mengelola pointer dan untuk memastikan tidak ada kebocoran memori yang terjadi.

Sungguh, singkatnya, mereka diinisialisasi dalam arti bahwa pada saat link, variabel pointer diberi alamat. Dalam kode contoh Anda di atas, yang dijamin akan crash atau menghasilkan SIGSEGV.

Demi kewarasan, selalu inisialisasi pointer ke NULL, dengan cara itu jika ada upaya untuk dereferensi tanpa mallocatau new akan memberi petunjuk kepada programmer tentang alasan mengapa program berperilaku salah.

Semoga ini membantu dan masuk akal,


0

Nah, jika C ++ melakukan inisialisasi pointer, maka orang-orang C yang mengeluh "C ++ lebih lambat dari C" akan memiliki sesuatu yang nyata untuk dipegang;)


Itu bukan alasan saya. Alasan saya adalah bahwa jika perangkat keras memiliki 512 byte ROM dan 128 byte RAM dan instruksi tambahan ke nol, pointer bahkan satu byte yang merupakan persentase yang cukup besar dari keseluruhan program. Saya butuh byte itu!
Jerry Jeremiah

0

C ++ berasal dari latar belakang C - dan ada beberapa alasan yang muncul kembali dari ini:

C, bahkan lebih dari C ++ adalah pengganti bahasa assembly. Ia tidak melakukan apa pun yang tidak Anda perintahkan. Oleh karena itu: Jika Anda ingin membatalkannya - lakukanlah!

Juga, jika Anda membatalkan sesuatu dalam bahasa bare-metal seperti C secara otomatis pertanyaan konsistensi muncul: Jika Anda malloc sesuatu - haruskah itu secara otomatis menjadi nol? Bagaimana dengan struct yang dibuat di stack? haruskah semua byte menjadi nol? Bagaimana dengan variabel global? bagaimana dengan pernyataan seperti "(* 0x18);" bukankah itu berarti bahwa posisi memori 0x18 harus menjadi nol?


Sebenarnya, di C, jika Anda ingin mengalokasikan semua memori nol, Anda dapat menggunakan calloc().
David Thornley

1
hanya maksud saya - jika Anda ingin melakukannya, Anda bisa, tetapi itu tidak dilakukan untuk Anda secara otomatis
gha.st

0

Apa petunjuk yang Anda bicarakan ini?

Untuk keamanan pengecualian, selalu menggunakan auto_ptr, shared_ptr, weak_ptrdan varian mereka yang lain.
Ciri khas kode yang baik adalah yang tidak menyertakan satu panggilan pun delete.


3
Sejak C ++ 11, hindari auto_ptrdan gantikan unique_ptr.
Deduplicator

-2

Oh Boy. Jawaban sebenarnya adalah mudah untuk menghilangkan memori, yang merupakan inisialisasi dasar untuk, katakanlah sebuah pointer. Yang juga tidak ada hubungannya dengan menginisialisasi objek itu sendiri.

Mempertimbangkan peringatan yang diberikan sebagian besar kompiler pada level tertinggi, saya tidak dapat membayangkan pemrograman pada level tertinggi dan memperlakukannya sebagai kesalahan. Sejak menyalakannya tidak pernah menyelamatkan saya bahkan satu bug dalam jumlah besar kode yang dihasilkan, saya tidak dapat merekomendasikan ini.


Jika pointer tidak diharapkan untuk menjadi NULL, menginisialisasi untuk yang hanya sebanyak kesalahan.
Deduplicator
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.