Mengapa kode ini memberikan hasil C++Sucks
? Apa konsep di baliknya?
#include <stdio.h>
double m[] = {7709179928849219.0, 771};
int main() {
m[1]--?m[0]*=2,main():printf((char*)m);
}
Uji di sini .
c
deobfuscation
codeslayer1
sumber
sumber
skcuS++C
.Jawaban:
Nomor tersebut
7709179928849219.0
memiliki representasi biner berikut sebagai 64-bitdouble
:+
menunjukkan posisi tanda;^
dari eksponen, dan-
mantissa (yaitu nilai tanpa eksponen).Karena representasi menggunakan eksponen biner dan mantissa, menggandakan jumlah akan menambah eksponen dengan satu. Program Anda melakukannya tepat 771 kali, sehingga eksponen yang dimulai pada 1075 (representasi desimal
10000110011
) menjadi 1075 + 771 = 1846 pada akhirnya; representasi biner tahun 1846 adalah11100110110
. Pola yang dihasilkan terlihat seperti ini:Pola ini sesuai dengan string yang Anda lihat dicetak, hanya mundur. Pada saat yang sama, elemen kedua array menjadi nol, memberikan terminator nol, membuat string cocok untuk diteruskan
printf()
.sumber
7709179928849219
nilainya, dan mendapatkan representasi biner kembali.Versi yang lebih mudah dibaca:
Itu secara rekursif memanggil
main()
771 kali.Pada awalnya,
m[0] = 7709179928849219.0
yang berdiri untukC++Suc;C
. Dalam setiap panggilan,m[0]
digandakan, untuk "memperbaiki" dua huruf terakhir. Dalam panggilan terakhir,m[0]
berisi representasi karakter ASCIIC++Sucks
danm[1]
hanya berisi nol, sehingga memiliki terminator nol untukC++Sucks
string. Semua dalam asumsi yangm[0]
disimpan pada 8 byte, sehingga masing-masing karakter membutuhkan 1 byte.Tanpa rekursi dan
main()
panggilan ilegal akan terlihat seperti ini:sumber
Penafian: Jawaban ini diposting ke bentuk asli dari pertanyaan, yang hanya menyebutkan C ++ dan menyertakan header C ++. Konversi pertanyaan menjadi murni C dilakukan oleh komunitas, tanpa masukan dari penanya semula.
Secara formal, tidak mungkin untuk beralasan tentang program ini karena programnya tidak lengkap (artinya, ini bukan C ++ legal) Itu melanggar C ++ 11 [basic.start.main] p3:
Selain itu, ini bergantung pada fakta bahwa pada komputer konsumen tipikal,
double
panjangnya 8 byte, dan menggunakan representasi internal tertentu yang terkenal. Nilai awal array dihitung sehingga ketika "algoritma" dilakukan, nilai akhir yang pertamadouble
akan sedemikian rupa sehingga representasi internal (8 byte) akan menjadi kode ASCII dari 8 karakterC++Sucks
. Elemen kedua dalam array adalah0.0
byte pertama yang berada0
dalam representasi internal, menjadikannya string gaya C yang valid. Ini kemudian dikirim ke keluaran menggunakanprintf()
.Menjalankan ini di HW di mana beberapa hal di atas tidak berlaku akan menghasilkan teks sampah (atau bahkan mungkin akses di luar batas) sebagai gantinya.
sumber
basic.start.main
3.6.1 / 3 dengan kata-kata yang sama.main()
, atau menggantinya dengan panggilan API untuk memformat harddisk, atau apa pun.Mungkin cara termudah untuk memahami kode adalah bekerja melalui hal-hal secara terbalik. Kami akan mulai dengan string untuk mencetak - untuk keseimbangan, kami akan menggunakan "C ++ Rocks". Poin penting: sama seperti aslinya, panjangnya persis delapan karakter. Karena kita akan melakukan (kira-kira) seperti aslinya, dan mencetaknya dalam urutan terbalik, kita akan mulai dengan meletakkannya dalam urutan terbalik. Untuk langkah pertama kami, kami hanya akan melihat pola bit itu sebagai
double
, dan mencetak hasilnya:Ini menghasilkan
3823728713643449.5
. Jadi, kami ingin memanipulasi itu dengan cara yang tidak jelas, tetapi mudah untuk dibalik. Saya akan semi-sewenang-wenang memilih perkalian dengan 256, yang memberi kita978874550692723072
. Sekarang, kita hanya perlu menulis beberapa kode yang dikaburkan untuk dibagi dengan 256, lalu mencetak masing-masing byte dengan urutan terbalik:Sekarang kami memiliki banyak casting, memberikan argumen kepada (rekursif)
main
yang sepenuhnya diabaikan (tetapi evaluasi untuk mendapatkan kenaikan dan penurunan sangat penting), dan tentu saja angka yang benar-benar sewenang-wenang mencari untuk menutupi fakta bahwa apa yang kita lakukan benar-benar sangat mudah.Tentu saja, karena intinya adalah kebingungan, jika kita merasa kita dapat mengambil lebih banyak langkah juga. Sebagai contoh, kita dapat mengambil keuntungan dari evaluasi hubung singkat, untuk mengubah
if
pernyataan kita menjadi satu ekspresi, sehingga badan utama terlihat seperti ini:Bagi siapa pun yang tidak terbiasa dengan kode yang dikaburkan (dan / atau kode golf) ini mulai terlihat sangat aneh - menghitung dan membuang logika
and
beberapa angka floating point yang tidak berarti dan nilai pengembalian darimain
, yang bahkan tidak mengembalikan nilai. Lebih buruk lagi, tanpa menyadari (dan berpikir tentang) bagaimana evaluasi hubung singkat bekerja, bahkan mungkin tidak segera jelas bagaimana hal itu menghindari rekursi tak terbatas.Langkah kami berikutnya mungkin akan memisahkan mencetak setiap karakter dari menemukan karakter itu. Kita dapat melakukannya dengan cukup mudah dengan menghasilkan karakter yang tepat sebagai nilai pengembalian
main
, dan mencetak apa yangmain
dikembalikan:Setidaknya bagi saya, itu tampaknya cukup membingungkan, jadi saya akan berhenti di situ.
sumber
Itu hanya membangun array ganda (16 byte) yang - jika diartikan sebagai array char - membangun kode ASCII untuk string "C ++ Sucks"
Namun, kode ini tidak berfungsi pada setiap sistem, itu bergantung pada beberapa fakta tidak terdefinisi berikut:
sumber
Kode berikut dicetak
C++Suc;C
, jadi seluruh perkalian hanya untuk dua huruf terakhirsumber
Yang lain telah menjelaskan pertanyaan dengan cukup teliti, saya ingin menambahkan catatan bahwa ini adalah perilaku yang tidak terdefinisi menurut standar.
C ++ 11 3.6.1 / 3 Fungsi utama
sumber
Kode dapat ditulis ulang seperti ini:
Apa yang dilakukannya adalah menghasilkan satu set byte dalam
double
arraym
yang sesuai dengan karakter 'C ++ Sucks' diikuti oleh null-terminator. Mereka mengaburkan kode dengan memilih nilai ganda yang ketika digandakan 771 kali menghasilkan, dalam representasi standar, set byte dengan terminator nol yang disediakan oleh anggota kedua array.Perhatikan bahwa kode ini tidak akan berfungsi di bawah representasi endian yang berbeda. Selain itu, panggilan
main()
tidak diijinkan.sumber
f
pengembalian Andaint
?int
kembalinya dalam pertanyaan. Biarkan saya memperbaikinya.Pertama-tama kita harus ingat bahwa angka presisi ganda disimpan dalam memori dalam format biner sebagai berikut:
(i) 1 bit untuk tanda
(ii) 11 bit untuk eksponen
(iii) 52 bit untuk besarnya
Urutan bit menurun dari (i) ke (iii).
Pertama bilangan pecahan desimal dikonversi menjadi bilangan biner pecahan ekivalen dan kemudian dinyatakan sebagai urutan besarnya dalam biner.
Jadi angka 7709179928849219.0 menjadi
Sekarang sambil mempertimbangkan bit magnitudo 1. diabaikan karena semua urutan metode magnitudo akan dimulai dengan 1.
Jadi bagian besarnya menjadi:
Sekarang kekuatan 2 adalah 52 , kita perlu menambahkan angka bias sebagai 2 ^ (bit untuk eksponen -1) -1 yaitu 2 ^ (11 -1) -1 = 1023 , sehingga eksponen kita menjadi 52 + 1023 = 1075
Sekarang kode kita memutipkan angka dengan 2 , 771 kali yang membuat eksponen meningkat sebesar 771
Jadi eksponen kami adalah (1075 + 771) = 1846 yang setara binernya adalah (11100110110)
Sekarang angka kita positif sehingga bit tanda kita adalah 0 .
Jadi nomor kami yang diubah menjadi:
bit tanda + eksponen + magnitudo (penggabungan bit-bit sederhana)
karena m dikonversi menjadi pointer char kita akan membagi pola bit dalam potongan 8 dari LSD
(yang setara Hex adalah :)
Yang dari peta karakter seperti yang ditunjukkan adalah:
Sekarang setelah ini dibuat m [1] adalah 0 yang berarti karakter NULL
Sekarang dengan asumsi bahwa Anda menjalankan program ini pada mesin little-endian (bit urutan lebih rendah disimpan di alamat yang lebih rendah) jadi pointer m pointer ke bit alamat terendah dan kemudian melanjutkan dengan mengambil bit dalam chuck 8 (seperti tipe yang dicor ke char * ) dan printf () berhenti ketika menemukan 00000000 di chunck terakhir ...
Namun kode ini tidak portabel.
sumber