Bisakah saya menggunakan NULL sebagai pengganti untuk nilai 0?

73

Apakah saya diizinkan menggunakan NULLpointer sebagai pengganti nilai 0?

Atau adakah yang salah dengan perbuatan itu?


Seperti, misalnya:

int i = NULL;

sebagai pengganti:

int i = 0;

Sebagai percobaan saya menyusun kode berikut:

#include <stdio.h>

int main(void)
{
    int i = NULL;
    printf("%d",i);

    return 0;
}

Keluaran:

0

Memang itu memberi saya peringatan ini, yang sepenuhnya benar sendiri:

warning: initialization makes integer from pointer without a cast [-Wint-conversion] 

tetapi hasilnya masih setara.


  • Apakah saya beralih ke "Perilaku Tidak Terdefinisi" dengan ini?
  • Apakah diizinkan menggunakan NULLcara ini?
  • Apakah ada yang salah dengan menggunakan NULLsebagai nilai numerik dalam ekspresi aritmatika?
  • Dan apa hasil dan perilaku di C ++ untuk kasus ini?

Saya telah membaca jawaban dari Apa perbedaan antara NULL, '\ 0' dan 0 tentang apa perbedaan antara NULL, \0dan 0, tetapi saya tidak mendapatkan informasi yang ringkas dari sana, jika cukup diizinkan dan juga hak untuk digunakan NULLsebagai nilai untuk beroperasi dengan dalam penugasan dan operasi aritmatika lainnya.

RobertS mendukung Monica Cellio
sumber
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Samuel Liew
Akan lebih baik untuk mengajukan dua pertanyaan terpisah, satu untuk C dan satu untuk C ++.
Konrad Rudolph

Jawaban:

82

Apakah saya diizinkan menggunakan pointer NULL sebagai pengganti nilai 0?

Tidak , tidak aman untuk melakukannya. NULLadalah konstanta null-pointer, yang dapat memiliki tipe int, tetapi yang lebih tipikal memiliki tipe void *(dalam C), atau sebaliknya tidak dapat langsung ditetapkan ke int(dalam C ++> = 11). Kedua bahasa memungkinkan pointer untuk dikonversi ke integer, tetapi mereka tidak menyediakan untuk konversi tersebut dilakukan secara implisit (meskipun beberapa kompiler menyatakan bahwa sebagai ekstensi). Selain itu, meskipun itu umum untuk mengkonversi pointer nol ke integer untuk menghasilkan nilai 0, standar tidak menjamin itu. Jika Anda menginginkan konstanta dengan tipe intdan nilai 0 maka eja 0.

  • Apakah saya mungkin beralih ke Perilaku Tidak Terdefinisi dengan ini?

Ya, pada implementasi mana pun yang NULLmeluas ke nilai dengan tipe void *atau lainnya yang tidak dapat langsung ditentukan int. Standar tidak menentukan perilaku penugasan Anda pada implementasi seperti itu, karena perilakunya tidak terdefinisi.

  • apakah diizinkan menggunakan NULL dengan cara itu?

Itu gaya yang buruk, dan itu akan merusak beberapa sistem dan dalam beberapa keadaan. Karena Anda tampaknya menggunakan GCC, itu akan pecah dalam contoh Anda sendiri jika Anda dikompilasi dengan -Werroropsi.

  • Apakah ada yang salah tentang menggunakan NULL sebagai nilai numerik dalam ekspresi aritmatika?

Iya. Sama sekali tidak dijamin memiliki nilai numerik. Jika yang Anda maksud 0 maka tulis 0, yang tidak hanya didefinisikan dengan baik, tetapi lebih pendek dan lebih jelas.

  • Dan bagaimana hasilnya dalam C ++ untuk kasus itu?

Bahasa C ++ lebih ketat tentang konversi daripada C dan memiliki aturan yang berbeda untuk NULL, tetapi di sana juga, implementasi dapat menyediakan ekstensi. Sekali lagi, jika Anda berarti 0 maka itu yang harus Anda tulis

