Jakie masz ogólne wskazówki na temat gry w golfa w C? Szukam pomysłów, które można by zastosować do ogólnych problemów z golfem, które są przynajmniej nieco specyficzne dla C (np. „Usuń komentarze” nie jest odpowiedzią). Proszę zamieścić jedną wskazówkę na odpowiedź. Podaj także, czy Twoja wskazówka dotyczy C89 i / lub C99 i czy działa tylko na niektórych kompilatorach.
137
Odpowiedzi:
Użyj bitowego XOR, aby sprawdzić nierówność między liczbami całkowitymi:
if(a^b)
zamiastif(a!=b)
ratuje 1 postać.źródło
a-b
daje ten sam efekt.a*b
zamiasta&&b
(ma różne preferencje, może, ale nie musi być zły). Jeśli znasz a / = -b (np. Nie są podpisani), toa||b
==a+b
?:
(zamiast jeśli): dla przykładu rób coś, jeśli coś jest inne:a^b?_diff_:;
?:
operatora, który jest po prostu równoważnya ? a : b
main
Lista argumentów nadużycia w celu zadeklarowania jednej lub więcej zmiennych całkowitych:(odpowiedź na alfabet w językach programowania )
To rozwiązanie narusza również fakt, że
a
(akaargc
) zaczyna się jako1
, pod warunkiem, że program jest wywoływany bez argumentów.Użyj zmiennych globalnych, aby zainicjować rzeczy do zera:
(odpowiedź na Anagram Code Golf! )
źródło
Za pomocą operatora przecinka można wykonywać wiele wyrażeń w jednym bloku, unikając nawiasów klamrowych:
Wyjścia:
1 2
źródło
break
.break
jest stwierdzeniem, a ta odpowiedź mówi o wyrażeniach.Unikaj katastrofalnych deklaracji typu argument-funkcja
Jeśli deklarujesz funkcję, w której wszystkie pięć argumentów jest
int
s, to życie jest dobre. możesz po prostu pisaćZałóżmy jednak, że
d
musi to byćchar
, a nawetint*
. Więc jesteś pieprzony! Jeśli jeden parametr jest poprzedzony typem, wszystkie muszą być:Ale poczekaj! Istnieje sposób na obejście tej katastrofalnej eksplozji bezużytecznych postaci. Wygląda to tak:
To nawet oszczędza na standardowej
main
deklaracji, jeśli musisz użyć argumentów wiersza poleceń:jest dwa bajty krótszy niż
Byłem zaskoczony odkryciem tego, ponieważ do tej pory nie spotkałem go na PPCG.
źródło
-std=gnu99
a teraz nie jesteś przenośny. W clc-speak nie piszesz nawet samego kodu „C”, ale „Gnu99-C”. „W tym miejscu najczęściej to ignorujemy, ale warto o tym wspomnieć, jeśli kod pocztowy jest specyficzny dla kompilatora. Czasami ludzie rzeczywiście nie pobrać i uruchomić te programy nasza. :)-std=c89
polecenia gcc lub clang, aby skompilowały kod zgodnie ze starszym standardem, który pozwala na domyślną int tylko z ostrzeżeniem.Zamiast> = i <= możesz po prostu użyć dzielenia liczb całkowitych (/), gdy porównywane wartości są powyżej zera, co oszczędza jeden znak. Na przykład:
Co oczywiście jest nadal możliwe do zmniejszenia, używając na przykład tylko> i ^ (sprytny sposób na uniknięcie pisania && lub || w niektórych przypadkach).
Sztuczka dzielenia liczb całkowitych jest na przykład przydatna do decydowania, czy liczba jest mniejsza niż 100, ponieważ zapisuje to znak:
Jest to również dobre w przypadkach, gdy potrzebny jest wyższy priorytet.
źródło
putchar(c>31&c<127?c:46);
Niektóre kompilatory, takie jak GCC, pozwalają pominąć podstawowe
#include
typy s, param i return dlamain
.Oto poprawny program C89 i C99, który kompiluje się (z ostrzeżeniami) z GCC:
Zauważ, że
#include
brakuje pliku for stdio.h, brakuje typu zwracanego przez for imain
brakuje deklaracji typu dlai
.źródło
printf()
(lub dowolną funkcję wariadyczną) bez prototypu powoduje niezdefiniowane zachowanie . GCC domyślnie nie kompiluje standardu C. Jeśli wywołasz gcc w trybie C89 (gcc -ansi -pedantic
) lub C99 (gcc -std=c99 -pedantic
), otrzymasz sporo skarg, przynajmniej w tym drugim przypadku.Trójskładnikowy operator warunkowy
?:
może być często używany jako zastępstwo dla prostychif
-else
wyciągów przy znacznych oszczędnościach.W przeciwieństwie do ekwiwalentu c ++ operator formalnie nie daje wartości , ale niektóre kompilatory (zwłaszcza gcc) pozwolą ci na ucieczkę, co jest niezłą premią.
źródło
&&
i||
może być również użyty:if(x==3)f()
staje się twoją sugestiąx==3?f():0
i może być dalej ulepszonyx==3&&f()
. Uważaj jednak na pierwszeństwo operatora - jeślif()
zostanie zastąpioney=1
,&&
rozwiązanie wymaga dodatkowego zestawu nawiasów.?:
daje wartość. Czy mogę tego użyć w kodzie produkcyjnym? lolx==3&&f()
można dalejx^3||f()
http://graphics.stanford.edu/~seander/bithacks.html
Bity są fajne.
Ale z różnymi priorytetami i nie zmieniaj x jak ++ i -. Możesz także użyć tego w naprawdę szczególnych przypadkach: ~ 9 jest krótsze niż -10.
To bardziej ezoteryczne, ale miałem okazję go użyć. Jeśli nie obchodzi Cię zwarcie
Również:
źródło
(x/y) == (x>=y)
) jest naprawdę przydatna.Użyj lambdas (nie można przenosić)
Zamiast
lub (tylko gcc)
lub (lvvm z obsługą bloków)
spróbuj czegoś takiego
... gdzie cytowany ciąg zawiera instrukcje języka maszynowego funkcji „lambda” (zgodne ze wszystkimi wymaganiami ABI platformy).
Działa to w środowiskach, w których stałe ciągów są oznaczone jako pliki wykonywalne. Domyślnie jest to prawdą w systemach Linux i OSX, ale nie w systemie Windows.
Jednym głupim sposobem nauczenia się pisania własnych funkcji „lambda” jest napisanie funkcji w C, skompilowanie jej, sprawdzenie za pomocą czegoś podobnego
objdump -D
i skopiowanie odpowiedniego kodu szesnastkowego do łańcucha. Na przykład,... po kompilacji
gcc -Os -c
dla systemu Linux x86_64 cel generuje coś takiegoGNU CC
goto
:Możesz wywoływać te „funkcje lambda” bezpośrednio, ale jeśli wywoływany kod nie przyjmuje parametrów i nie zamierza zwrócić, możesz użyć
goto
kilku bajtów. Więc zamiastlub (jeśli twoje środowisko nie ma arabskich glifów)
Próbować
lub
W tym przykładzie
eb fe
jest językiem maszynowym x86 dla czegoś podobnegofor(;;);
i jest prostym przykładem czegoś, co nie przyjmuje parametrów i nie zamierza wrócić :-)Okazuje się, że możesz
goto
kodować, który zwraca do rodzica dzwoniącego.Powyższy przykład (może się kompilować i uruchamiać w systemie Linux z
gcc -O
) jest wrażliwy na układ stosu.EDYCJA: W zależności od zestawu narzędzi może być konieczne użycie
-zexecstack
flagi kompilacji.Jeśli nie jest to od razu widoczne, odpowiedź została napisana głównie dla lolów. Nie biorę odpowiedzialności za lepsze lub gorsze gry w golfa lub negatywne wyniki psychologiczne po przeczytaniu tego.
źródło
Używaj kursorów zamiast wskaźników. Zatrzymaj
brk()
na początku i użyj go jako wskaźnika bazy .Następnie utwórz #define dla dostępu do pamięci.
M
staje się postfiksem*
stosowanym do liczb całkowitych. (Stara sztuczka [x] == x [a].)Ale jest więcej! Następnie możesz mieć argumenty wskaźnika i zwraca w funkcjach, które są krótsze niż makra (szczególnie jeśli skrócisz „return”):
Aby utworzyć kursor ze wskaźnika, odejmij wskaźnik bazowy, uzyskując wartość ptrdiff_t, która zostanie obcięta do wartości int, straty są twoje.
Ta technika jest używana w mojej odpowiedzi na napisanie interpretera dla niepisanego rachunku lambda .
źródło
Zdefiniuj parametry zamiast zmiennych.
f(x){int y=x+1;...}
f(x,y){y=x+1;...}
Nie musisz tak naprawdę przekazywać drugiego parametru.
Można również użyć pierwszeństwa operatora, aby zapisać nawias.
Na przykład
(x+y)*2
może zostaćx+y<<1
.źródło
x+y*2
oszczędzając kolejny znak.x+y*2
to nie to samo, ze względu na pierwszeństwo operatora.x+y<<1
przykładzie, zakładając, że był oceniany jakox+(y<<1)
i zasugerowałem*2
zamiast tego. Nie wiedziałem, że operacje przesunięcia bitów zostały ocenione jako np.(x+y)<<2
Ponieważ zwykle
EOF == -1
używaj bitowego operatora NOT do sprawdzania EOF:while(~(c=getchar()))
lubwhile(c=getchar()+1)
modyfikuj wartość c w każdym miejscuźródło
while(1+c=getchar())
zadziałałoby?+
ma wyższy priorytet niż operator przypisania=
, więc1+c=getchar()
jest równoważny z(1+c)=getchar()
, który nie kompiluje, ponieważ(1+c)
nie jest wartością.Operator trójskładnikowy
?:
jest niezwykły, ponieważ ma dwa oddzielne elementy. Z tego powodu zapewnia nieco luki w standardowych regułach pierwszeństwa operatorów. Może to być przydatne w celu uniknięcia nawiasów.Weź następujący przykład:
Podejście zwykle golfa jest zastąpienie
if
z&&
, ale z powodu niskiego priorytetu operatora przecinek, trzeba dodatkową parę nawiasów:Jednak środkowa sekcja operatora trójskładnikowego nie potrzebuje nawiasów:
Podobne komentarze dotyczą indeksów tablicy.
źródło
b-=a=b
jest jeszcze krótszy.?:
Sztuką jest nadal pomocne,-=
ponieważ ma również niską preferencji.x>0||(y=3)
,x>0?0:(y=3)
jest bezużyteczny, alex<1?y=3:0
spełnia swoje zadanie.x>5?:y=1
Każda część kodu, która powtarza się kilka razy, jest kandydatem do zastąpienia przez procesor wstępny.
jest bardzo częstym przypadkiem użycia, jeśli kod obejmuje więcej niż kilka funkcji. Inne wydłużone słowa kluczowe, takie jak
while
,double
,switch
, icase
są kandydatami; a także wszystko, co jest idomatyczne w kodzie.Generalnie rezerwuję w tym celu wielkie litery.
źródło
-DR=return
. Zauważ, że jeśli dodasz określone znaki, konieczne może być utworzenie pojedynczych lub podwójnych cudzysłowów wokół definicji-DP='puts("hello")'
.Jeśli twój program odczytuje lub pisze na podstawie każdego kroku, zawsze spróbuj użyć funkcji odczytu i zapisu zamiast getchar () i putchar () .
Przykład ( Odwróć standardowe wejście i umieść na standardowe wyjście )
Ćwiczenie: Za pomocą tej techniki, aby uzyskać dobry wynik tutaj .
źródło
Pętle zwrotne
Jeśli możesz, spróbuj wymienić
z
źródło
Jeśli kiedykolwiek potrzebujesz wypisać pojedynczy znak nowej linii (
\n
), nie używajputchar(10)
, użyjputs("")
.źródło
Wykorzystaj wartości zwracane do zera. Jeśli wywołasz jakąś funkcję, która w normalnych warunkach zwróci zero, możesz umieścić ją w miejscu, w którym oczekiwane jest zero. Podobnie, jeśli wiesz, że funkcja zwróci wartość niezerową, z dodatkiem huku. W końcu i tak nie radzisz sobie z błędami w golfie kodowym, prawda?
Przykłady:
źródło
Przypisz zamiast powrotu.
To nie jest tak naprawdę standardowy C, ale działa z każdym znanym mi kompilatorem i procesorem:
ma taki sam efekt jak:
Ponieważ pierwszy argument jest przechowywany w tym samym rejestrze procesora co wartość zwracana.
Uwaga: Jak zauważono w jednym komentarzu, jest to niezdefiniowane zachowanie i nie ma gwarancji, że zadziała dla każdej operacji. I każda optymalizacja kompilatora po prostu ją pominie.
X-Makra
Kolejna przydatna funkcja: X-Makra mogą ci pomóc, gdy masz listę zmiennych i musisz wykonać operację obejmującą wszystkie z nich:
https://en.wikipedia.org/wiki/X_Macro
źródło
-O0
zawsze wybiera wyrażenia w rejestrze wartości zwracanej. Spojrzałem przynajmniej na x86, ARM i MIPS (na gcc.godbolt.org ), a gcc wydaje się, że robi to, aby to zrobić-O0
. Ale pamiętam, czy skorzystać z tego języka programowania, jesteś znaczygcc -O0
, nie C i należy oznaczyć odpowiednio swoją odpowiedź, a nie C . Nie działa na żadnym poziomie optymalizacji innym niż-O0
tryb debugowania i nie działa z clang IIRC.Użyj
*a
zamiast,a[0]
aby uzyskać dostęp do pierwszego elementu tablicy.Operatory relacyjne (
!=
,>
etc.) dać0
lub1
. Użyj tego z operatorami arytmetycznymi, aby uzyskać różne przesunięcia w zależności od tego, czy warunek jest prawdziwy, czy fałszywy:a[1+2*(i<3)]
uzyska dostęp,a[1]
jeślii >= 3
ia[3]
inaczej.źródło
a[i<3?3:1]
jest dwa znaki krótsze niża[1+2*(i<3)]
.Możesz zajrzeć do archiwów IOCCC (międzynarodowy konkurs zaciemnionego kodu C).
Jedną z godnych uwagi sztuczek jest # zdefiniowanie makr, których rozwinięcie ma niezrównoważone nawiasy klamrowe / nawiasy, na przykład
źródło
#define P;printf(
.for(int i=0;i<n;i++){a(i);b(i);}
można skrócić na kilka sposobów:for(int i=0;i<n;){a(i);b(i++);}
-1 do przejścia++
do ostatniegoi
w pętlifor(int i=0;i<n;b(i++))a(i);
-3 więcej za przeniesienie wszystkich instrukcji oprócz jednej do górnej i górnej pętli, usunięcie nawiasów klamrowychźródło
Idź funkcjonalnie!
Jeśli możesz sprowadzić swój problem do prostych funkcji z tą samą sygnaturą i zdefiniowanych jako pojedyncze wyrażenia, możesz zrobić lepiej niż
#define r return
i wyliczyć prawie całą płytę podstawową dla zdefiniowania funkcji.Wynikiem programu jest wartość statusu zwrócona do systemu operacyjnego lub kontrolująca powłokę lub IDE.
Użycie
__VA_ARGS__
umożliwia użycie operatora przecinka do wprowadzenia punktów sekwencji w tych wyrażeniach funkcji . Jeśli nie jest to potrzebne, makro może być krótsze.źródło
służy
scanf("%*d ");
do odczytu danych wejściowych manekina. (w przypadku, gdy dane wejściowe nie mają znaczenia w dalszym programie), jest on krótszy niż wscanf("%d",&t);
przypadku, gdy należy również zadeklarować zmienną t.przechowywanie znaków w tablicy int jest znacznie lepsze niż tablica znaków. przykład.
s[],t;main(c){for(scanf("%*d ");~(c=getchar());s[t++]=c)putchar(s[t]);}
źródło
%*d
nie tylko w golfie, ponieważ jest to również przydatne w sytuacjach, w których na przykład chciałby się pominąć nowy wierszscanf("%[^\n]%*c",str);
:)Wydrukuj znak, a następnie znak powrotu karetki zamiast:
lub
po prostu zadeklaruj c jako int i:
źródło
puts(&c)
naprawdę działa To niekoniecznie musi być zakończone zerem.char *
, widzimy ciąg singletonu: znak c , po którym następuje bajt zerowy.Użycie
asprintf()
oszczędza ci jawnego przydzielania, a także mierzenia długości łańcucha, zwanego teżchar*
! Nie jest to może zbyt przydatne do gry w golfa, ale ułatwia codzienną pracę z tablicami char. Istnieje kilka dobrze radzi w 21st Century C .Przykład użycia:
źródło
import
Jeśli musiszJak zauważono w pierwszej odpowiedzi , niektóre kompilatory (zwłaszcza GCC i clang) pozwalają ominąć
#include
standardowe funkcje biblioteczne.Nawet jeśli nie możesz po prostu usunąć
#include
, mogą istnieć inne sposoby, aby tego uniknąć , ale nie zawsze jest to praktyczne lub szczególnie golfowe.W pozostałych przypadkach możesz użyć
#import<header file>
zamiast#include<header file>
zapisać bajt. Jest to rozszerzenie GNU i jest uważane za przestarzałe, ale działa co najmniej w gcc 4.8, gcc 5.1 i clang 3.7.źródło
Spróbuj
cpow()
zamiastcos()
Zamiast
spróbuj czegoś takiego
Wykorzystuje to formułę Eulera , nieco złożoną analizę i spostrzeżenie, że przypisanie kompleksu podwójnemu daje rzeczywistą część (ostrożnie przy wywoływaniu funkcji variadic i innych subtelności).
Tego rodzaju lewę można wykorzystać do zmniejszenia
w
dlatego
źródło
Oto kilka wskazówek, które wykorzystałem na swoją korzyść. Bezwstydnie ukradłem je innym, więc pochwalcie każdego oprócz mnie:
Połącz przypisanie z wywołaniami funkcji
Zamiast tego:
Zrób to:
Zainicjuj wiele zmiennych razem (jeśli to możliwe)
Zamiast tego:
Zrób to:
Zwiń wartości zerowe / niezerowe
To fajna sztuczka, którą wybrałem od kogoś tutaj (nie pamiętam kogo, przepraszam). Jeśli masz wartość całkowitą i musisz zwinąć ją do 1 lub 0, możesz
!!
to zrobić z łatwością. Czasami jest to korzystne dla innych alternatyw?:
.Weź tę sytuację:
Zamiast tego możesz to zrobić:
Inny przykład:
Może być przepisany jako:
źródło
R*-~!!mxxxx
Znajomość podstawowych równości logicznych może być w stanie zaoszczędzić kilka bajtów. Na przykład zamiast
if (!(a&&b)){}
próbować zamiast tego użyć prawa DeMorganif (!a||!b){}
. To samo dotyczy funkcji bitowych: zamiast~(a|b)
do~a&~b
.źródło