Apa tips umum yang Anda miliki untuk bermain golf di C? Saya mencari ide yang dapat diterapkan pada masalah kode golf secara umum yang setidaknya agak spesifik untuk C (mis. "Hapus komentar" bukan jawaban). Silakan kirim satu tip per jawaban. Juga, harap sertakan jika tip Anda berlaku untuk C89 dan / atau C99 dan jika itu hanya bekerja pada kompiler tertentu.
137
Jawaban:
Gunakan bitwise XOR untuk memeriksa ketimpangan antar integer:
if(a^b)
bukannyaif(a!=b)
menyimpan 1 karakter.sumber
a-b
memberi Anda efek yang sama.a*b
sebagai gantinyaa&&b
(memiliki prioritas berbeda, mungkin atau mungkin tidak buruk). Jika Anda tahu a / = -b (misalnya mereka tidak ditandai) makaa||b
==a+b
?:
(bukan jika): untuk contoh hanya melakukan sesuatu jika berbeda:a^b?_diff_:;
?:
operator yang setara dengana ? a : b
main
Daftar argumen penyalahgunaan untuk mendeklarasikan satu atau lebih variabel integer:(jawaban untuk Alfabet dalam bahasa pemrograman )
Solusi ini juga menyalahgunakan fakta bahwa
a
(aliasargc
) dimulai sebagai1
, asalkan program dipanggil tanpa argumen.Gunakan variabel global untuk menginisialisasi hal ke nol:
(jawaban untuk Golf Kode Anagram! )
sumber
Operator koma dapat digunakan untuk mengeksekusi banyak ekspresi dalam satu blok sekaligus menghindari kawat gigi:
Output:
1 2
sumber
break
.break
pernyataan, dan jawaban ini berbicara tentang ekspresi.Hindari deklarasi tipe argumen fungsi-bencana
Jika Anda mendeklarasikan fungsi di mana kelima argumen adalah
int
s, maka hidup itu baik. Anda cukup menulisTetapi anggaplah
d
perlu menjadichar
, atau bahkan sebuahint*
. Maka Anda kacau! Jika satu parameter didahului oleh suatu tipe, semuanya harus:Tapi tunggu! Ada cara di sekitar ledakan bencana karakter yang tidak berguna ini. Bunyinya seperti ini:
Ini bahkan menghemat
main
deklarasi standar jika Anda perlu menggunakan argumen baris perintah:dua byte lebih pendek dari
Saya terkejut menemukan ini, karena sejauh ini saya belum menemukannya di PPCG.
sumber
-std=gnu99
dan sekarang Anda tidak portabel. Di CLC-Speak, Anda bahkan tidak menulis kode "C", tetapi "Gnu99-C". 'Sekitar sini kita sebagian besar mengabaikan itu, tetapi ada baiknya menyebutkannya jika Anda memposting kode yang spesifik compiler. Terkadang orang benar - benar mengunduh dan menjalankan program-program kita ini. :)-std=c89
untuk memberitahu gcc atau dentang untuk mengkompilasi kode Anda sesuai dengan standar yang lebih lama, yang memungkinkan int implisit hanya dengan peringatan.Alih-alih> = dan <= Anda cukup menggunakan pembagian integer (/) ketika nilai yang dibandingkan di atas nol, yang menyimpan satu karakter. Sebagai contoh:
Yang tentu saja masih menyusut, misalnya menggunakan hanya> dan ^ (cara cerdas untuk menghindari penulisan && atau || dalam beberapa kasus).
Trik pembagian integer misalnya berguna untuk memutuskan apakah angka kurang dari 100, karena ini menyimpan karakter:
Ini juga baik dalam kasus-kasus ketika prioritas lebih tinggi diperlukan.
sumber
putchar(c>31&c<127?c:46);
Kompiler tertentu, seperti GCC, memungkinkan Anda untuk menghilangkan
#include
tipe dasar , param, dan mengembalikanmain
.Berikut ini adalah program C89 dan C99 yang valid yang mengkompilasi (dengan peringatan) dengan GCC:
Perhatikan bahwa
#include
untuk stdio.h hilang, jenis kembalinya untukmain
hilang, dan deklarasi tipe untuki
hilang.sumber
printf()
(atau fungsi variadik apa pun) tanpa prototipe menyebabkan perilaku yang tidak terdefinisi . GCC tidak mengkompilasi standar C secara default. Jika Anda memanggil gcc dalam mode C89 (gcc -ansi -pedantic
) atau mode C99 (gcc -std=c99 -pedantic
), Anda akan mendapatkan beberapa keluhan, setidaknya dalam kasus terakhir.Operator kondisional terner
?:
sering dapat digunakan sebagai berdiri di untuk sederhanaif
-else
pernyataan di tabungan yang cukup.Berbeda dengan c ++ , operator tidak secara formal menghasilkan nilai , tetapi beberapa kompiler (terutama gcc) akan membiarkan Anda melakukannya, yang merupakan bonus yang bagus.
sumber
&&
dan||
juga dapat digunakan:if(x==3)f()
menjadi dengan saran Andax==3?f():0
, dan dapat lebih ditingkatkanx==3&&f()
. Tapi hati-hati dengan prioritas operator - jikaf()
diganti dengany=1
, maka&&
solusinya memerlukan kurung tambahan.?:
menghasilkan nilai yang tinggi. Bisakah saya menggunakannya dalam kode produksi? lolx==3&&f()
bisa bermain golf lebih lanjut kex^3||f()
http://graphics.stanford.edu/~seander/bithacks.html
Bit itu bagus.
Tetapi dengan presedensi yang berbeda, dan jangan ubah x seperti ++ dan -. Anda juga dapat menggunakan ini dalam kasus yang sangat spesifik: ~ 9 lebih pendek dari -10.
Itu lebih esoteris, tetapi saya punya kesempatan untuk menggunakannya. Jika Anda tidak peduli tentang hubungan arus pendek
Juga:
sumber
(x/y) == (x>=y)
) sangat berguna.Gunakan lambdas (tidak dapat diangkut)
Dari pada
atau (hanya gcc)
atau (llvm dengan dukungan balok)
coba sesuatu seperti
... di mana string yang dikutip berisi instruksi bahasa mesin dari fungsi "lambda" Anda (sesuai dengan semua persyaratan platform ABI).
Ini berfungsi di lingkungan di mana konstanta string ditandai dapat dieksekusi. Secara default ini benar di Linux dan OSX tetapi tidak di Windows.
Salah satu cara konyol untuk belajar menulis fungsi "lambda" Anda sendiri adalah menulis fungsi dalam C, mengompilasinya, memeriksanya dengan sesuatu seperti
objdump -D
dan menyalin kode hex yang sesuai ke dalam sebuah string. Sebagai contoh,... ketika dikompilasi dengan
gcc -Os -c
untuk target Linux x86_64 menghasilkan sesuatu sepertiGNU CC
goto
:Anda dapat memanggil "fungsi lambda" ini secara langsung tetapi jika kode yang Anda panggil tidak mengambil parameter dan tidak akan kembali, Anda dapat menggunakan
goto
untuk menyimpan beberapa byte. Jadi, bukannyaatau (jika lingkungan Anda tidak memiliki mesin terbang Arab)
Mencoba
atau
Dalam contoh ini,
eb fe
adalah bahasa mesin x86 untuk sesuatu sepertifor(;;);
dan merupakan contoh sederhana dari sesuatu yang tidak mengambil parameter dan tidak akan kembali :-)Ternyata Anda bisa
goto
kode yang kembali ke induk panggilan.Contoh di atas (mungkin dikompilasi dan dijalankan di Linux dengan
gcc -O
) sensitif terhadap tata letak tumpukan.EDIT: Bergantung pada rantai alat Anda, Anda mungkin harus menggunakan
-zexecstack
flag kompilasi.Jika tidak segera terlihat, jawaban ini terutama ditulis untuk lol. Saya tidak bertanggung jawab untuk bermain golf yang lebih baik atau lebih buruk atau hasil psikologis yang merugikan dari membaca ini.
sumber
Gunakan kursor bukan pointer. Rebut
brk()
di awal dan gunakan sebagai basis-pointer .Kemudian buat #define untuk akses memori.
M
menjadi postfix yang*
diterapkan ke bilangan bulat. (Trik [x] == x [a] lama.)Tapi, masih ada lagi! Kemudian Anda bisa memiliki pointer pointer dan mengembalikan fungsi yang lebih pendek dari makro (terutama jika Anda menyingkat 'return'):
Untuk membuat kursor dari sebuah pointer, Anda mengurangi pointer-basis, menghasilkan ptrdiff_t, yang memotong menjadi int, kerugian adalah milik Anda.
Teknik ini digunakan dalam jawaban saya untuk Menulis penerjemah untuk kalkulus lambda yang tidak diketik .
sumber
Tentukan parameter, bukan variabel.
f(x){int y=x+1;...}
f(x,y){y=x+1;...}
Anda tidak perlu melewati parameter kedua.
Anda juga dapat menggunakan prioritas operator untuk menyimpan tanda kurung.
Misalnya
(x+y)*2
bisa jadix+y<<1
.sumber
x+y*2
, menyimpan lagi arang.x+y*2
tidak sama, karena prioritas operator.x+y<<1
contoh, dengan asumsi itu sedang dievaluasix+(y<<1)
, dan menyarankan yang*2
sebaliknya. Saya tidak tahu operasi bitshift dievaluasi sebagai contoh(x+y)<<2
Karena biasanya
EOF == -1
, gunakan operator bitwise TIDAK untuk memeriksa EOF:while(~(c=getchar()))
atauwhile(c=getchar()+1)
dan memodifikasi nilai c di setiap tempatsumber
while(1+c=getchar())
berhasil?+
memiliki prioritas lebih tinggi daripada operator penugasan=
, sehingga1+c=getchar()
setara dengan(1+c)=getchar()
, yang tidak dikompilasi karena(1+c)
bukan nilai.Operator ternary
?:
tidak biasa karena memiliki dua bagian yang terpisah. Karena itu, ini memberikan sedikit celah untuk aturan prioritas operator standar. Ini berguna untuk menghindari tanda kurung.Ambil contoh berikut:
Pendekatan golf yang biasa adalah untuk mengganti
if
dengan&&
, tetapi karena prioritas rendah dari operator koma, Anda memerlukan sepasang tanda kurung tambahan:Bagian tengah operator ternary tidak membutuhkan tanda kurung, meskipun:
Komentar serupa berlaku untuk subscript array.
sumber
b-=a=b
bahkan lebih pendek. The?:
trick masih membantu,-=
karena juga memiliki preferensi yang rendah.x>0||(y=3)
,x>0?0:(y=3)
tidak berguna, tetapix<1?y=3:0
melakukan pekerjaan.x>5?:y=1
Bagian mana pun dari kode Anda yang berulang beberapa kali adalah kandidat untuk penggantian dengan pra-prosesor.
adalah kasus penggunaan yang sangat umum jika kode Anda melibatkan lebih dari beberapa fungsi. Kata kunci gondrong lainnya seperti
while
,double
,switch
, dancase
juga calon; serta segala sesuatu yang idomatis dalam kode Anda.Saya biasanya memesan karakter huruf besar untuk tujuan ini.
sumber
-DR=return
. Perhatikan bahwa jika Anda memasukkan karakter tertentu, mungkin perlu memiliki tanda kutip tunggal atau ganda di sekitar define-DP='puts("hello")'
.Jika program Anda membaca atau menulis pada satu di setiap langkah dasar selalu mencoba menggunakan fungsi baca dan tulis alih-alih getchar () dan putchar () .
Contoh ( Membalikkan stdin dan menempatkan pada stdout )
Latihan: Gunakan teknik ini untuk mendapatkan skor yang bagus di sini .
sumber
Membalikkan loop
Jika Anda bisa, coba ganti
dengan
sumber
Jika Anda perlu mengeluarkan satu karakter baris baru (
\n
), jangan gunakanputchar(10)
, gunakanputs("")
.sumber
Manfaatkan nilai kembali ke nol hal. Jika Anda memanggil beberapa fungsi, dan fungsi itu mengembalikan nol dalam kondisi normal, maka Anda dapat menempatkannya di lokasi di mana nol diharapkan. Begitu juga jika Anda tahu fungsinya akan kembali non-nol, dengan tambahan bang. Bagaimanapun, Anda tidak melakukan penanganan kesalahan yang tepat dalam kode golf dalam hal apa pun, bukan?
Contoh:
sumber
Tetapkan alih-alih kembali.
Ini sebenarnya bukan C standar, tetapi bekerja dengan setiap kompiler dan CPU yang saya tahu:
memiliki efek yang sama dengan:
Karena argumen pertama disimpan ke register CPU yang sama dengan nilai pengembalian.
Catatan: Seperti disebutkan dalam satu komentar, ini adalah perilaku yang tidak terdefinisi dan tidak dijamin bekerja untuk setiap operasi. Dan setiap optimasi kompiler hanya akan melewatinya.
X-Macro
Fitur lain yang bermanfaat: X-Macro dapat membantu Anda ketika Anda memiliki daftar variabel dan Anda perlu melakukan beberapa operasi yang melibatkan semuanya:
https://en.wikipedia.org/wiki/X_Macro
sumber
-O0
selalu memilih untuk mengevaluasi ekspresi dalam register nilai-kembali. Saya telah melihat x86, ARM, dan MIPS setidaknya (di gcc.godbolt.org ), dan gcc tampaknya berusaha keras untuk melakukannya di-O0
. Tapi ingat jika Anda mengambil keuntungan dari ini, bahasa yang Anda pemrograman di yaitugcc -O0
, tidak C , dan Anda harus label jawaban Anda sesuai, bukan sebagai C . Gagal pada level optimasi apa pun selain-O0
mode debug, dan tidak berfungsi dengan clang IIRC.Gunakan
*a
alih-aliha[0]
untuk mengakses elemen pertama array.Operator relasional (
!=
,>
, dll) memberikan0
atau1
. Gunakan ini dengan operator aritmatika untuk memberikan offset yang berbeda tergantung pada apakah kondisinya benar atau salah:a[1+2*(i<3)]
akan mengaksesa[1]
jikai >= 3
dana[3]
sebaliknya.sumber
a[i<3?3:1]
dua karakter lebih pendek daria[1+2*(i<3)]
.Anda dapat melihat arsip IOCCC (kontes kode C internasional yang dikaburkan).
Salah satu trik penting adalah #define macro yang ekspansi nya memiliki kurung kurawal / kurung yang tidak seimbang
sumber
#define P;printf(
.for(int i=0;i<n;i++){a(i);b(i);}
dapat dibuat lebih pendek beberapa cara:for(int i=0;i<n;){a(i);b(i++);}
-1 untuk memindahkan++
ke yang terakhiri
dalam loopfor(int i=0;i<n;b(i++))a(i);
-3 lagi untuk memindahkan semua kecuali satu pernyataan ke atas dan keluar dari loop utama, menghapus kawat gigisumber
Bersikap fungsional!
Jika Anda dapat mengurangi masalah Anda menjadi fungsi-fungsi sederhana dengan tanda tangan yang sama dan didefinisikan sebagai ekspresi tunggal, maka Anda dapat melakukan lebih baik daripada
#define r return
dan memfaktorkan hampir semua boilerplate untuk mendefinisikan suatu fungsi.Hasil program adalah nilai statusnya dikembalikan ke OS atau mengendalikan shell atau IDE.
Menggunakan
__VA_ARGS__
memungkinkan Anda untuk menggunakan operator koma untuk memperkenalkan titik-titik urutan dalam ekspresi fungsi ini . Jika ini tidak diperlukan, makro bisa lebih pendek.sumber
gunakan
scanf("%*d ");
untuk membaca input dummy. (Dalam hal input tidak berarti dalam program lebih lanjut) itu lebih pendek daripada discanf("%d",&t);
mana Anda juga perlu mendeklarasikan variabel t.menyimpan karakter dalam array int jauh lebih baik daripada array karakter. contoh.
s[],t;main(c){for(scanf("%*d ");~(c=getchar());s[t++]=c)putchar(s[t]);}
sumber
%*d
tidak hanya dalam Golf karena itu juga berguna dalam situasi di mana seseorang akan, misalnya, ingin melewatkan baris baru discanf("%[^\n]%*c",str);
:)Cetak karakter lalu carriage return, alih-alih:
atau
sederhana, nyatakan c sebagai int dan:
sumber
puts(&c)
bekerja? Itu tidak harus diakhiri dengan nol.char *
, kita melihat string tunggal: karakter c , diikuti oleh byte nol.Menggunakan
asprintf()
menghemat alokasi eksplisit dan juga mengukur panjang string aliaschar*
! Ini mungkin tidak terlalu berguna untuk kode golf, tetapi memudahkan pekerjaan sehari-hari dengan array arang. Ada menyarankan beberapa lebih baik dalam abad ke-21 C .Contoh penggunaan:
sumber
import
jika kamu harusSeperti disebutkan dalam jawaban pertama , beberapa kompiler (terutama, GCC dan dentang) memungkinkan Anda untuk menghilangkan
#include
s untuk fungsi perpustakaan standar.Sekalipun Anda tidak bisa begitu saja melepasnya
#include
, mungkin ada cara lain untuk menghindarinya , tetapi itu tidak selalu praktis atau khususnya golf.Dalam kasus yang tersisa, Anda dapat menggunakan
#import<header file>
alih-alih#include<header file>
menyimpan byte. Ini adalah ekstensi GNU dan dianggap sudah usang, tetapi berfungsi setidaknya di gcc 4.8, gcc 5.1, dan clang 3.7.sumber
Coba
cpow()
alih-alihcos()
Dari pada
coba sesuatu seperti
Ini menggunakan rumus Euler , analisis kompleks kecil dan pengamatan yang menetapkan kompleks untuk menghasilkan ganda bagian nyata (hati-hati dengan panggilan fungsi variadic dan seluk-beluk lainnya).
Trik jenis ini dapat digunakan untuk mengurangi
ke
sumber
Berikut adalah beberapa tips yang saya gunakan untuk keuntungan saya. Saya tanpa malu-malu mencuri mereka dari orang lain, jadi kredit untuk siapa pun kecuali saya:
Gabungkan tugas dengan panggilan fungsi
Alih-alih ini:
Melakukan hal ini:
Inisialisasi beberapa variabel secara bersamaan (bila mungkin)
Alih-alih ini:
Melakukan hal ini:
Ciutkan nilai nol / bukan nol
Ini adalah trik rapi yang saya ambil dari seseorang di sini (tidak ingat siapa, maaf). Ketika Anda memiliki nilai integer dan Anda perlu menciutkannya menjadi 1 atau 0, Anda bisa menggunakannya
!!
untuk melakukannya dengan mudah. Ini kadang menguntungkan untuk alternatif lain seperti?:
.Ambil situasi ini:
Anda bisa melakukan ini:
Contoh lain:
Dapat ditulis ulang sebagai:
sumber
R*-~!!mxxxx
Mengetahui persamaan logis dasar mungkin dapat menyimpan beberapa byte. Misalnya, alih-
if (!(a&&b)){}
alih coba gunakan hukum DeMorganif (!a||!b){}
. Hal yang sama berlaku untuk fungsi bitwise: alih-alih~(a|b)
melakukannya~a&~b
.sumber