John Bollinger
sumber
4
Anda harus menunjukkan bahwa "yang lebih tipikal memiliki tipe void *" hanya benar dari C. void *bukan tipe legal untuk C ++ (karena Anda tidak dapat menetapkan void*untuk tipe pointer lainnya). Dalam C ++ 89 dan C ++ 03, sebenarnya NULL harus bertipe int, tetapi dalam versi yang lebih baru bisa (dan biasanya) nullptr_t.
Martin Bonner mendukung Monica
Anda juga salah mengonversi void*ke intperilaku tidak terdefinisi. Bukan itu; itu adalah implementasi perilaku yang ditentukan.
Martin Bonner mendukung Monica
@ MartinBonnersupportsMonica, Dalam konteks di mana C menetapkan bahwa pointer dikonversi ke integer, hasil konversi memang ditentukan implementasi, tapi bukan itu yang saya bicarakan. Ini adalah penugasan pointer ke nilai lt tipe integer (tanpa secara eksplisit mengkonversi melalui gips) yang memiliki perilaku tidak terdefinisi. Bahasa tidak mendefinisikan konversi otomatis di sana.
John Bollinger
@ MartinBonnersupportsMonica, saya telah mengedit agar lebih inklusif dari pertimbangan C ++. Dalam hal apa pun, tema sentral berlaku sama untuk kedua bahasa: jika Anda menginginkan integer 0 maka tuliskan secara eksplisit sebagai konstanta integer dengan tipe yang sesuai.
John Bollinger
31

NULLadalah beberapa konstanta null pointer. Dalam C itu bisa berupa ekspresi konstanta integer dengan nilai 0atau ekspresi seperti itu void*, dengan yang terakhir lebih mungkin. Yang berarti Anda tidak dapat berasumsi untuk menggunakan secara NULLbergantian dengan nol. Misalnya, dalam contoh kode ini

char const* foo = "bar"; 
foo + 0;

Mengganti 0dengan NULLtidak dijamin sebagai program C yang valid, karena penambahan antara dua pointer (apalagi dari jenis pointer yang berbeda) tidak ditentukan. Ini akan menyebabkan diagnostik dikeluarkan karena pelanggaran kendala. Operan untuk penambahan tidak akan valid .


Adapun C ++, semuanya agak berbeda. Kurangnya konversi implisit dari void*ke jenis objek lain berarti yang NULLsecara historis didefinisikan 0dalam kode C ++. Di C ++ 03, Anda mungkin bisa lolos begitu saja. Tetapi karena C ++ 11 secara hukum dapat didefinisikan sebagai nullptrkata kunci . Sekarang lagi menghasilkan kesalahan, sejakstd::nullptr_t mungkin tidak ditambahkan ke tipe pointer.

Jika NULLditentukan saat nullptritu, bahkan percobaan Anda menjadi tidak valid. Tidak ada konversi dari std::nullptr_tke integer. Itulah mengapa ini dianggap sebagai konstanta null pointer yang lebih aman.

StoryTeller - Unslander Monica
sumber
Untuk kelengkapan, 0L juga merupakan konstanta penunjuk nol dan dapat digunakan seperti NULLpada kedua bahasa.
eerorika
1
@ jamesqf Standar mengatakan bahwa konstanta integer dengan nilai 0 adalah konstanta penunjuk nol. Oleh karena itu 0L adalah konstanta penunjuk nol.
eerorika
1
@eerorika: Hanya apa yang dibutuhkan dunia, standar yang mengabaikan kenyataan :-) Karena jika saya mengingat perakitan 80286 saya dengan benar, Anda bahkan tidak dapat menetapkan penunjuk jauh sebagai satu operasi tunggal, sehingga penulis kompiler harus spesial -buat itu.
jamesqf
2
@ jamesqf Per FAQ C , re: membuat 0null pointer konstan: "jelas sebagai sop untuk semua kode C yang masih ada ditulis dengan buruk yang membuat asumsi yang salah"
Andrew Henle
3
@ jamesqf, bahwa setiap dan setiap konstanta integer dengan nilai 0 adalah konstanta null-pointer (dalam C) tidak ada hubungannya dengan implementasi pointer hardware. Perhatikan juga bahwa standar C tidak mengenali perbedaan antara pointer dekat dan jauh dalam hal apa pun, tetapi mendukung penugasan pointer-to-pointer. Ini juga mendukung (beberapa) perbandingan pointer, yang menyajikan masalah menarik untuk format pengalamatan tersegmentasi seperti 286's.
John Bollinger
21

Apakah saya diizinkan menggunakan pointer NULL sebagai pengganti nilai 0?

int i = NULL;

Aturan bervariasi antara bahasa dan versinya. Dalam beberapa kasus Anda bisa dan dalam kasus lain, Anda tidak bisa. Bagaimanapun, Anda seharusnya tidak . Jika Anda beruntung, kompiler Anda akan memperingatkan ketika Anda mencobanya atau bahkan lebih baik, gagal dikompilasi.

Dalam C ++, sebelum C ++ 11 (kutipan dari C ++ 03):

[lib.support.types]

NULL adalah konstanta penunjuk C ++ null yang ditentukan implementasi dalam Standar Internasional ini.

