const char * const versus const char *?

110

Saya menjalankan beberapa program contoh untuk membiasakan diri dengan C ++ dan saya mengalami pertanyaan berikut. Pertama, berikut ini contoh kode:

void print_string(const char * the_string)
{
    cout << the_string << endl;
}

int main () {
    print_string("What's up?");
}

Pada kode di atas, parameter ke print_string bisa saja const char * const the_string. Mana yang lebih tepat untuk ini?

Saya memahami bahwa perbedaannya adalah bahwa yang satu adalah penunjuk ke karakter konstan, sedangkan yang lainnya adalah penunjuk konstan ke karakter konstan. Tapi mengapa keduanya berhasil? Kapan itu relevan?

gambar
sumber

Jawaban:

244

Yang terakhir mencegah Anda memodifikasi the_stringbagian dalam print_string. Ini sebenarnya akan sesuai di sini, tetapi mungkin verbositas membuat pengembang enggan.

char* the_string: Saya dapat mengubah titik charmana the_string, dan saya dapat memodifikasi titik charmana.

const char* the_string: Saya dapat mengubah poin charmana the_string, tetapi saya tidak dapat mengubah charpoin mana.

char* const the_string: Saya tidak bisa mengubah poin charmana the_string, tapi saya bisa memodifikasi charpoin mana.

const char* const the_string: Saya tidak dapat mengubah poin charyang mana the_string, dan saya juga tidak dapat mengubah charpoin yang mana.

Kent Boogaart
sumber
11
1 untuk kalimat terakhir. Const-correctness memang bertele-tele, tetapi sangat berharga.
mskfisher
6
@ Xeo: formulir Anda bahkan lebih membingungkan karena itu satu transposisi dari mengubah artinya sepenuhnya. const char *jauh lebih baik karena constberada di sisi yang berlawanan.
R .. GitHub STOP HELPING ICE
7
@R ..: Yah, setidaknya bagi saya tidak. Membaca dari kanan ke kiri, saya mendapatkan "pointer ke const char". Bagi saya, rasanya lebih baik seperti itu.
Xeo
6
Anda membodohi diri sendiri karena tipe C dibaca dari dalam ke luar, bukan dari kiri ke kanan. :-)
R .. GitHub STOP HELPING ICE
11
Saya sedikit sebuah malu aku ternyata satu-satunya yang tidak memahami hal ini ... tapi apa perbedaan antara "char untuk yang menunjuk" dan "char di mana ia menunjuk"?
Kurangnya
138
  1. Pointer yang bisa diubah ke karakter yang bisa berubah

    char *p;
  2. Pointer yang dapat diubah ke karakter konstan

    const char *p;
  3. Penunjuk konstan ke karakter yang bisa berubah

    char * const p; 
  4. Penunjuk konstan ke karakter konstan

    const char * const p;
James Michael Hare
sumber
Bukankah seharusnya ini: const char* p; --> constant pointer to mutable characterdan char *const p; --> mutable pointer to constant character
PnotNP
2
@NulledPointer Tidak. Deklarasi C ++ dibentuk dari kanan ke kiri. Dengan demikian, Anda dapat membaca const char * psebagai: "p adalah penunjuk ke konstanta karakter" atau penunjuk yang dapat berubah ke karakter konstan seperti yang dinyatakan James dengan benar. sama dengan yang kedua :).
Samidamaru
28

const char * constberarti penunjuk serta data yang ditunjuk penunjuk, keduanya adalah const!

const char *hanya berarti data yang ditunjuk penunjuk, adalah const. Namun pointer itu sendiri bukanlah const.

Contoh.

const char *p = "Nawaz";
p[2] = 'S'; //error, changing the const data!
p="Sarfaraz"; //okay, changing the non-const pointer. 

const char * const p = "Nawaz";
p[2] = 'S'; //error, changing the const data!
p="Sarfaraz"; //error, changing the const pointer. 
Nawaz
sumber
20

(Saya tahu ini sudah tua tetapi saya tetap ingin berbagi.)

Hanya ingin menguraikan jawaban Thomas Matthews. Deklarasi Tipe C Right-Left Rule cukup banyak mengatakan: saat membaca deklarasi tipe C mulai dari pengenal dan ke kanan saat Anda bisa dan pergi saat Anda tidak bisa.

Ini paling baik dijelaskan dengan beberapa contoh:

Contoh 1

  • Mulai dari pengenal, kita tidak bisa ke kanan jadi kita ke kiri

    const char* const foo
                ^^^^^

    foo adalah sebuah konstanta ...

  • Lanjutkan ke kiri

    const char* const foo
              ^

    foo adalah penunjuk konstan ke ...

  • Lanjutkan ke kiri

    const char* const foo
          ^^^^

    foo adalah pointer konstan untuk char di ...

  • Lanjutkan ke kiri

    const char* const foo
    ^^^^^

    foo adalah penunjuk konstan ke konstanta char (Lengkap!)

