Apa yang terjadi dengan seni membaca manual yang bagus?
Jens
8
Saya pikir pertanyaan sebenarnya adalah apa POINT dari opsi seperti ini? mengapa ada yang mau tahu nilai angka char yang dicetak apalagi tulis nilai itu langsung ke memori. Sepertinya para pengembang bosan dan memutuskan untuk memperkenalkan bug ke dalam kernal
Anda menyebutkan bahwa argumen harus berupa penunjuk ke int yang ditandatangani, lalu Anda menggunakan int yang tidak ditandai dalam contoh Anda (mungkin hanya salah ketik).
bta
1
@AndrewS: Karena fungsi akan mengubah nilai variabel.
Jack
3
@Jack intselalu masuk.
jamesdlin
1
@ jamesdlin: Kesalahan saya. Maaf .. saya tidak tahu di mana saya membaca itu.
Jack
1
Untuk beberapa alasan sampel menimbulkan kesalahan dengan catatan n format specifies disabled. Apa alasannya?
Johnny_D
186
Sebagian besar jawaban ini menjelaskan apa yang %ndilakukan (yaitu untuk mencetak apa-apa dan untuk menulis jumlah karakter yang dicetak sejauh ini ke intvariabel), tetapi sejauh ini tidak ada yang benar-benar memberikan contoh dari apa yang digunakan . Ini satu:
int n;
printf("%s: %nFoo\n","hello",&n);
printf("%*sBar\n", n,"");
akan dicetak:
hello:FooBar
dengan Foo and Bar sejajar. (Sangat sepele untuk melakukannya tanpa menggunakan %nuntuk contoh khusus ini, dan secara umum seseorang selalu dapat memutuskan printfpanggilan pertama :
int n = printf("%s: ","hello");
printf("Foo\n");
printf("%*sBar\n", n,"");
Apakah sedikit menambah kenyamanan layak menggunakan sesuatu seperti esoteris %n(dan mungkin memperkenalkan kesalahan) terbuka untuk diperdebatkan.)
Ya ampun - ini adalah versi berbasis karakter menghitung ukuran piksel string dalam font yang diberikan!
Bisakah Anda menjelaskan mengapa & n dan * diperlukan. Apakah mereka berdua petunjuk?
Andrew S
9
@AndrewS &nadalah pointer ( &adalah alamat-operator); sebuah pointer diperlukan karena C adalah nilai by-pass, dan tanpa sebuah pointer, printftidak dapat mengubah nilai n. The %*spenggunaan dalam printfformat string mencetak %sspecifier (dalam hal ini string kosong "") menggunakan lebar lapangan dari nkarakter. Penjelasan printfprinsip - prinsip dasar pada dasarnya di luar ruang lingkup pertanyaan ini (dan jawaban); Saya akan merekomendasikan membaca printfdokumentasi atau mengajukan pertanyaan Anda sendiri pada SO.
jamesdlin
3
Terima kasih telah menunjukkan kasus penggunaan. Saya tidak mengerti mengapa orang hanya menyalin-tempel manual ke SO dan menulis ulang terkadang. Kita adalah manusia dan semuanya dilakukan karena suatu alasan yang harus selalu dijelaskan dalam sebuah jawaban. "Tidak melakukan apa-apa" seperti mengatakan "Kata keren berarti keren" - pengetahuan yang hampir tidak berguna.
the_endian
1
@PSkocik Ini berbelit-belit dan cukup rentan kesalahan tanpa menambahkan tingkat tipuan ekstra.
jamesdlin
18
Saya belum benar-benar melihat banyak penggunaan %nspecifier yang praktis di dunia nyata , tetapi saya ingat bahwa itu digunakan pada kerentanan oldfool printf dengan serangan format string beberapa waktu yang lalu.
Sesuatu yang seperti ini
void authorizeUser(char* username,char* password){...code here setting authorized to false...
printf(username);if( authorized ){
giveControl(username);}}
di mana pengguna jahat dapat mengambil keuntungan dari parameter nama pengguna yang diteruskan ke printf sebagai string format dan menggunakan kombinasi %d, %catau w / e untuk pergi melalui tumpukan panggilan dan kemudian memodifikasi variabel yang diotorisasi ke nilai sebenarnya.
Ya itu penggunaan esoteris, tetapi selalu berguna untuk mengetahui saat menulis daemon untuk menghindari celah keamanan? : D
Ada lebih banyak alasan daripada %nmenghindari menggunakan string input yang tidak dicentang sebagai printfstring format.
Keith Thompson
13
Dari sini kita melihat bahwa ia menyimpan jumlah karakter yang dicetak sejauh ini.
n Argumen harus menjadi pointer ke integer yang ditulis jumlah byte yang ditulis ke output sejauh ini oleh panggilan ini ke salah satu fprintf()fungsi. Tidak ada argumen yang dikonversi.
Contoh penggunaannya adalah:
int n_chars =0;
printf("Hello, World%n",&n_chars);
Sejauh ini semua jawaban tentang itu %ntidak, tetapi tidak mengapa ada orang yang menginginkannya sejak awal. Saya menemukan ini agak berguna dengan sprintf/ snprintf, ketika Anda mungkin perlu memecah atau memodifikasi string yang dihasilkan, karena nilai yang disimpan adalah indeks array ke dalam string yang dihasilkan. Aplikasi ini jauh lebih berguna, dengan sscanf, terutama karena fungsi dalam scanfkeluarga tidak mengembalikan jumlah karakter yang diproses tetapi jumlah bidang.
Penggunaan lain yang benar-benar meretas adalah mendapatkan pseudo-log10 secara gratis pada saat yang sama saat mencetak nomor sebagai bagian dari operasi lain.
+1 untuk menyebutkan kegunaan untuk %n, meskipun saya mohon berbeda tentang "semua jawaban ...". = P
jamesdlin
1
Orang-orang jahat terima kasih telah menggunakan printf /% n, sprintf, dan sscanf;)
jww
6
@noloader: Bagaimana bisa begitu? Penggunaan%n benar-benar nol bahaya kerentanan terhadap penyerang. Kekeliruan yang salah tempat itu %nsebenarnya berasal dari praktik bodoh mengirimkan string pesan daripada string format sebagai argumen format. Situasi ini tentu saja tidak pernah muncul ketika %nsebenarnya merupakan bagian dari format string yang sedang digunakan.
R .. GitHub BERHENTI MEMBANTU ICE
% n memungkinkan Anda menulis ke memori. Saya pikir Anda mengasumsikan bahwa penyerang tidak mengontrol pointer itu (saya bisa saja salah). Jika penyerang mengontrol pointer (itu hanya parameter lain untuk printf), ia dapat melakukan penulisan 4 byte. Apakah dia dapat untung atau tidak adalah cerita yang berbeda.
jww
8
@noloader: Itu benar tentang penggunaan pointer. Tidak ada yang mengatakan "orang jahat terima kasih" untuk menulis *p = f();. Mengapa harus %n, yang hanya merupakan cara lain untuk menulis hasil ke objek yang ditunjuk oleh pointer, dianggap "berbahaya", daripada menganggap pointer itu sendiri berbahaya?
R .. GitHub BERHENTI MEMBANTU ICE
10
Argumen yang terkait dengan %nakan diperlakukan sebagai int*dan diisi dengan jumlah total karakter yang dicetak pada saat itu di printf.
Suatu hari saya menemukan diri saya dalam situasi di mana %ndengan baik akan menyelesaikan masalah saya. Tidak seperti jawaban saya sebelumnya , dalam hal ini, saya tidak dapat menemukan alternatif yang baik.
Saya memiliki kontrol GUI yang menampilkan beberapa teks tertentu. Kontrol ini dapat menampilkan bagian teks itu dengan huruf tebal (atau miring, atau bergaris bawah, dll.), Dan saya bisa menentukan bagian mana dengan menentukan indeks karakter awal dan akhir.
Dalam kasus saya, saya membuat teks dengan kontrol snprintf, dan saya ingin salah satu dari substitusi dibuat tebal. Menemukan indeks awal dan akhir untuk substitusi ini tidak sepele karena:
String berisi banyak pergantian, dan salah satu dari pergantian tersebut adalah sewenang-wenang, teks yang ditentukan pengguna. Ini berarti bahwa melakukan pencarian teks untuk substitusi yang saya pedulikan berpotensi ambigu.
String format mungkin dilokalkan, dan mungkin menggunakan $ekstensi POSIX untuk penentu format format. Oleh karena itu mencari string format asli untuk penspesifikasi format itu sendiri tidak mudah.
Aspek lokalisasi juga berarti bahwa saya tidak dapat dengan mudah memecah string format menjadi beberapa panggilan snprintf.
Oleh karena itu cara paling mudah untuk menemukan indeks di sekitar substitusi tertentu adalah dengan melakukan:
Saya akan memberi Anda +1 untuk case use. Tetapi Anda akan gagal audit, jadi Anda mungkin harus menemukan cara lain untuk menandai awal dan akhir teks tebal. Sepertinya tiga snprintfsambil memeriksa nilai kembali akan berfungsi dengan baik karena snprintfmengembalikan jumlah karakter yang ditulis. Mungkin sesuatu seperti: int begin = snprintf(..., "blah blah %s %f yada yada", ...);dan int end = snprintf(..., "%s", ...);kemudian ekor: snprintf(..., "blah blah");.
jww
3
@jww Masalah dengan beberapa snprintfpanggilan adalah bahwa penggantian mungkin diatur ulang di lokal lain, sehingga mereka tidak dapat dipecah seperti itu.
jamesdlin
Terima kasih untuk contohnya. Tapi tidak bisakah Anda, seperti, menulis urutan kontrol terminal untuk membuat output tebal tepat sebelum bidang dan kemudian menulis urutan setelahnya? Jika Anda tidak melakukan hardcode pada urutan kontrol terminal, Anda dapat membuatnya juga posisional (dapat dipesan ulang).
PSkocik
1
@PSkocik Jika Anda mengeluarkan ke terminal. Jika Anda bekerja dengan, katakanlah, kontrol edit kaya Win32, itu tidak akan membantu kecuali jika Anda ingin kembali dan mem-parsing urutan kontrol terminal sesudahnya. Itu juga mengasumsikan bahwa Anda ingin menghormati urutan kontrol terminal di sisa teks yang diganti; jika tidak, maka Anda harus memfilter atau menghindarinya. Saya tidak mengatakan bahwa tidak mungkin melakukannya tanpa %n; Saya mengklaim bahwa menggunakan %nlebih mudah daripada alternatif.
jamesdlin
1
Itu tidak mencetak apa pun. Ini digunakan untuk mengetahui berapa banyak karakter yang dicetak sebelum %nmuncul dalam format string, dan output ke int yang disediakan:
#include<stdio.h>int main(int argc,char* argv[]){int resultOfNSpecifier =0;
_set_printf_count_output(1);/* Required in visual studio */
printf("Some format string%n\n",&resultOfNSpecifier);
printf("Count of chars before the %%n: %d\n", resultOfNSpecifier);return0;}
ketika saya mencoba kode Anda itu memberi saya: Karakter Hello World dicetak sejauh ini = 36 ,,,,, mengapa 36?! Saya menggunakan GCC 32bit di mesin windows.
%nada di C89. Itu tidak bekerja dengan MSVC karena Microsoft menonaktifkannya secara default untuk masalah keamanan; Anda harus menelepon _set_printf_count_outputdulu untuk mengaktifkannya. (Lihat jawaban Merlyn Morgan-Graham.)
Anda salah besar. Itu tercantum dengan jelas di Tabel B-1 ( printfkonversi) dari Lampiran B dari K&R, edisi ke-2. (Halaman 244 salinan saya.) Atau lihat bagian 7.9.6.1 (halaman 134) dari spesifikasi ISO C90.
%n
membuatprintf
Turing-selesai secara tidak sengaja dan Anda dapat misalnya mengimplementasikan Brainfuck di dalamnya, lihat github.com/HexHive/printbf dan oilshell.org/blog/2019/02/07.html#appendix-a-minor-sublanguagesJawaban:
Tidak ada yang dicetak. Argumen harus berupa penunjuk ke int yang ditandatangani, tempat jumlah karakter yang ditulis sejauh ini disimpan.
Kode sebelumnya dicetak:
sumber
int
selalu masuk.n format specifies disabled
. Apa alasannya?Sebagian besar jawaban ini menjelaskan apa yang
%n
dilakukan (yaitu untuk mencetak apa-apa dan untuk menulis jumlah karakter yang dicetak sejauh ini keint
variabel), tetapi sejauh ini tidak ada yang benar-benar memberikan contoh dari apa yang digunakan . Ini satu:akan dicetak:
dengan Foo and Bar sejajar. (Sangat sepele untuk melakukannya tanpa menggunakan
%n
untuk contoh khusus ini, dan secara umum seseorang selalu dapat memutuskanprintf
panggilan pertama :Apakah sedikit menambah kenyamanan layak menggunakan sesuatu seperti esoteris
%n
(dan mungkin memperkenalkan kesalahan) terbuka untuk diperdebatkan.)sumber
&n
adalah pointer (&
adalah alamat-operator); sebuah pointer diperlukan karena C adalah nilai by-pass, dan tanpa sebuah pointer,printf
tidak dapat mengubah nilain
. The%*s
penggunaan dalamprintf
format string mencetak%s
specifier (dalam hal ini string kosong""
) menggunakan lebar lapangan darin
karakter. Penjelasanprintf
prinsip - prinsip dasar pada dasarnya di luar ruang lingkup pertanyaan ini (dan jawaban); Saya akan merekomendasikan membacaprintf
dokumentasi atau mengajukan pertanyaan Anda sendiri pada SO.Saya belum benar-benar melihat banyak penggunaan
%n
specifier yang praktis di dunia nyata , tetapi saya ingat bahwa itu digunakan pada kerentanan oldfool printf dengan serangan format string beberapa waktu yang lalu.Sesuatu yang seperti ini
di mana pengguna jahat dapat mengambil keuntungan dari parameter nama pengguna yang diteruskan ke printf sebagai string format dan menggunakan kombinasi
%d
,%c
atau w / e untuk pergi melalui tumpukan panggilan dan kemudian memodifikasi variabel yang diotorisasi ke nilai sebenarnya.Ya itu penggunaan esoteris, tetapi selalu berguna untuk mengetahui saat menulis daemon untuk menghindari celah keamanan? : D
sumber
%n
menghindari menggunakan string input yang tidak dicentang sebagaiprintf
string format.Dari sini kita melihat bahwa ia menyimpan jumlah karakter yang dicetak sejauh ini.
Contoh penggunaannya adalah:
n_chars
maka akan memiliki nilai12
.sumber
Sejauh ini semua jawaban tentang itu
%n
tidak, tetapi tidak mengapa ada orang yang menginginkannya sejak awal. Saya menemukan ini agak berguna dengansprintf
/snprintf
, ketika Anda mungkin perlu memecah atau memodifikasi string yang dihasilkan, karena nilai yang disimpan adalah indeks array ke dalam string yang dihasilkan. Aplikasi ini jauh lebih berguna, dengansscanf
, terutama karena fungsi dalamscanf
keluarga tidak mengembalikan jumlah karakter yang diproses tetapi jumlah bidang.Penggunaan lain yang benar-benar meretas adalah mendapatkan pseudo-log10 secara gratis pada saat yang sama saat mencetak nomor sebagai bagian dari operasi lain.
sumber
%n
, meskipun saya mohon berbeda tentang "semua jawaban ...". = P%n
benar-benar nol bahaya kerentanan terhadap penyerang. Kekeliruan yang salah tempat itu%n
sebenarnya berasal dari praktik bodoh mengirimkan string pesan daripada string format sebagai argumen format. Situasi ini tentu saja tidak pernah muncul ketika%n
sebenarnya merupakan bagian dari format string yang sedang digunakan.*p = f();
. Mengapa harus%n
, yang hanya merupakan cara lain untuk menulis hasil ke objek yang ditunjuk oleh pointer, dianggap "berbahaya", daripada menganggap pointer itu sendiri berbahaya?Argumen yang terkait dengan
%n
akan diperlakukan sebagaiint*
dan diisi dengan jumlah total karakter yang dicetak pada saat itu diprintf
.sumber
Suatu hari saya menemukan diri saya dalam situasi di mana
%n
dengan baik akan menyelesaikan masalah saya. Tidak seperti jawaban saya sebelumnya , dalam hal ini, saya tidak dapat menemukan alternatif yang baik.Saya memiliki kontrol GUI yang menampilkan beberapa teks tertentu. Kontrol ini dapat menampilkan bagian teks itu dengan huruf tebal (atau miring, atau bergaris bawah, dll.), Dan saya bisa menentukan bagian mana dengan menentukan indeks karakter awal dan akhir.
Dalam kasus saya, saya membuat teks dengan kontrol
snprintf
, dan saya ingin salah satu dari substitusi dibuat tebal. Menemukan indeks awal dan akhir untuk substitusi ini tidak sepele karena:String berisi banyak pergantian, dan salah satu dari pergantian tersebut adalah sewenang-wenang, teks yang ditentukan pengguna. Ini berarti bahwa melakukan pencarian teks untuk substitusi yang saya pedulikan berpotensi ambigu.
String format mungkin dilokalkan, dan mungkin menggunakan
$
ekstensi POSIX untuk penentu format format. Oleh karena itu mencari string format asli untuk penspesifikasi format itu sendiri tidak mudah.Aspek lokalisasi juga berarti bahwa saya tidak dapat dengan mudah memecah string format menjadi beberapa panggilan
snprintf
.Oleh karena itu cara paling mudah untuk menemukan indeks di sekitar substitusi tertentu adalah dengan melakukan:
sumber
snprintf
sambil memeriksa nilai kembali akan berfungsi dengan baik karenasnprintf
mengembalikan jumlah karakter yang ditulis. Mungkin sesuatu seperti:int begin = snprintf(..., "blah blah %s %f yada yada", ...);
danint end = snprintf(..., "%s", ...);
kemudian ekor:snprintf(..., "blah blah");
.snprintf
panggilan adalah bahwa penggantian mungkin diatur ulang di lokal lain, sehingga mereka tidak dapat dipecah seperti itu.%n
; Saya mengklaim bahwa menggunakan%n
lebih mudah daripada alternatif.Itu tidak mencetak apa pun. Ini digunakan untuk mengetahui berapa banyak karakter yang dicetak sebelum
%n
muncul dalam format string, dan output ke int yang disediakan:( Dokumentasi untuk
_set_printf_count_output
)sumber
Ini akan menyimpan nilai jumlah karakter yang dicetak sejauh ini dalam
printf()
fungsi itu.Contoh:
Output dari program ini adalah
sumber
% n adalah C99, berfungsi tidak dengan VC ++.
sumber
%n
ada di C89. Itu tidak bekerja dengan MSVC karena Microsoft menonaktifkannya secara default untuk masalah keamanan; Anda harus menelepon_set_printf_count_output
dulu untuk mengaktifkannya. (Lihat jawaban Merlyn Morgan-Graham.)printf
konversi) dari Lampiran B dari K&R, edisi ke-2. (Halaman 244 salinan saya.) Atau lihat bagian 7.9.6.1 (halaman 134) dari spesifikasi ISO C90._set_printf_count_output