Tidak masuk akal untuk menggunakan konstanta penunjuk nol sebagai integer. Namun...

[conv.ptr]

Konstanta penunjuk nol adalah nilai konstanta ekspresi integral (5.19) dari tipe integer yang bernilai nol.

Jadi, itu akan berfungsi secara teknis bahkan jika itu tidak masuk akal. Karena teknis ini, Anda mungkin menemukan program yang ditulis dengan buruk yang menyalahgunakanNULL .

Sejak C ++ 11 (kutipan dari konsep terbaru):

[conv.ptr]

Sebuah nol pointer konstan adalah literal bilangan bulat ([lex.icon]) dengan nilai nol atau prvalue tipe std :: nullptr_t .

A std​::​nullptr_­ttidak dapat dikonversi menjadi integer, jadi menggunakan NULLinteger hanya akan bekerja secara kondisional, tergantung pada pilihan yang dibuat oleh implementasi bahasa.

PS nullptradalah nilai jenis std​::​nullptr_­t. Kecuali Anda membutuhkan program untuk mengkompilasi di pra-C ++ 11, Anda harus selalu menggunakan nullptrbukan NULL.


C sedikit berbeda (kutipan dari C11 draft N1548):

6.3.2.3 Bahasa / Konversi / Operan / Pointer lain

3 Ekspresi konstanta bilangan bulat dengan nilai 0, atau ekspresi yang digunakan untuk mengetikvoid * , disebut konstanta penunjuk nol. ...

Jadi, kasusnya mirip dengan posting C ++ 11 yaitu penyalahgunaan NULLkarya bersyarat tergantung pada pilihan yang dibuat oleh implementasi bahasa.

eerorika
sumber
10

Ya , meskipun tergantung pada pelaksanaan Anda mungkin perlu para pemain. Tapi ya, itu 100% sah, jika tidak.

Meskipun itu benar-benar gaya yang sangat buruk (tidak perlu dikatakan?).

NULLadalah, atau dulu, sebenarnya bukan C ++, itu adalah C. Namun standar tidak , seperti untuk banyak warisan C, memiliki dua klausa ([diff.null] dan [support.types.nullptr]) yang secara teknis membuat NULLC ++. Ini adalah konstanta null-pointer implementasi-didefinisikan . Oleh karena itu, bahkan jika itu gaya yang buruk, secara teknis C ++ seperti yang seharusnya.
Seperti yang ditunjukkan dalam catatan kaki , implementasi yang mungkin bisa 0atau 0L, tetapi tidak (void*)0 .

NULLbisa, tentu saja (standar tidak secara eksplisit mengatakannya, tetapi itu adalah satu-satunya pilihan yang tersisa setelah 0atau 0L) menjadinullptr . Itu hampir tidak pernah terjadi, tetapi itu adalah kemungkinan yang sah.

Peringatan bahwa kompiler menunjukkan kepada Anda menunjukkan bahwa kompiler sebenarnya tidak sesuai (kecuali Anda dikompilasi dalam mode C). Karena, well, menurut peringatan itu, ia benar - benar mengkonversi null pointer (bukan nullptryang akan menjadi nullptr_t, yang akan berbeda), jadi tampaknya definisi NULLmemang (void*)0, yang mungkin tidak.

Either way, Anda memiliki dua kemungkinan kasus yang sah (mis. Kompiler tidak rusak). Entah (kasus realistis), NULLadalah sesuatu seperti 0atau 0L, maka Anda memiliki konversi "nol atau satu" menjadi bilangan bulat, dan Anda baik untuk melakukannya.

Atau NULLmemang nullptr. Dalam hal ini Anda memiliki nilai berbeda yang memiliki jaminan tentang perbandingan serta konversi yang jelas dari bilangan bulat, tetapi sayangnya tidak untuk bilangan bulat. Namun, ia memiliki konversi yang jelas ke bool(menghasilkan false), dan boolmemiliki konversi yang jelas ke integer (menghasilkan 0).

Sayangnya, itu adalah dua konversi, jadi itu tidak dalam "nol atau satu" seperti yang ditunjukkan dalam [konv]. Jadi, jika implementasi Anda didefinisikan NULLsebagai nullptr, maka Anda harus menambahkan pemeran eksplisit agar kode Anda benar.

Damon
sumber
6

Dari faq C:

T: Jika NULL dan 0 setara dengan konstanta penunjuk nol, yang harus saya gunakan?

