Saya telah menemukan C
kode yang mencetak dari 1 hingga 1000 tanpa loop atau kondisi : Tapi saya tidak mengerti cara kerjanya. Adakah yang bisa membaca kode dan menjelaskan setiap baris?
#include <stdio.h>
#include <stdlib.h>
void main(int j) {
printf("%d\n", j);
(&main + (&exit - &main)*(j/1000))(j+1);
}
c
function-pointers
ob_dev
sumber
sumber
main
C ++.Jawaban:
Jangan pernah menulis kode seperti itu.
Karena
j<1000
,j/1000
adalah nol (pembagian integer). Begitu:setara dengan:
Yang mana:
Yang menelepon
main
denganj+1
.Jika
j == 1000
, maka baris yang sama keluar sebagai:Yang intinya adalah
Yang mana
exit(j+1)
dan meninggalkan program.(&exit)(j+1)
danexit(j+1)
pada dasarnya hal yang sama - mengutip C99 §6.3.2.1 / 4:exit
adalah penunjuk fungsi. Bahkan tanpa&
operator alamat- unary , itu diperlakukan sebagai pointer ke fungsi. (&
Hanya membuatnya eksplisit.)Dan panggilan fungsi dijelaskan dalam §6.5.2.2 / 1 dan berikut:
Jadi
exit(j+1)
berfungsi karena konversi otomatis dari tipe fungsi ke tipe pointer-to-function, dan(&exit)(j+1)
bekerja dengan konversi eksplisit ke tipe pointer-to-function.Yang sedang berkata, kode di atas tidak sesuai (
main
mengambil dua argumen atau tidak sama sekali), dan&exit - &main
, saya percaya, tidak terdefinisi sesuai dengan §6.5.6 / 9:Penambahan itu
(&main + ...)
akan berlaku dengan sendirinya, dan dapat digunakan, jika jumlah yang ditambahkan adalah nol, karena §6.5.6 / 7 mengatakan:Jadi menambahkan nol
&main
akan baik-baik saja (tapi tidak banyak digunakan).sumber
foo(arg)
dan(&foo)(arg)
setara, mereka memanggil foo dengan argumen arg. newty.de/fpt/fpt.html adalah halaman yang menarik tentang pointer fungsi.foo
adalah sebuah pointer,&foo
adalah alamat dari pointer itu. Dalam kasus kedua,foo
adalah array, dan&foo
setara dengan foo.((void(*[])()){main, exit})[j / 1000](j + 1);
&foo
tidak sama denganfoo
ketika datang ke array.&foo
adalah pointer ke array,foo
adalah pointer ke elemen pertama. Mereka memiliki nilai yang sama. Untuk fungsi,fun
dan&fun
keduanya adalah petunjuk fungsi.Ini menggunakan rekursi, aritmatika pointer, dan mengeksploitasi perilaku pembulatan divisi integer.
The
j/1000
putaran jangka turun ke 0 untuk semuaj < 1000
; sekalij
mencapai 1000, itu dievaluasi menjadi 1.Sekarang jika Anda memiliki
a + (b - a) * n
, di manan
0 atau 1, Anda berakhir dengana
jikan == 0
, danb
jikan == 1
. Menggunakan&main
(alamatmain()
) dan&exit
untuka
danb
, istilah(&main + (&exit - &main) * (j/1000))
kembali&main
ketika dij
bawah 1000,&exit
jika tidak. Pointer fungsi yang dihasilkan kemudian diumpankan argumenj+1
.Keseluruhan konstruksi ini menghasilkan perilaku rekursif: sementara di
j
bawah 1000,main
menyebut dirinya secara rekursif; ketikaj
mencapai 1000, ia memanggilexit
sebaliknya, membuat program keluar dengan kode keluar 1001 (yang agak kotor, tetapi berfungsi).sumber
exit
, yang mengambil kode keluar sebagai argumennya dan, yah, keluar dari proses saat ini. Pada titik itu, j adalah 1000, jadi j + 1 sama dengan 1001, yang menjadi kode keluar.