NULL vs Nol di C

Saya baru saja membaca Dapatkah saya menggunakan NULL sebagai pengganti nilai 0?

Singkatnya dalam jawaban disebutkan bahwa penggunaan NULL sebagai pengganti nilai 0 tidak disarankan dan akan mengarah ke UB.

Namun di Apakah aman untuk mengasumsikan bahwa konstanta NULL adalah nol?, singkatnya dikatakan bahwa asumsi if(!ptr)//ptr is a pointer tidak sepenuhnya salah .

Saya tahu isi pertanyaannya berbeda, tetapi bagaimana menjelaskan bahwa menggunakan NULL sebagai pengganti 0 adalah salah, sedangkan if(!ptr) benar? Karena if(!ptr) setara dengan if(ptr==0) (saya berasumsi ini benar, tidak yakin).

Selain itu, saya telah menggunakan if(ptr==0) dan tidak pernah salah bagi saya (untuk memeriksa apakah ptr adalah NULL), dan saya telah menetapkan 0 ke penunjuk ptr dan ketika saya men-debug kode saya ptr adalah NULL< /kuat>. Apakah kedua pengalaman ini aman?


person hanie    schedule 03.04.2020    source sumber


Jawaban (3)


Dari referensi penunjuk NULL ini:

Untuk menginisialisasi pointer ke null atau untuk menetapkan nilai null ke pointer yang sudah ada, konstanta pointer null (NULL, atau konstanta bilangan bulat lainnya dengan nilai nol) dapat digunakan.

[Tekankan pada saya]

Jadi konstanta bilangan bulat 0 adalah konstanta penunjuk nol yang valid.

Namun perhatikan bahwa ini tidak berarti bahwa nilai null sebenarnya pada platform perangkat keras yang digunakan sama dengan 0, ini hanya berarti bahwa kompiler menerima 0 sebagai alias untuk konstanta penunjuk nol yang bergantung pada sistem.

Selain itu, penunjuk nol selalu "salah" dan penunjuk bukan nol selalu "benar", itulah sebabnya kondisi seperti if (ptr) atau if (!ptr) berfungsi dengan baik.

person Some programmer dude    schedule 03.04.2020

Saya tahu isi pertanyaannya berbeda, tetapi bagaimana menjelaskan bahwa menggunakan NULL sebagai pengganti 0 adalah salah, sedangkan if(!ptr) benar? Karena if(!ptr) setara dengan if(ptr==0) (saya berasumsi ini benar, tidak yakin).

if(!ptr) setara dengan if (!ptr != 0) berdasarkan semantik pernyataan if, yang setara dengan if (ptr == 0) berdasarkan semantik operator !, !=, dan == dengan operan penunjuk. Ini cukup konsisten, tetapi tidak mengikuti apa pun yang Anda ketahui tentang operasi bilangan bulat. Operasi pada pointer memiliki seperangkat aturannya sendiri.

Dan itulah yang dibawa pulang. Karena konstanta bilangan bulat dengan nilai nol -- yang merupakan konstruksi kode sumber -- antara lain adalah konstanta penunjuk nol, maka menurut definisi sebanding dengan semua nol nilai penunjuk. Ini tidak menjelaskan apa pun tentang representasi nilai penunjuk nol jenis apa pun, tentang jenis ekspresi yang diperluas makro NULL, atau tentang nilai yang dihasilkan dengan mengonversi ke bilangan bulat nilai penunjuk nol tertentu yang tidak diwakili dalam kode sumber sebagai konstanta bilangan bulat dengan nilai nol.

person John Bollinger    schedule 03.04.2020

NULL adalah makro. Ini adalah "konstanta penunjuk nol yang ditentukan implementasi;" C17dr § 7.19 3.

Ekspresi konstanta bilangan bulat dengan nilai 0, atau ekspresi seperti itu yang diberi tipe void *, disebut konstanta penunjuk nol. C17dr § 6.3.2.3 3