A: Ini hanya dalam konteks pointer NULLdan 0setara. NULLharus tidak digunakan bila jenis lain 0 diperlukan, meskipun mungkin bekerja, karena hal itu mengirimkan pesan gaya yang salah. (Selanjutnya, ANSI memungkinkan definisi NULL menjadi ((void *)0), yang tidak akan bekerja sama sekali dalam konteks non-pointer.) Secara khusus, jangan gunakan NULLketika karakter null ASCII null (NUL) diinginkan. Berikan definisi Anda sendiri

http://c-faq.com/null/nullor0.html

phuclv
sumber
5

Penafian: Saya tidak tahu C ++. Jawaban saya tidak dimaksudkan untuk diterapkan dalam konteks C ++

'\0'adalah intdengan nilai nol, persis seperti 100% 0.

for (int k = 10; k > '\0'; k--) /* void */;
for (int k = 10; k > 0; k--) /* void */;

Dalam konteks pointer , 0dan NULLsetara 100%:

if (ptr) /* ... */;
if (ptr != NULL) /* ... */;
if (ptr != '\0') /* ... */;
if (ptr != 0) /* ... */;

semuanya setara 100%.


Perhatikan tentang ptr + NULL

Konteks ptr + NULLini bukan yang dari pointer. Tidak ada definisi untuk penambahan pointer dalam bahasa C; pointer dan integer dapat ditambahkan (atau dikurangkan). Dalam ptr + NULLjika salah ptratau NULLadalah pointer, yang lain harus integer, sehingga ptr + NULLsecara efektif (int)ptr + NULLatau ptr + (int)NULLdan tergantung pada definisi ptrdan NULLbeberapa perilaku dapat diharapkan: itu semua kerja, peringatan untuk konversi antara pointer dan integer, kegagalan untuk mengkompilasi, .. .

pmg
sumber
Saya pernah melihat #define NULL (void *)0sebelumnya. Apakah Anda yakin NULL dan 0 polos 100% setara?
machine_1
2
Dalam konteks penunjuk, ya ... syaratnya ditekankan dalam jawaban saya, terima kasih
21g
@ phuclv: Saya sama sekali tidak tahu tentang C ++. Jawaban saya (kecuali sedikit di antara tanda kurung) adalah tentang C
pmg
@ phuclv ptr + NULLtidak menggunakan NULLdalam konteks pointer
pmg
3
@JesperJuhl: dalam konteks pointer mereka setara 100%. Saya tidak tahu apa nullptritu, tetapi ((void*)0)dan 0(atau '\0') setara dalam konteks petunjuk ...if (ptr == '\0' /* or equivalent 0, NULL */)
pmg
5

Tidak, tidak lagi disukai untuk menggunakan NULL(cara lama initilisasi pointer).

Sejak C ++ 11:

Kata kunci nullptrmenunjukkan pointer literal. Ini adalah nilai dari tipe std :: nullptr_t. Ada konversi implisit dari nullptrke nilai pointer nol dari semua jenis pointer dan pointer ke tipe anggota. Konversi serupa ada untuk konstanta penunjuk nol, yang mencakup nilai tipe std::nullptr_tserta makro NULL.

https://en.cppreference.com/w/cpp/language/nullptr

Sebenarnya, std :: nullptr_t adalah tipe dari null pointer literal nullptr,. Ini adalah tipe berbeda yang bukan tipe pointer atau pointer ke tipe anggota.

#include <cstddef>
#include <iostream>

void f(int* pi)
{
   std::cout << "Pointer to integer overload\n";
}

void f(double* pd)
{
   std::cout << "Pointer to double overload\n";
}

void f(std::nullptr_t nullp)
{
   std::cout << "null pointer overload\n";
}

int main()
{
    int* pi; double* pd;

    f(pi);
    f(pd);
    f(nullptr);  // would be ambiguous without void f(nullptr_t)
    // f(0);  // ambiguous call: all three functions are candidates
    // f(NULL); // ambiguous if NULL is an integral null pointer constant 
                // (as is the case in most implementations)
}

Keluaran:

Pointer to integer overload
Pointer to double overload
null pointer overload
Mannoj
sumber
Pertanyaannya adalah tentang menetapkan NULL ke 0 untuk bilangan bulat. Dalam hal ini, tidak ada yang berubah dengan nullptr bukan NULL.
ivan.ukr
Dia menggunakan kata-kata sebagai "NULL pointer".
Mannoj
Omong-omong, C ++ tidak memiliki konsep NULL setelah C ++ 11. Penulis mungkin bingung tentang penggunaan constexpr atau mendefinisikan inisialisasi cara lama. en.cppreference.com/w/cpp/language/default_initialization
Mannoj