Apakah perilaku tidak terdefinisi untuk mencetak pointer nol dengan penentu %p
konversi?
#include <stdio.h>
int main(void) {
void *p = NULL;
printf("%p", p);
return 0;
}
Pertanyaannya berlaku untuk standar C, dan bukan untuk implementasi C.
c
language-lawyer
c99
undefined-behavior
c11
Dror K.
sumber
sumber
Jawaban:
Ini adalah salah satu kasus sudut aneh di mana kami tunduk pada batasan bahasa Inggris dan struktur yang tidak konsisten dalam standar. Jadi paling banter, saya bisa membuat argumen tandingan yang meyakinkan, karena tidak mungkin membuktikannya :) 1
Kode dalam pertanyaan menunjukkan perilaku yang terdefinisi dengan baik.
Karena [7.1.4] adalah dasar pertanyaannya, mari kita mulai dari sana:
Ini adalah bahasa yang kikuk. Salah satu interpretasinya adalah bahwa item dalam daftar adalah UB untuk semua fungsi perpustakaan, kecuali diganti dengan deskripsi individu. Namun daftar tersebut dimulai dengan "seperti", yang menunjukkan bahwa ini ilustrasi, bukan lengkap. Misalnya, ia tidak menyebutkan penghentian null string yang benar (penting untuk perilaku misalnya
strcpy
).Jadi jelas maksud / ruang lingkup 7.1.4 hanyalah bahwa "nilai tidak valid" mengarah ke UB ( kecuali dinyatakan lain ). Kita harus melihat deskripsi setiap fungsi untuk menentukan apa yang dianggap sebagai "nilai tidak valid".
Contoh 1 -
strcpy
[7.21.2.3] hanya mengatakan ini:
Itu tidak menyebutkan secara eksplisit pointer nol, namun juga tidak menyebutkan terminator nol. Sebaliknya, seseorang menyimpulkan dari "string yang ditunjukkan oleh
s2
" bahwa satu-satunya nilai yang valid adalah string (yaitu, penunjuk ke array karakter yang diakhiri dengan null).Memang, pola ini dapat dilihat di seluruh deskripsi individu. Beberapa contoh lain:
Contoh 2 -
printf
[7.19.6.1] mengatakan ini tentang
%p
:Null adalah nilai pointer yang valid, dan bagian ini tidak menyebutkan secara eksplisit bahwa null adalah kasus khusus, atau pointer harus menunjuk ke suatu objek. Dengan demikian itu didefinisikan perilaku.
1. Kecuali jika penulis standar tampil ke depan, atau kecuali kita dapat menemukan sesuatu yang mirip dengan dokumen alasan yang menjelaskan hal-hal.
sumber
Jawaban Singkat
Iya . Mencetak pointer nol dengan penentu
%p
konversi memiliki perilaku yang tidak ditentukan. Karena itu, saya tidak mengetahui adanya implementasi yang sesuai yang akan berperilaku tidak semestinya.Jawabannya berlaku untuk salah satu standar C (C89 / C99 / C11).
Jawaban Panjang
The
%p
konversi specifier mengharapkan argumen dari jenis pointer ke kekosongan, konversi dari pointer ke karakter dicetak adalah pelaksanaan yang ditetapkan. Itu tidak menyatakan bahwa pointer nol diharapkan.Pengenalan fungsi pustaka standar menyatakan bahwa penunjuk nol sebagai argumen untuk fungsi (pustaka standar) dianggap sebagai nilai yang tidak valid, kecuali jika dinyatakan lain secara eksplisit.
C99
/C11
§7.1.4 p1
Contoh untuk fungsi (pustaka standar) yang mengharapkan penunjuk nol sebagai argumen yang valid:
fflush()
menggunakan pointer null untuk membersihkan "semua aliran" (yang berlaku).freopen()
menggunakan pointer nol untuk menunjukkan file "saat ini terkait" dengan aliran.snprintf()
memungkinkan untuk melewatkan pointer nol ketika 'n' adalah nol.realloc()
menggunakan pointer nol untuk mengalokasikan objek baru.free()
memungkinkan untuk melewatkan pointer nol.strtok()
menggunakan pointer nol untuk panggilan berikutnya.Jika kita mengambil kasus untuk
snprintf()
, masuk akal untuk mengizinkan melewatkan pointer nol ketika 'n' adalah nol, tetapi ini tidak berlaku untuk fungsi (perpustakaan standar) lain yang memungkinkan nol 'n' serupa. Sebagai contoh:memcpy()
,memmove()
,strncpy()
,memset()
,memcmp()
.Ini tidak hanya ditentukan dalam pendahuluan ke pustaka standar, tetapi juga sekali lagi dalam pengantar fungsi berikut:
C99 §7.21.1 p2
/C11 §7.24.1 p2
Apakah ini disengaja?
Saya tidak tahu apakah UB
%p
dengan pointer nol sebenarnya disengaja, tetapi karena standar secara eksplisit menyatakan bahwa pointer nol dianggap nilai tidak valid sebagai argumen untuk fungsi perpustakaan standar, dan kemudian berjalan dan secara eksplisit menentukan kasus-kasus di mana null pointer adalah argumen yang valid (snprintf, bebas, dll), dan kemudian ia pergi dan sekali lagi mengulangi persyaratan untuk argumen akan berlaku bahkan dalam nol 'n' kasus (memcpy
,memmove
,memset
), maka saya pikir itu masuk akal untuk mengasumsikan bahwa Komite standar C tidak terlalu peduli dengan hal-hal seperti itu yang tidak ditentukan.sumber
%p
tidak seharusnya merupakan perilaku yang tidak terdefinisiPenulis Standar C tidak berupaya untuk membuat daftar secara mendalam semua persyaratan perilaku yang harus dipenuhi oleh implementasi agar sesuai untuk tujuan tertentu. Sebaliknya, mereka berharap bahwa orang-orang yang menulis kompiler akan menggunakan akal sehat tertentu apakah Standar mengharuskannya atau tidak.
Pertanyaan apakah ada sesuatu yang memanggil UB jarang dengan sendirinya berguna. Pertanyaan penting sebenarnya adalah:
Haruskah seseorang yang mencoba menulis kompiler berkualitas membuatnya berperilaku dengan cara yang dapat diprediksi? Untuk skenario yang dijelaskan, jawabannya jelas ya.
Haruskah programmer berhak mengharapkan bahwa kompiler berkualitas untuk apa pun yang menyerupai platform normal akan berperilaku dengan cara yang dapat diprediksi? Dalam skenario yang dijelaskan, saya akan mengatakan jawabannya adalah ya.
Mungkinkah beberapa penulis kompilator yang bodoh meregangkan interpretasi Standar untuk membenarkan melakukan sesuatu yang aneh? Saya berharap tidak, tetapi tidak akan mengesampingkannya.
Haruskah penyusun sanitasi mengoceh tentang perilakunya? Itu akan tergantung pada tingkat paranoia penggunanya; kompiler pembersih mungkin seharusnya tidak default untuk mengoceh tentang perilaku seperti itu, tetapi mungkin menyediakan opsi konfigurasi untuk dilakukan jika program mungkin di-porting ke kompiler "pintar" / bodoh yang berperilaku aneh.
Jika interpretasi yang masuk akal dari Standar akan menyiratkan suatu perilaku yang didefinisikan, tetapi beberapa penulis kompilator memperluas interpretasi untuk membenarkan melakukan sebaliknya, apakah penting apa yang dikatakan Standar?
sumber
printf("%p", (void*) 0)
perilaku tidak terdefinisi atau tidak, menurut Standar? Panggilan fungsi yang sangat bertingkat sama relevannya dengan harga teh di China. Dan ya, UB sangat umum dalam program dunia nyata - bagaimana dengan itu?%p
untuk setiap kemungkinan arti dari pertanyaan tersebut.