Jawaban:
Mereka sama persis. Namun, dalam
int *myVariable, myVariable2;
Tampaknya jelas bahwa myVariable memiliki tipe int * , sedangkan myVariable2 memiliki tipe int . Di
int* myVariable, myVariable2;
mungkin tampak jelas bahwa keduanya bertipe int * , tetapi itu tidak benar karena myVariable2
memiliki tipe int .
Oleh karena itu, gaya pemrograman pertama lebih intuitif.
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 tipe int
, yang masuk akal.
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 nilai pointer
itu sendiri.
Karena * pada baris itu mengikat lebih dekat ke variabel daripada ke tipe:
int* varA, varB; // This is misleading
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:
int* varA;
int varB;
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.
int *const a, b;
. Di mana * "mengikat"? Tipe a
is int* 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.
*a = 10;
Baris di atas bukan berarti saya ingin menetapkan 10
ke a
, itu berarti saya ingin menetapkan 10
apa pun lokasi memori a
poin ke. Dan saya belum pernah melihat orang menulis
* a = 10;
apakah kamu Jadi operator dereference hampir selalu ditulis tanpa spasi. Ini mungkin untuk membedakannya dari perkalian yang terputus di beberapa baris:
x = a * b * c * d
* e * f * g;
Ini *e
akan menyesatkan, bukan?
Oke, sekarang apa arti sebenarnya dari baris berikut:
int *a;
Kebanyakan orang akan mengatakan:
Ini berarti bahwa a
adalah penunjuk ke int
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 tipe int
.
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.
int a, *b, (*c)();
sebagai sesuatu seperti "menyatakan objek-objek berikut sebagai int
: objek a
, objek menunjuk ke b
, dan objek kembali dari fungsi yang ditunjukkan oleh c
".
*
dalam int *a;
tidak operator, dan tidak dereferencing a
(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 dari a
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 oleh int *a;
. a
bahkan belum ada pada saat *
ini sedang diproses. Apakah Anda menganggapnya int *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.
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
int* pMyPointer;
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:
int (*a);
tidak seperti:
(int*) a;
Jika Anda terbiasa menempatkan bintang di sebelah variabel, itu akan membuat deklarasi Anda lebih mudah dibaca. Itu juga menghindari merusak pemandangan seperti:
int* a[10];
- Edit -
Untuk menjelaskan dengan tepat apa yang saya maksud ketika saya mengatakan itu diuraikan sebagai int (*a)
, itu berarti yang *
mengikat lebih erat a
daripada yang dilakukannya int
, dalam banyak cara yang dalam ekspresi 4 + 3 * 7
3
mengikat lebih erat 7
daripada yang dilakukannya 4
karena prioritas lebih tinggi dari *
.
Dengan permintaan maaf untuk seni ascii, sinopsis AST untuk penguraian int *a
terlihat kurang lebih seperti ini:
Declaration
/ \
/ \
Declaration- Init-
Secifiers Declarator-
| List
| |
| ...
"int" |
Declarator
/ \
/ ...
Pointer \
| Identifier
| |
"*" |
"a"
Seperti yang ditunjukkan dengan jelas, *
ikat lebih erat ke a
sejak leluhur mereka bersama Declarator
, sementara Anda harus pergi jauh-jauh ke atas pohon Declaration
untuk menemukan leluhur bersama yang melibatkan int
.
(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 memungkinkan typedef int *iptr;
iptr a, b;
membuat beberapa petunjuk. Dengan menggunakan typedef Anda dapat mengikat *
ke int
.
Declaration-Specifier
dengan "dekorasi" di Declarator
untuk sampai pada jenis akhir untuk setiap variabel. Namun itu tidak "bergerak" dekorasi untuk specifier Deklarasi jika tidak int a[10], b;
akan menghasilkan hasil yang benar-benar konyol, itu mem-parsing int *a, b[10];
sebagai int
*a
,
b[10]
;
. Tidak ada cara lain untuk menggambarkannya yang masuk akal.
int*a
pada akhirnya dibaca oleh kompiler sebagai: " a
memiliki tipe int*
". Itulah yang saya maksud dengan komentar asli saya.
Karena itu lebih masuk akal ketika Anda memiliki deklarasi seperti:
int *a, *b;
int* a, b;
membuat b
pointer ke int
. Tetapi mereka tidak melakukannya. Dan dengan alasan yang bagus. Di bawah sistem yang diusulkan Anda apa jenis b
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.
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:
Dalam C dan C ++, dalam kasus umum, pengenal yang dideklarasikan dikelilingi oleh informasi tipe.
Itu mungkin terdengar gila, tetapi Anda tahu itu benar. Berikut ini beberapa contohnya:
int main(int argc, char *argv[])
berarti " main
adalah fungsi yang membawa int
dan array pointer ke char
dan mengembalikan sebuah int
." 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)
berarti fn
adalah penunjuk ke fungsi yang mengambil int
dan tidak mengembalikan apa pun.
int a[10]
mendeklarasikan 'a' sebagai array 10 int
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:
typedef int array_of_10_ints[10];
array_of_10_ints a;
(*fn)
tetap mempertahankan pointer yang terkait dengan fn
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 .
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:
(6.7) deklarasi:
deklarasi-penentu init-declarator-list opt;
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:
(6.7.6) deklarator:
pointer opt direct-declarator
...
(6.7.6) pointer:
*
type-qualifier-list opt
*
tipe-kualifikasi-list opt pointer
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
atau int*const x
?
Pertimbangkan int *const a, b;
, apa saja jenis a
danb
? Tidak begitu jelas bahwa "bintang milik variabel" lebih lama lagi. Sebaliknya, orang akan mulai merenungkan di mana const
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 dalam typedef
(yang seharusnya tidak, praktik yang sangat buruk!) Dan berpikir "bintang milik nama variabel" cenderung menulis bug yang sangat halus ini:
/*** bad code, don't do this ***/
typedef int *bad_idea_t;
...
void func (const bad_idea_t *foo);
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
sebenarnya int*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 sebagai const (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:
DCL04-C. Jangan mendeklarasikan lebih dari satu variabel per deklarasi
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:
int* a; // or int *a
int b;
Argumen lain yang masuk akal tetapi subyektif adalah bahwa mengingat int* a
tipe a
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 .
typedef int *bad_idea_t;
void func(const bad_idea_t bar);
Seperti yang diajarkan nabi besar Dan Saks "Jika Anda selalu menempatkan const
sejauh sejauh yang Anda bisa, tanpa mengubah makna semantik" ini sepenuhnya berhenti menjadi masalah. Itu juga membuat const
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
int *x = new int();
Ini tidak diterjemahkan ke
int *x;
*x = new int();
Ini sebenarnya diterjemahkan menjadi
int *x;
x = new int();
Ini membuat int *x
notasi agak tidak konsisten.
new
adalah operator C ++. Pertanyaannya ditandai "C" dan bukan "C ++".
typedefs
, tetapi itu akan menambah kompleksitas yang tidak perlu, IMHO.