Contoh 2

  • Mulai dari pengenal, kita tidak bisa ke kanan jadi kita ke kiri

    char* const foo
          ^^^^^

    foo adalah sebuah konstanta ...

  • Lanjutkan ke kiri

    char* const foo
        ^

    foo adalah penunjuk konstan ke ...

  • Lanjutkan ke kiri

    char* const foo
    ^^^^

    foo adalah penunjuk konstan ke char (Selesai!)

Contoh 1337

  • Mulailah dari pengenal, tapi sekarang kita bisa melakukannya dengan benar!

    const char* const* (*foo[8])()
                            ^^^

    foo adalah array 8 ...

  • Tekan tanda kurung jadi tidak bisa ke kanan lagi, ke kiri

    const char* const* (*foo[8])()
                        ^

    foo adalah larik 8 penunjuk ke ...

  • Selesai di dalam kurung, sekarang bisa ke kanan

    const char* const* (*foo[8])()
                                ^^

    foo adalah larik 8 penunjuk ke fungsi yang mengembalikan ...

  • Tidak lebih ke kanan, ke kiri

    const char* const* (*foo[8])()
                     ^

    foo adalah larik 8 penunjuk ke fungsi yang mengembalikan satu penunjuk ke ...

  • Lanjutkan ke kiri

    const char* const* (*foo[8])()
                ^^^^^

    foo adalah larik berisi 8 penunjuk ke fungsi yang mengembalikan satu penunjuk ke a konstanta ...

  • Lanjutkan ke kiri

    const char* const* (*foo[8])()
              ^

    foo adalah larik yang terdiri dari 8 penunjuk ke fungsi yang mengembalikan satu penunjuk ke konstanta penunjuk ke ...

  • Lanjutkan ke kiri

    const char* const* (*foo[8])()
          ^^^^

    foo adalah larik yang terdiri dari 8 penunjuk ke fungsi yang mengembalikan satu penunjuk ke penunjuk konstan ke a Char ...

  • Lanjutkan ke kiri

    const char* const* (*foo[8])()
    ^^^^^

    foo adalah larik berisi 8 penunjuk ke fungsi yang mengembalikan satu penunjuk ke penunjuk konstan ke karakter konstanta (Lengkap!)

Penjelasan lebih lanjut: http://www.unixwiz.net/techtips/reading-cdecl.html

Garrett
sumber
CMIIW, const char * const foo harus setara dengan char const * const foo?
luochenhuan
@luochenhuan Ya, memang benar.
Garrett
12

Banyak orang menyarankan membaca penentu tipe dari kanan ke kiri.

const char * // Pointer to a `char` that is constant, it can't be changed.
const char * const // A const pointer to const data.

Dalam kedua bentuk, penunjuk menunjuk ke data konstan atau hanya-baca.

Dalam bentuk kedua, penunjuk tidak dapat diubah; penunjuk akan selalu menunjuk ke tempat yang sama.

Thomas Matthews
sumber
3

Perbedaannya adalah bahwa tanpa tambahan constprogrammer dapat mengubah, di dalam metode, di mana pointer menunjuk ke; sebagai contoh:

 void print_string(const char * the_string)
 {
    cout << the_string << endl;
    //....
    the_string = another_string();
    //....

 }

Itu malah ilegal jika tanda tangan itu void print_string(const char * const the_string)

Banyak programmer merasa terlalu bertele-tele (dalam kebanyakan skenario) constkata kunci tambahan dan mengabaikannya, meskipun secara semantik kata itu benar.

leonbloy.dll
sumber
2

Yang terakhir, Anda menjamin untuk tidak mengubah penunjuk dan karakter terlebih dahulu, Anda hanya menjamin bahwa isinya tidak akan berubah tetapi Anda dapat memindahkan penunjuk.

Yesus Ramos
sumber
Ahh jadi tanpa const terakhir, saya benar-benar dapat mengatur pointer untuk menunjuk ke string yang sama sekali berbeda?
gambar
Ya, tanpa konstanta terakhir itu Anda dapat menggunakan penunjuk parameter untuk melakukan beberapa iterasi dengan aritmatika penunjuk, di mana jika ada konstanta itu Anda harus membuat penunjuk Anda sendiri yang merupakan salinan dari parameter itu.
Jesus Ramos
2

Tidak ada alasan mengapa salah satu tidak berhasil. Yang print_string()dilakukan hanyalah mencetak nilainya. Itu tidak mencoba untuk memodifikasinya.

Sebaiknya buat fungsi yang tidak mengubah argumen mark sebagai const. Keuntungannya adalah variabel yang tidak dapat diubah (atau Anda tidak ingin diubah) dapat diteruskan ke fungsi ini tanpa kesalahan.