Jadi NULL mungkin bertipe void *, int, long, unsigned, long long, dst.

0 adalah konstanta int.

Jika sudah oke.

Penugasan: Keduanya di bawah menetapkan p,q ke beberapa penunjuk nol.

void *p = 0;
void *q = NULL; 

Perbandingan kode: p==q benar karena semua penunjuk nol sama. Semua null pointer tidak sama dengan alamat objek apa pun. !p dan !q keduanya 1.

Saat tidak oke.

Argumen fungsi

Jenis dan ukurannya NULL ditentukan implementasinya.

printf("%d\n", 0);            // OK - %d expects an int
printf("%d\n", NULL);         // Not OK, NULL could be long, void *, etc.
printf("%p\n", NULL);         // Not OK, NULL could be int, long, long long
printf("%p\n", (void*) NULL); // OK - %p expects a void*

_Generic()

Hasil di bawah ini adalah implementasi yang ditentukan.

_Generic((NULL), \
  void *: "void *", \
  int: "int", \
  long: "long", \
  default: "TBD")

Perbandingan makro

Di bawah ini menghasilkan "kesalahan: operator '*' tidak memiliki operan yang benar" untuk saya. #if !0 baik-baik saja.

#if !NULL
#error foo
#endif
person chux - Reinstate Monica    schedule 03.04.2020
comment
apakah ini ptr=0 oke? Maksud saya, apakah inisialisasi ini bukan ptr=NULL benar untuk digunakan? - person hanie; 03.04.2020
comment
Implementasi mungkin atau mungkin tidak menjamin apa pun tentang semua contoh printf, tetapi implementasi yang menentukan bahwa mereka mendefinisikan NULL dengan cara tertentu akan diperlukan untuk memproses baris tidak oke kedua atau ketiga dengan cara yang bermakna, tergantung pada seberapa tepat mereka menentukan NULL. - person supercat; 03.04.2020
comment
Yang pertama mengonversi int 0 menjadi sebuah pointer, null pointer dan kemudian menetapkannya. Didefinisikan dengan baik. Yang ke-2 mengonversi tipe apa pun NULL menjadi sebuah pointer, null pointer dan kemudian menetapkannya. Juga terdefinisi dengan baik. Gunakan salah satu kode terbaik untuk standar/gaya pengkodean grup Anda. - person chux - Reinstate Monica; 03.04.2020
comment
@supercat Implementasi mungkin atau mungkin tidak menjamin apa pun tentang semua contoh printf, --› Apa yang tidak dijamin tentang printf("%d\n", 0);? - person chux - Reinstate Monica; 03.04.2020
comment
@chux-ReinstateMonica: Mereka akan diminta untuk memberikan jaminan tentang beberapa, namun tidak semua. - person supercat; 03.04.2020
comment
@supercat Saya harap kita sepakat bahwa yang ke-2 dan ke-3 adalah UB potensial mengingat kemungkinan penentu dan tipe argumen mungkin tidak cocok. - person chux - Reinstate Monica; 03.04.2020
comment
@chux-ReinstateMonica: Masing-masing akan menjadi UB pada beberapa implementasi. Standar ini tidak memiliki terminologi untuk menjelaskan tindakan yang mungkin merupakan UB atau tidak berdasarkan ciri-ciri yang ditentukan implementasi; jika ia mengklasifikasikan tindakan tersebut sebagai semua, ia cenderung mengklasifikasikannya sebagai UB tanpa memperhatikan apakah beberapa implementasi harus mendefinisikannya [Misalnya, C99 kemungkinan hanya dimaksudkan untuk menghilangkan persyaratan perilaku C89 untuk angka negatif yang bergeser ke kiri dalam implementasi besaran tanda, yang mungkin masuk akal, tetapi dibutuhkan perilaku yang ditentukan di semua platform ke UB di semua platform. - person supercat; 03.04.2020