Mengapa sebagian besar variabel pemrogram nama C seperti ini:
int *myVariable;
daripada seperti ini:
int* myVariable;
Keduanya valid. Sepertinya saya bahwa tanda bintang adalah bagian dari jenisnya, bukan bagian dari nama variabel. Adakah yang bisa menjelaskan logika ini?
c
pointers
variables
naming-conventions
WBlasko
sumber
sumber
typedefs
, tetapi itu akan menambah kompleksitas yang tidak perlu, IMHO.Jawaban:
Mereka sama persis. Namun, dalam
Tampaknya jelas bahwa myVariable memiliki tipe int * , sedangkan myVariable2 memiliki tipe int . Di
mungkin tampak jelas bahwa keduanya bertipe int * , tetapi itu tidak benar karena
myVariable2
memiliki tipe int .Oleh karena itu, gaya pemrograman pertama lebih intuitif.
sumber
int* someVar
untuk proyek pribadi. Itu lebih masuk akal.int[10] x
. Ini bukan sintaks C. Tata bahasanya secara eksplisit diuraikan sebagai:,int (*x)
dan bukan sebagai(int *) x
, sehingga menempatkan tanda bintang di sebelah kiri hanya menyesatkan dan didasarkan pada kesalahpahaman sintaksis deklarasi C.int* myVariable; int myVariable2;
.Jika Anda melihatnya dengan cara lain,
*myVariable
adalah tipeint
, yang masuk akal.sumber
myVariable
bisa NULL, dalam hal ini*myVariable
menyebabkan kesalahan segmentasi, tetapi tidak ada tipe NULL.int x = 5; int *pointer = &x;
karena itu menyarankan kita mengatur int*pointer
ke beberapa nilai, bukan nilaipointer
itu sendiri.Karena * pada baris itu mengikat lebih dekat ke variabel daripada ke tipe:
Seperti yang ditunjukkan @Lundin di bawah ini, const menambahkan lebih banyak lagi kehalusan untuk dipikirkan. Anda dapat sepenuhnya menghindarinya dengan mendeklarasikan satu variabel per baris, yang tidak pernah ambigu:
Keseimbangan antara kode yang jelas dan kode yang ringkas sulit dipukul - selusin baris yang berlebihan
int a;
juga tidak bagus. Namun, saya default ke satu deklarasi per baris dan khawatir tentang menggabungkan kode nanti.sumber
int *const a, b;
. Di mana * "mengikat"? Tipea
isint* const
, jadi bagaimana Anda bisa mengatakan bahwa * milik variabel ketika itu adalah bagian dari tipe itu sendiri?Sesuatu yang belum pernah disebutkan di sini sejauh ini adalah tanda bintang ini sebenarnya adalah " operator dereferensi " di C.
Baris di atas bukan berarti saya ingin menetapkan
10
kea
, itu berarti saya ingin menetapkan10
apa pun lokasi memoria
poin ke. Dan saya belum pernah melihat orang menulisapakah kamu Jadi operator dereference hampir selalu ditulis tanpa spasi. Ini mungkin untuk membedakannya dari perkalian yang terputus di beberapa baris:
Ini
*e
akan menyesatkan, bukan?Oke, sekarang apa arti sebenarnya dari baris berikut:
Kebanyakan orang akan mengatakan:
Ini berarti bahwa
a
adalah penunjuk keint
nilai.Ini secara teknis benar, kebanyakan orang suka melihat / membacanya seperti itu dan itu adalah cara bagaimana standar C modern akan mendefinisikannya (perhatikan bahwa bahasa C sendiri mendahului semua standar ANSI dan ISO). Tapi itu bukan satu-satunya cara untuk melihatnya. Anda juga dapat membaca baris ini sebagai berikut:
Nilai dereferensi
a
dari tipeint
.Jadi sebenarnya tanda bintang dalam deklarasi ini juga dapat dilihat sebagai operator dereference, yang juga menjelaskan penempatannya. Dan itu
a
adalah pointer tidak benar-benar dideklarasikan sama sekali, itu tersirat oleh fakta, bahwa satu-satunya hal yang Anda benar-benar dapat dereferensi adalah pointer.Standar C hanya mendefinisikan dua arti bagi
*
operator:Dan tipuan hanya satu makna tunggal, tidak ada makna tambahan untuk mendeklarasikan pointer, hanya ada tipuan, yang dilakukan oleh operasi dereference, ia melakukan akses tidak langsung, demikian juga dalam pernyataan seperti
int *a;
ini adalah akses tidak langsung (*
berarti akses tidak langsung) dan dengan demikian pernyataan kedua di atas jauh lebih dekat dengan standar daripada yang pertama.sumber
int a, *b, (*c)();
sebagai sesuatu seperti "menyatakan objek-objek berikut sebagaiint
: objeka
, objek menunjuk keb
, dan objek kembali dari fungsi yang ditunjukkan olehc
".*
dalamint *a;
tidak operator, dan tidak dereferencinga
(yang bahkan tidak didefinisikan belum)*
(itu hanya memberikan makna terhadap total ekspresi, tidak ke*
dalam ekspresi!). Dikatakan bahwa "int a;" mendeklarasikan pointer, yang ia lakukan, tidak pernah mengklaim sebaliknya, tetapi tanpa makna yang diberikan*
, membacanya sebagai * nilai dereferenced daria
int masih benar-benar valid, karena itu memiliki arti faktual yang sama. Tidak ada, benar-benar tidak ada yang tertulis dalam 6.7.6.1 yang akan bertentangan dengan pernyataan itu.int *a;
adalah deklarasi, bukan ekspresi.a
tidak direferensikan olehint *a;
.a
bahkan belum ada pada saat*
ini sedang diproses. Apakah Anda menganggapnyaint *a = NULL;
sebagai bug karena referensi nol?Aku akan pergi mengambil risiko di sini dan mengatakan bahwa ada jawaban langsung untuk pertanyaan ini , baik untuk deklarasi variabel dan untuk parameter dan kembali jenis, yaitu bahwa tanda bintang harus pergi di sebelah nama:
int *myVariable;
. Untuk menghargai mengapa, lihat bagaimana Anda mendeklarasikan jenis simbol lain di C:int my_function(int arg);
untuk suatu fungsi;float my_array[3]
untuk sebuah array.Pola umum, yang disebut sebagai deklarasi follow use , adalah bahwa jenis simbol dibagi menjadi bagian sebelum nama, dan bagian-bagian di sekitar nama, dan bagian-bagian ini di sekitar nama meniru sintaks yang akan Anda gunakan untuk mendapatkan nilai jenis di sebelah kiri:
int a_return_value = my_function(729);
float an_element = my_array[2];
dan:
int copy_of_value = *myVariable;
.C ++ melempar kunci pas dalam karya dengan referensi, karena sintaks pada titik di mana Anda menggunakan referensi identik dengan jenis nilai, sehingga Anda bisa berpendapat bahwa C ++ mengambil pendekatan yang berbeda untuk C. Di sisi lain, C ++ tetap sama perilaku C dalam kasus pointer, jadi referensi benar-benar berdiri sebagai yang aneh dalam hal ini.
sumber
Itu hanya masalah preferensi.
Ketika Anda membaca kode, membedakan antara variabel dan pointer lebih mudah dalam kasus kedua, tetapi hal itu dapat menyebabkan kebingungan ketika Anda meletakkan kedua variabel dan pointer dari tipe umum dalam satu baris (yang seringkali tidak disarankan oleh pedoman proyek, karena mengurangi keterbacaan).
Saya lebih suka mendeklarasikan pointer dengan tanda yang sesuai di sebelah ketikkan nama, misalnya
sumber
Seorang guru yang hebat pernah berkata, "Baca jalan kompiler, Anda harus."
http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835
Memang ini pada topik penempatan const, tetapi aturan yang sama berlaku di sini.
Kompiler membacanya sebagai:
tidak seperti:
Jika Anda terbiasa menempatkan bintang di sebelah variabel, itu akan membuat deklarasi Anda lebih mudah dibaca. Itu juga menghindari merusak pemandangan seperti:
- Edit -
Untuk menjelaskan dengan tepat apa yang saya maksud ketika saya mengatakan itu diuraikan sebagai
int (*a)
, itu berarti yang*
mengikat lebih erata
daripada yang dilakukannyaint
, dalam banyak cara yang dalam ekspresi4 + 3 * 7
3
mengikat lebih erat7
daripada yang dilakukannya4
karena prioritas lebih tinggi dari*
.Dengan permintaan maaf untuk seni ascii, sinopsis AST untuk penguraian
int *a
terlihat kurang lebih seperti ini:Seperti yang ditunjukkan dengan jelas,
*
ikat lebih erat kea
sejak leluhur mereka bersamaDeclarator
, sementara Anda harus pergi jauh-jauh ke atas pohonDeclaration
untuk menemukan leluhur bersama yang melibatkanint
.sumber
(int*) a
.int
pada kasus ini. Langkah 2. Baca deklarasi, termasuk dekorasi jenis apa pun.*a
pada kasus ini. Langkah 3 Baca karakter berikutnya. Jika koma, konsumsilah dan kembali ke langkah 2. Jika titik koma berhenti. Jika ada yang lain melemparkan kesalahan sintaksis. ...int* a, b;
dan mendapatkan sepasang petunjuk. Maksud saya membuat adalah bahwa*
mengikat ke variabel dan diuraikan dengan itu, bukan dengan tipe untuk membentuk "tipe dasar" dari deklarasi. Itu juga bagian dari alasan bahwa typedef diperkenalkan untuk memungkinkantypedef int *iptr;
iptr a, b;
membuat beberapa petunjuk. Dengan menggunakan typedef Anda dapat mengikat*
keint
.Declaration-Specifier
dengan "dekorasi" diDeclarator
untuk sampai pada jenis akhir untuk setiap variabel. Namun itu tidak "bergerak" dekorasi untuk specifier Deklarasi jika tidakint a[10], b;
akan menghasilkan hasil yang benar-benar konyol, itu mem-parsingint *a, b[10];
sebagaiint
*a
,
b[10]
;
. Tidak ada cara lain untuk menggambarkannya yang masuk akal.int*a
pada akhirnya dibaca oleh kompiler sebagai: "a
memiliki tipeint*
". Itulah yang saya maksud dengan komentar asli saya.Karena itu lebih masuk akal ketika Anda memiliki deklarasi seperti:
sumber
int* a, b;
membuatb
pointer keint
. Tetapi mereka tidak melakukannya. Dan dengan alasan yang bagus. Di bawah sistem yang diusulkan Anda apa jenisb
dalam deklarasi berikut:int* a[10], b;
?Untuk mendeklarasikan banyak pointer dalam satu baris, saya lebih suka
int* a, * b;
yang secara lebih intuitif mendeklarasikan "a" sebagai pointer ke integer, dan tidak mencampur gaya ketika juga mendeklarasikan "b." Seperti yang dikatakan seseorang, saya tidak akan mendeklarasikan dua tipe berbeda dalam pernyataan yang sama.sumber
Orang yang lebih suka
int* x;
mencoba memaksakan kode mereka ke dunia fiksi di mana jenisnya ada di sebelah kiri dan pengenal (nama) ada di sebelah kanan.Saya katakan "fiksi" karena:
Itu mungkin terdengar gila, tetapi Anda tahu itu benar. Berikut ini beberapa contohnya:
int main(int argc, char *argv[])
berarti "main
adalah fungsi yang membawaint
dan array pointer kechar
dan mengembalikan sebuahint
." Dengan kata lain, sebagian besar tipe informasi ada di sebelah kanan. Beberapa orang berpikir deklarasi fungsi tidak masuk hitungan karena itu entah bagaimana "istimewa." OK, mari kita coba variabel.void (*fn)(int)
berartifn
adalah penunjuk ke fungsi yang mengambilint
dan tidak mengembalikan apa pun.int a[10]
mendeklarasikan 'a' sebagai array 10int
detik.pixel bitmap[height][width]
.Jelas, saya sudah memilih contoh ceri yang memiliki banyak jenis info di sebelah kanan untuk menyampaikan maksud saya. Ada banyak deklarasi di mana sebagian besar - jika tidak semua - dari jenis di sebelah kiri, seperti
struct { int x; int y; } center
.Sintaksis deklarasi ini tumbuh dari keinginan K&R untuk mendeklarasikan penggunaannya. Membaca deklarasi sederhana adalah intuitif, dan membaca yang lebih kompleks dapat dikuasai dengan mempelajari aturan kanan-kiri-kanan (kadang-kadang disebut aturan spiral atau hanya aturan kanan-kiri).
C cukup sederhana sehingga banyak programmer C merangkul gaya ini dan menulis deklarasi sederhana sebagai
int *p
.Dalam C ++, sintaks mendapat sedikit lebih kompleks (dengan kelas, referensi, templat, kelas enum), dan, sebagai reaksi terhadap kerumitan itu, Anda akan melihat lebih banyak upaya memisahkan jenis dari pengenal dalam banyak deklarasi. Dengan kata lain, Anda mungkin melihat lebih banyak
int* p
deklarasi gaya jika Anda melihat petak besar kode C ++.Dalam bahasa apa pun, Anda selalu dapat memiliki tipe di sisi kiri deklarasi variabel dengan (1) tidak pernah mendeklarasikan beberapa variabel dalam pernyataan yang sama, dan (2) memanfaatkan
typedef
s (atau alias deklarasi, yang, ironisnya, menempatkan alias pengidentifikasi di sebelah kiri jenis). Sebagai contoh:sumber
(*fn)
tetap mempertahankan pointer yang terkait denganfn
bukan tipe kembali.Saya sudah menjawab pertanyaan serupa di CP, dan, karena tidak ada yang menyebutkan itu, juga di sini saya harus menunjukkan bahwa C adalah bahasa format bebas , gaya apa pun yang Anda pilih ok sementara parser dapat membedakan masing-masing token. Kekhasan ini C menyebabkan jenis yang sangat khusus kontes yang disebut kontes kebingungan C .
sumber
Banyak argumen dalam topik ini sangat subjektif dan argumen tentang "bintang mengikat nama variabel" adalah naif. Berikut beberapa argumen yang bukan hanya pendapat:
Kualifikasi jenis pointer yang terlupakan
Secara formal, "bintang" tidak termasuk dalam jenis atau nama variabel, itu adalah bagian dari item gramatikal bernama pointer . Sintaks C formal (ISO 9899: 2018) adalah:
Di mana declaration-specifiers berisi tipe (dan penyimpanan), dan init-declarator-list berisi pointer dan nama variabel. Yang kami lihat jika kami membedah sintaksis daftar deklarator ini lebih lanjut:
Di mana deklarator adalah seluruh deklarasi, deklarator langsung adalah pengenal (nama variabel), dan sebuah pointer adalah bintang yang diikuti oleh daftar kualifikasi jenis opsional yang dimiliki oleh pointer itu sendiri.
Apa yang membuat berbagai argumen gaya tentang "bintang milik variabel" tidak konsisten, adalah bahwa mereka telah melupakan kualifikasi jenis penunjuk ini.
int* const x
,int *const x
atauint*const x
?Pertimbangkan
int *const a, b;
, apa saja jenisa
danb
? Tidak begitu jelas bahwa "bintang milik variabel" lebih lama lagi. Sebaliknya, orang akan mulai merenungkan di manaconst
milik.Anda pasti dapat membuat argumen suara bahwa bintang tersebut termasuk dalam kualifikasi jenis pointer, tetapi tidak lebih dari itu.
Daftar kualifikasi tipe untuk pointer dapat menyebabkan masalah bagi mereka yang menggunakan
int *a
style. Mereka yang menggunakan pointer di dalamtypedef
(yang seharusnya tidak, praktik yang sangat buruk!) Dan berpikir "bintang milik nama variabel" cenderung menulis bug yang sangat halus ini:Ini mengkompilasi dengan bersih. Sekarang Anda mungkin berpikir kode itu dibuat const benar. Tidak begitu! Kode ini secara tidak sengaja adalah kebenaran const palsu.
Jenis
foo
sebenarnyaint*const*
- pointer paling luar dibuat hanya-baca, bukan data yang runcing. Jadi di dalam fungsi ini bisa kita lakukan**foo = n;
dan itu akan mengubah nilai variabel di pemanggil.Ini karena dalam ekspresi
const bad_idea_t *foo
,*
itu bukan milik nama variabel di sini! Dalam kode semu, deklarasi parameter ini harus dibaca sebagaiconst (bad_idea_t *) foo
dan tidak seperti(const bad_idea_t) *foo
. Bintang milik tipe pointer tersembunyi dalam kasus ini - jenisnya adalah pointer dan pointer yang memenuhi syarat ditulis sebagai*const
.Tetapi kemudian akar masalah dalam contoh di atas adalah praktik menyembunyikan pointer di belakang a
typedef
dan bukan*
style.Mengenai deklarasi beberapa variabel dalam satu baris
Mendeklarasikan banyak variabel pada satu baris secara luas diakui sebagai praktik buruk 1) . CERT-C merangkumnya dengan baik sebagai:
Hanya membaca bahasa Inggris, maka akal sehat setuju bahwa sebuah deklarasi harus satu deklarasi.
Dan tidak masalah apakah variabelnya pointer atau tidak. Mendeklarasikan setiap variabel pada satu baris membuat kode lebih jelas di hampir setiap kasus.
Jadi argumen tentang programmer bingung
int* a, b
adalah buruk. Akar masalahnya adalah penggunaan beberapa deklarator, bukan penempatan*
. Terlepas dari gaya, Anda harus menulis ini sebagai gantinya:Argumen lain yang masuk akal tetapi subyektif adalah bahwa mengingat
int* a
tipea
tanpa pertanyaanint*
maka bintang tersebut termasuk dalam jenis kualifikasi.Tetapi pada dasarnya kesimpulan saya adalah bahwa banyak argumen yang diposting di sini hanya subyektif dan naif. Anda tidak dapat benar-benar membuat argumen yang valid untuk gaya mana pun - itu benar-benar masalah preferensi pribadi subjektif.
1) CERT-C DCL04-C .
sumber
typedef int *bad_idea_t;
void func(const bad_idea_t bar);
Seperti yang diajarkan nabi besar Dan Saks "Jika Anda selalu menempatkanconst
sejauh sejauh yang Anda bisa, tanpa mengubah makna semantik" ini sepenuhnya berhenti menjadi masalah. Itu juga membuatconst
deklarasi Anda lebih konsisten untuk dibaca. "Segala sesuatu di sebelah kanan kata const adalah apa yang const, segala yang di sebelah kiri adalah tipenya." Ini akan berlaku untuk semua const dalam deklarasi. Cobalah denganint const * * const x;
Mempertimbangkan
Ini tidak diterjemahkan ke
Ini sebenarnya diterjemahkan menjadi
Ini membuat
int *x
notasi agak tidak konsisten.sumber
new
adalah operator C ++. Pertanyaannya ditandai "C" dan bukan "C ++".