Sejauh sintaks yang tepat, Anda ingin menunjukkan tipe argumen mana yang "aman" untuk diteruskan ke fungsi.

Jonathan Wood
sumber
2

Saya pikir ini jarang berbeda relevan, karena fungsi Anda tidak dipanggil dengan argumen seperti & * the_string atau ** the_string. Pointer itu sendiri adalah argumen tipe-nilai, jadi meskipun Anda memodifikasinya, Anda tidak akan mengubah salinan yang digunakan untuk memanggil fungsi Anda. Versi yang Anda tunjukkan memastikan bahwa string tidak akan berubah, dan saya pikir itu cukup dalam kasus ini.

Eglin
sumber
2

const char *berarti Anda tidak dapat menggunakan penunjuk untuk mengubah apa yang diarahkan. Anda dapat mengubah penunjuk untuk menunjuk ke sesuatu yang lain.

Mempertimbangkan:

const char * promptTextWithDefault(const char * text)
{
    if ((text == NULL) || (*text == '\0'))
        text = "C>";
    return text;
}

Parameternya adalah penunjuk non-const ke char const, sehingga dapat diubah ke const char *nilai lain (seperti string konstan). Namun, jika kami salah menulis*text = '\0' maka kami akan mendapatkan kesalahan kompilasi.

Bisa dibilang, jika Anda tidak berniat mengubah tujuan parameter, Anda bisa membuat parameter const char * const text, tetapi tidak umum melakukannya. Kami biasanya mengizinkan fungsi untuk mengubah nilai yang diteruskan ke parameter (karena kami melewatkan parameter dengan nilai, setiap perubahan tidak mempengaruhi pemanggil).

BTW: Ini adalah praktik yang baik untuk dihindari char const *karena sering salah dibaca - artinya sama dengan const char *, tetapi terlalu banyak orang yang membacanya sebagai makna char * const.

TonyR
sumber
Wow! Saya mencoba untuk mencari tahu perbedaan antara saya const char *dan tanda tangan char const *- cara Anda mengucapkan BTW sangat membantu!
sage
1

Hampir semua jawaban lain benar, tetapi salah satu aspeknya terlewat: Saat Anda menggunakan ekstra constpada parameter dalam deklarasi fungsi, compiler pada dasarnya akan mengabaikannya. Untuk sesaat, mari kita abaikan kompleksitas contoh Anda sebagai penunjuk dan cukup gunakan int.

void foo(const int x);

mendeklarasikan fungsi yang sama seperti

void foo(int x);

Hanya dalam definisi fungsinya adalah ekstra constbermakna:

void foo(const int x) {
    // do something with x here, but you cannot change it
}

Definisi ini kompatibel dengan salah satu deklarasi di atas. Penelepon tidak peduli bahwa xadalahconst -yang 's detail implementasi yang tidak relevan di situs panggilan.

Jika Anda memiliki constpenunjuk ke constdata, aturan yang sama berlaku:

// these declarations are equivalent
void print_string(const char * const the_string);
void print_string(const char * the_string);

// In this definition, you cannot change the value of the pointer within the
// body of the function.  It's essentially a const local variable.
void print_string(const char * const the_string) {
    cout << the_string << endl;
    the_string = nullptr;  // COMPILER ERROR HERE
}

// In this definition, you can change the value of the pointer (but you 
// still can't change the data it's pointed to).  And even if you change
// the_string, that has no effect outside this function.
void print_string(const char * the_string) {
    cout << the_string << endl;
    the_string = nullptr;  // OK, but not observable outside this func
}

Beberapa programmer C ++ repot-repot membuat parameter const, bahkan ketika mereka bisa, terlepas dari apakah parameter tersebut adalah pointer.

Adrian McCarthy
sumber
"compiler pada dasarnya akan mengabaikannya" tidak selalu benar, Visual C ++ 2015 akan menghasilkan peringatan jika Anda menambahkan ekstra constke parameter fungsi Anda dalam definisi tetapi bukan deklarasi.
raymai97
@ raymai97: Saya pikir itu bug di kompiler 2015, tapi saya tidak punya 2015 berguna untuk diuji. Saya bekerja seperti yang saya jelaskan pada tahun 2017, yang menurut percakapan saya dengan beberapa pakar standar, adalah perilaku yang diharapkan.
Adrian McCarthy
-1

Perbedaan di antara keduanya adalah bahwa char * dapat mengarah ke sembarang penunjuk. Sebaliknya, karakter konstanta * menunjuk ke konstanta yang ditentukan di bagian DATA yang dapat dieksekusi. Dan, dengan demikian, Anda tidak dapat mengubah nilai karakter dari string * char *.

Maz
sumber
Saya tidak menanyakan perbedaan antara char * dan const char *. Saya bertanya antara const char * dan const char * const
gambar
Jawaban ini salah. A const char*dapat menunjukkan kemana saja yang diinginkan.
Kubik