Saya telah menulis hal-hal seperti
char *x=NULL;
dengan asumsi itu
char *x=2;
akan membuat char
pointer ke alamat 2.
Tapi, dalam Tutorial Pemrograman GNU C dikatakan bahwa int *my_int_ptr = 2;
menyimpan nilai integer 2
ke alamat acak apa pun my_int_ptr
saat dialokasikan.
Ini sepertinya menyiratkan bahwa saya sendiri char *x=NULL
menetapkan apa pun nilai NULL
cast ke a char
ke beberapa alamat acak dalam memori.
Sementara
#include <stdlib.h>
#include <stdio.h>
int main()
{
char *x=NULL;
if (x==NULL)
printf("is NULL\n");
return EXIT_SUCCESS;
}
tidak, pada kenyataannya, mencetak
adalah NULL
ketika saya mengompilasi dan menjalankannya, saya khawatir bahwa saya mengandalkan perilaku yang tidak ditentukan, atau setidaknya perilaku yang kurang ditentukan, dan bahwa saya harus menulis
char *x;
x=NULL;
sebagai gantinya.
c
pointers
initialization
fagricipni.dll
sumber
sumber
int *x = whatever;
dilakukan dan apa yangint *x; *x = whatever;
dilakukannya.int *x = whatever;
sebenarnya berperilaku sepertiint *x; x = whatever;
, tidak*x = whatever;
.Jawaban:
TL; DR Ya, sangat banyak.
The klaim yang sebenarnya dibuat pada panduan dibaca seperti
Yah, mereka adalah salah, Anda benar.
Untuk pernyataan, ( mengabaikan, untuk saat ini, fakta bahwa penunjuk ke konversi integer adalah perilaku yang ditentukan implementasi )
int * my_int_ptr = 2;
my_int_ptr
adalah variabel (tipe pointer keint
), ia memiliki alamat sendiri (tipe: alamat pointer ke integer), Anda menyimpan nilai2
ke dalam yang alamat.Sekarang,
my_int_ptr
sebagai tipe penunjuk, bisa kita katakan, ini menunjuk ke nilai "type" di lokasi memori yang ditunjukkan oleh nilai yang dipegangmy_int_ptr
. Jadi, Anda pada dasarnya menempatkan nilai dari variabel pointer, bukan nilai dari lokasi memori yang ditunjuk oleh pointer.Jadi, sebagai kesimpulan
char *x=NULL;
menginisialisasi variabel pointer
x
keNULL
, bukan nilai di alamat memori yang ditunjukkan oleh pointer .Ini sama dengan
char *x; x = NULL;
Ekspansi:
Sekarang, menjadi benar-benar sesuai, pernyataan seperti
int * my_int_ptr = 2;
ilegal, karena melibatkan pelanggaran batasan. Untuk lebih jelasnya,
my_int_ptr
adalah variabel penunjuk, ketikint *
2
memiliki tipeint
, menurut definisi.dan mereka bukan tipe yang "kompatibel", jadi inisialisasi ini tidak valid karena melanggar aturan penetapan sederhana, yang disebutkan dalam bab §6.5.16.1 / P1, yang dijelaskan dalam jawaban Lundin .
Jika ada yang tertarik bagaimana inisialisasi dihubungkan dengan batasan tugas sederhana, kutipan
C11
, bab §6.7.9, P11sumber
2
adalahint
, penugasan adalah masalah. Tapi lebih dari itu.NULL
mungkin juga merupakanint
, sebuahint 0
. Hanya saja yangchar *x = 0;
didefinisikan dengan baik danchar *x = 2;
tidak. 6.3.2.3 Pointer 3 (BTW: C tidak mendefinisikan literal bilangan bulat , hanya literal string dan literal gabungan .0
Merupakan konstanta bilangan bulat )char *x = (void *)0;
, untuk menyesuaikan diri? atau hanya dengan ekspresi lain yang menghasilkan nilai0
?0
bersifat spesial: konstanta tersebut secara implisit mengonversi ke pointer nol secara terpisah dari aturan biasa untuk secara eksplisit mentransmisikan ekspresi integer umum ke jenis pointer.int *p = somePtrExpression
IMHO agak mengerikan karena sepertinya itu mengatur nilai*p
tetapi sebenarnya mengatur nilaip
.Tutorialnya salah. Dalam ISO C,
int *my_int_ptr = 2;
adalah kesalahan. Di GNU C, artinya sama denganint *my_int_ptr = (int *)2;
. Ini mengubah integer2
menjadi alamat memori, dengan cara tertentu seperti yang ditentukan oleh kompilator.Itu tidak mencoba untuk menyimpan apa pun di lokasi yang dialamatkan oleh alamat itu (jika ada). Jika Anda melanjutkan menulis
*my_int_ptr = 5;
, maka itu akan mencoba menyimpan nomor5
di lokasi yang dialamatkan oleh alamat itu.sumber
Untuk memperjelas mengapa tutorial salah,
int *my_int_ptr = 2;
merupakan "pelanggaran batasan", ini adalah kode yang tidak diperbolehkan untuk dikompilasi dan kompilator harus memberi Anda diagnosis saat menghadapinya.Sesuai 6.5.16.1 Penugasan sederhana:
Dalam hal ini operan kiri adalah penunjuk yang tidak memenuhi syarat. Tidak ada yang menyebutkan bahwa operan kanan diperbolehkan menjadi integer (tipe aritmatika). Jadi kodenya melanggar standar C.
GCC diketahui berperilaku buruk kecuali Anda secara eksplisit memberitahukannya sebagai compiler C standar. Jika Anda mengkompilasi kode sebagai
-std=c11 -pedantic-errors
, itu akan memberikan diagnosis yang benar seperti yang harus dilakukan.sumber
void *
, disebut konstanta penunjuk nol." Perhatikan poin poin kedua hingga terakhir dalam kutipan Anda. Oleh karena itu,int* p = 0;
cara menulis adalah legalint* p = NULL;
. Meskipun yang terakhir lebih jelas dan lebih konvensional.int m = 1, n = 2 * 2, * p = 1 - 1, q = 2 - 1;
juga legal.intptr_t
eksplisit ke salah satu jenis yang diizinkan di sisi kanan. Artinya,void* a = (void*)(intptr_t)b;
sah menurut poin 4, tetapi(intptr_t)b
bukan merupakan jenis penunjuk yang kompatibel, atau avoid*
, atau konstanta penunjuk nol, danvoid* a
bukan juga jenis aritmatika_Bool
. Standar mengatakan bahwa konversi itu legal, tetapi tidak implisit.int *my_int_ptr = 2
Ini sepenuhnya salah. Jika ini benar-benar ditulis, dapatkan buku atau tutorial yang lebih baik.
int *my_int_ptr = 2
mendefinisikan pointer integer yang menunjuk ke alamat 2. Kemungkinan besar Anda akan mengalami crash jika Anda mencoba mengakses alamat2
.*my_int_ptr = 2
, yaitu tanpaint
di baris, menyimpan nilai dua ke alamat acak mana punmy_int_ptr
yang ditunjuk. Setelah mengatakan ini, Anda dapat menetapkanNULL
ke pointer saat sudah ditentukan.char *x=NULL;
sangat valid C.Sunting: Saat menulis ini, saya tidak tahu bahwa konversi integer ke pointer adalah implementasi perilaku yang ditentukan. Silakan lihat jawaban bagus oleh @MM dan @SouravGhosh untuk detailnya.
sumber
Banyak kebingungan tentang petunjuk C berasal dari pilihan yang sangat buruk yang awalnya dibuat terkait gaya pengkodean, diperkuat oleh pilihan kecil yang sangat buruk dalam sintaks bahasa.
int *x = NULL;
benar C, tetapi sangat menyesatkan, saya bahkan akan mengatakan tidak masuk akal, dan itu telah menghambat pemahaman bahasa bagi banyak pemula. Itu membuat orang berpikir bahwa nanti kita bisa melakukan*x = NULL;
yang tentu saja tidak mungkin. Anda lihat, jenis variabelnya bukanint
, dan nama variabelnya bukan*x
, dan*
deklarasi in juga tidak memainkan peran fungsional apa pun yang berkolaborasi dengan=
. Ini murni deklaratif. Jadi, yang lebih masuk akal adalah ini:int* x = NULL;
yang juga benar C, meskipun tidak sesuai dengan gaya pengkodean K&R asli. Itu membuatnya sangat jelas bahwa tipenya adalahint*
, dan variabel pointernya adalahx
, sehingga menjadi bukti jelas bahkan bagi yang belum tahu bahwa nilainyaNULL
disimpan kex
dalamnya, yang merupakan pointer keint
.Selain itu, akan lebih mudah untuk mendapatkan aturan: ketika bintang jauh dari nama variabel maka itu adalah deklarasi, sedangkan bintang yang dilampirkan ke nama adalah dereferensi penunjuk.
Jadi, sekarang menjadi jauh lebih dapat dimengerti bahwa lebih jauh kita bisa melakukan
x = NULL;
atau*x = 2;
dengan kata lain memudahkan seorang pemula untuk melihat bagaimanavariable = expression
mengarah kepointer-type variable = pointer-expression
dandereferenced-pointer-variable = expression
. (Untuk awal, dengan 'ekspresi' maksud saya 'nilai r'.)Pilihan yang tidak menguntungkan dalam sintaks bahasa adalah ketika mendeklarasikan variabel lokal Anda dapat mengatakan
int i, *p;
yang mendeklarasikan integer dan pointer ke integer, sehingga membuat orang percaya bahwa*
adalah bagian yang berguna dari nama tersebut. Tetapi tidak, dan sintaks ini hanyalah kasus khusus yang unik, ditambahkan untuk kenyamanan, dan menurut saya seharusnya tidak pernah ada, karena itu membatalkan aturan yang saya usulkan di atas. Sejauh yang saya tahu, tidak ada tempat lain dalam bahasa ini yang memiliki makna sintaksis ini, tetapi meskipun demikian, ini menunjukkan perbedaan dalam cara jenis penunjuk didefinisikan di C. Di mana pun, dalam deklarasi variabel tunggal, dalam daftar parameter, di anggota struct, dll. Anda dapat mendeklarasikan pointer Anda sebagaitype* pointer-variable
penggantitype *pointer-variable
; itu sangat legal dan lebih masuk akal.sumber
int *x = NULL; is correct C, but it is very misleading, I would even say nonsensical,
... Saya harus setuju untuk tidak setuju.It makes one think
.... berhenti berpikir, baca buku C dulu, jangan tersinggung.int* somePtr, someotherPtr
menyatakan dua petunjuk, pada kenyataannya, saya biasa menulisint* somePtr
tetapi itu mengarah ke bug yang Anda gambarkan.create
sebagai gantinyacreat
. :) Intinya adalah, begitulah adanya dan kita perlu membentuk diri kita sendiri untuk beradaptasi dengan itu. Itu semua bermuara pada pilihan pribadi pada akhirnya, setuju.Saya ingin menambahkan sesuatu yang ortogonal ke banyak jawaban yang sangat bagus. Sebenarnya, menginisialisasi ke
NULL
jauh dari praktik yang buruk dan mungkin berguna jika penunjuk itu mungkin atau mungkin tidak digunakan untuk menyimpan blok memori yang dialokasikan secara dinamis.int * p = NULL; ... if (...) { p = (int*) malloc(...); ... } ... free(p);
Karena menurut standar ISO-IEC 9899
free
adalah nop jika argumennya adalahNULL
, kode di atas (atau sesuatu yang lebih bermakna di sepanjang baris yang sama) adalah sah.sumber
void*
bertobat sesuai kebutuhan. Tetapi memiliki kode yang berfungsi dengan kompiler C dan C ++ dapat memiliki keuntungan.const
pointer yang dideklarasikan di res media , tetapi bahkan ketika pointer harus bisa berubah (seperti yang digunakan dalam loop atau olehrealloc()
), mengaturnya untukNULL
menangkap bug di tempat yang digunakan sebelumnya itu diatur dengan nilai aslinya. Pada kebanyakan sistem, dereferensiNULL
menyebabkan segfault pada titik kegagalan (meskipun ada pengecualian), sedangkan penunjuk yang tidak diinisialisasi berisi sampah dan penulisan ke sana merusak memori arbitrer.NULL
, tetapi bisa sangat sulit untuk membedakan penunjuk sampah dari penunjuk yang valid. Jadi sangat membantu untuk memastikan bahwa semua petunjuk selalu valid atauNULL
, sejak saat deklarasi.ini adalah penunjuk nol
int * nullPtr = (void*) 0;
sumber
Ini benar.
int main() { char * x = NULL; if (x==NULL) printf("is NULL\n"); return EXIT_SUCCESS; }
Fungsi ini benar untuk apa yang dilakukannya. Ini memberikan alamat 0 ke char pointer x. Artinya, ini mengarahkan penunjuk x ke alamat memori 0.
Alternatif:
int main() { char* x = 0; if ( !x ) printf(" x points to NULL\n"); return EXIT_SUCCESS; }
Tebakan saya tentang apa yang Anda inginkan adalah:
int main() { char* x = NULL; x = alloc( sizeof( char )); *x = '2'; if ( *x == '2' ) printf(" x points to an address/location that contains a '2' \n"); return EXIT_SUCCESS; } x is the street address of a house. *x examines the contents of that house.
sumber
char* x = 0; if (x == 0)
benar. Pointer tidak harus berupa bilangan bulat.