Wskazówki do gry w golfa w C


137

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.


8
Myślę, że największa wskazówka w jednym zdaniu to: Przeczytaj zwycięskie kody przesłane do IOCCC.
vsz

Odpowiedzi:


107

Użyj bitowego XOR, aby sprawdzić nierówność między liczbami całkowitymi:

if(a^b)zamiast if(a!=b)ratuje 1 postać.


73
a-bdaje ten sam efekt.
ugoren

22
Podobnie możesz użyć a*bzamiast a&&b(ma różne preferencje, może, ale nie musi być zły). Jeśli znasz a / = -b (np. Nie są podpisani), to a||b==a+b
walpen

3
lepiej jeszcze połącz to z Operatorem Elvisa ?:(zamiast jeśli): dla przykładu rób coś, jeśli coś jest inne: a^b?_diff_:;
Olivier Dulac

1
@OlivierDulac Czy istnieje kompilator, który akceptuje pustą trójskładnikową, jeśli fałszywa gałąź?
Jonathan Frech

1
@OlivierDulac Możesz sprawdzić. Z tego co wiem, GCC ma ?:operatora, który jest po prostu równoważnya ? a : b
Chromium

75
  • mainLista argumentów nadużycia w celu zadeklarowania jednej lub więcej zmiennych całkowitych:

    main(a){for(;++a<28;)putchar(95+a);}
    

    (odpowiedź na alfabet w językach programowania )

    To rozwiązanie narusza również fakt, że a(aka argc) zaczyna się jako 1, pod warunkiem, że program jest wywoływany bez argumentów.

  • Użyj zmiennych globalnych, aby zainicjować rzeczy do zera:

    t[52],i;main(c){for(;i<52;)(c=getchar())<11?i+=26:t[i+c-97]++;
    for(i=27;--i&&t[i-1]==t[i+25];);puts(i?"false":"true");}
    

    (odpowiedź na Anagram Code Golf! )


62

Za pomocą operatora przecinka można wykonywać wiele wyrażeń w jednym bloku, unikając nawiasów klamrowych:

main(){                                                                                     

int i = 0;                                                                                  
int j = 1;                                                                                  
if(1)                                                                                       
    i=j,j+=1,printf("%d %d\n",i,j); // multiple statements are all executed                                                  
else                                                                                        
    printf("failed\n");                                                                     

}

Wyjścia: 1 2


Nie działa, jeśli jedna z instrukcji jest break.
Maxim Mikhaylov,

9
@ MaxLawnboy, ponieważ breakjest stwierdzeniem, a ta odpowiedź mówi o wyrażeniach.
NieDzejkob

59

Unikaj katastrofalnych deklaracji typu argument-funkcja

Jeśli deklarujesz funkcję, w której wszystkie pięć argumentów jest ints, to życie jest dobre. możesz po prostu pisać

f(a,b,c,d,e){

Załóżmy jednak, że dmusi to być char, a nawet int*. Więc jesteś pieprzony! Jeśli jeden parametr jest poprzedzony typem, wszystkie muszą być:

f(int a,int b,int c,int*d,int e){

Ale poczekaj! Istnieje sposób na obejście tej katastrofalnej eksplozji bezużytecznych postaci. Wygląda to tak:

f(a,b,c,d,e) int *d; {

To nawet oszczędza na standardowej maindeklaracji, jeśli musisz użyć argumentów wiersza poleceń:

main(c,v)char**v;{

jest dwa bajty krótszy niż

main(int c,char**v){

Byłem zaskoczony odkryciem tego, ponieważ do tej pory nie spotkałem go na PPCG.


6
Dlaczego na ziemi to działa?
Nathaniel

30
Najwyraźniej nazywa się to stylem K&R i poprzedza ANSI C o dekadę.
Dennis

Pamiętaj, że używanie funkcji K&R i nowszych (powiedzmy 99) funkcji razem jest niemożliwe lub może nie być możliwe. Zależy od twojego kompilatora.
dmckee

5
@dmckee ma rację. C99 nie pozwala na niejawne int, więc musisz go użyć, -std=gnu99a 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. :)
luser droog,

@luserdroog: Możesz użyć -std=c89polecenia gcc lub clang, aby skompilowały kod zgodnie ze starszym standardem, który pozwala na domyślną int tylko z ostrzeżeniem.
Peter Cordes

37

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:

putchar(c/32&&126/c?c:46); //Prints the character, but if it is unprintable print "."

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).

putchar(c>31^c>126?c:46);

Sztuczka dzielenia liczb całkowitych jest na przykład przydatna do decydowania, czy liczba jest mniejsza niż 100, ponieważ zapisuje to znak:

a<100 vs 99/a

Jest to również dobre w przypadkach, gdy potrzebny jest wyższy priorytet.


Możesz pisaćputchar(c>31&c<127?c:46);
Jin X

37

Niektóre kompilatory, takie jak GCC, pozwalają pominąć podstawowe #includetypy s, param i return dla main.

Oto poprawny program C89 i C99, który kompiluje się (z ostrzeżeniami) z GCC:

main(i) { printf("%d", i); }

Zauważ, że #includebrakuje pliku for stdio.h, brakuje typu zwracanego przez for i mainbrakuje deklaracji typu dla i.


17
Technicznie nie jest to zgodne ze standardami, ponieważ main akceptuje zero lub dwa parametry, a nie jeden. Nie żeby ktokolwiek dbał o golfa kodowego.
Konrad Borowski

Wywołanie 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.
Nisse Engström

@ NisseEngström: konwencje wywoływania w głównych implementacjach C umożliwiają bezpieczne wywoływanie funkcji variadic bez prototypów. Dlatego większość implementacji języka C definiuje zachowanie.
Peter Cordes

29

Trójskładnikowy operator warunkowy ?:może być często używany jako zastępstwo dla prostych if- elsewycią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ą.


Dodatek: jeśli potrzebujesz tylko if, ale nie innego, trójka może być nadal przydatna.
Casey

9
&&i ||może być również użyty: if(x==3)f()staje się twoją sugestią x==3?f():0i może być dalej ulepszony x==3&&f(). Uważaj jednak na pierwszeństwo operatora - jeśli f()zostanie zastąpione y=1, &&rozwiązanie wymaga dodatkowego zestawu nawiasów.
ugoren 12.01.12

1
Nigdy nie zdawałem sobie sprawy, że gcc ?:daje wartość. Czy mogę tego użyć w kodzie produkcyjnym? lol
Jeff Burdges

4
@ugoren: x==3&&f()można dalej x^3||f()
grać w

@fgrieu, tak, choć nie jest to dokładnie temat tutaj ( sugeruje to odpowiedź ).
ugoren

27

http://graphics.stanford.edu/~seander/bithacks.html

Bity są fajne.

~-x = x - 1
-~x = x + 1

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.

if(!(x&y)) x | y == x ^ y == x + y
if(!(~x&y)) x ^ y == x - y

To bardziej ezoteryczne, ale miałem okazję go użyć. Jeśli nie obchodzi Cię zwarcie

x*y == x && y
if(x!=-y) x+y == x || y

Również:

if(x>0 && y>0) x/y == x>=y   

5
Ostatnia wskazówka ( (x/y) == (x>=y)) jest naprawdę przydatna.
ugoren

24

Użyj lambdas (nie można przenosić)

Zamiast

f(int*a,int*b){return*a>*b?1:-1;}
...
qsort(a,b,4,f);

lub (tylko gcc)

qsort(a,b,4,({int L(int*a,int*b){a=*a>*b?1:-1;}L;}));

lub (lvvm z obsługą bloków)

qsort_b(a,b,4,^(const void*a,const void*b){return*(int*)a>*(int*)b?1:-1;});

spróbuj czegoś takiego

qsort(a,b,4,"\x8b\7+\6\xc3");

... 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 -Di skopiowanie odpowiedniego kodu szesnastkowego do łańcucha. Na przykład,

int f(int*a, int*b){return *a-*b;}

... po kompilacji gcc -Os -cdla systemu Linux x86_64 cel generuje coś takiego

0:   8b 07                   mov    (%rdi),%eax
2:   2b 06                   sub    (%rsi),%eax
4:   c3                      retq

GNU 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ć gotokilku bajtów. Więc zamiast

((int(*)())L"ﻫ")();

lub (jeśli twoje środowisko nie ma arabskich glifów)

((int(*)())L"\xfeeb")();

Próbować

goto*&L"ﻫ";

lub

goto*&L"\xfeeb";

W tym przykładzie eb fejest językiem maszynowym x86 dla czegoś podobnego for(;;);i jest prostym przykładem czegoś, co nie przyjmuje parametrów i nie zamierza wrócić :-)

Okazuje się, że możesz gotokodować, który zwraca do rodzica dzwoniącego.

#include<stdio.h>
int f(int a){
 if(!a)return 1;
 goto*&L"\xc3c031"; // return 0;
 return 2; // never gets here
}
int main(){
 printf("f(0)=%d f(1)=%d\n",f(0),f(1));
}

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 -zexecstackflagi 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.


2
Właśnie napisałem skrypt, aby odczytać części funkcji C ze standardowego i wydrukować C lambda. Być może warto wspomnieć w twojej odpowiedzi, może po prostu miło cię widzieć, ponieważ nauczyłeś mnie tego w pierwszej kolejności.
MD XF

23

Używaj kursorów zamiast wskaźników. Zatrzymaj brk()na początku i użyj go jako wskaźnika bazy .

char*m=brk();

Następnie utwórz #define dla dostępu do pamięci.

#define M [m]

Mstaje 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”):

f(x){return x M;} //implicit ints, but they work like pointers
#define f(x) (x M)

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.

char *p = sbrk(sizeof(whatever)) - m;
strcpy(m+p, "hello world");

Ta technika jest używana w mojej odpowiedzi na napisanie interpretera dla niepisanego rachunku lambda .


21

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)*2może zostać x+y<<1.


Albo po prostu x+y*2oszczędzając kolejny znak.
Braden Best

4
@ B1KMusic, x+y*2to nie to samo, ze względu na pierwszeństwo operatora.
ugoren

Racja, lol. To byłoby x + (y * 2). Byłem skupiony na tym x+y<<1przykładzie, zakładając, że był oceniany jako x+(y<<1)i zasugerowałem *2zamiast tego. Nie wiedziałem, że operacje przesunięcia bitów zostały ocenione jako np.(x+y)<<2
Braden Best

20

Ponieważ zwykle EOF == -1używaj bitowego operatora NOT do sprawdzania EOF: while(~(c=getchar()))lub while(c=getchar()+1)modyfikuj wartość c w każdym miejscu


1
Nie znam wystarczająco dobrze C, ale nie while(1+c=getchar())zadziałałoby?
4ıʇǝɥʇuʎs

6
@ No.ıʇǝɥʇuʎs Nie. Operator dodawania +ma wyższy priorytet niż operator przypisania =, więc 1+c=getchar()jest równoważny z (1+c)=getchar(), który nie kompiluje, ponieważ (1+c)nie jest wartością.
ace_HongKongIndependence

19

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:

if (t()) a = b, b = 0;  /* 15 chars */

Podejście zwykle golfa jest zastąpienie ifz &&, ale z powodu niskiego priorytetu operatora przecinek, trzeba dodatkową parę nawiasów:

t() && (a = b, b = 0);  /* still 15 chars */

Jednak środkowa sekcja operatora trójskładnikowego nie potrzebuje nawiasów:

t() ? a = b, b = 0 : 0;  /* 14 chars */

Podobne komentarze dotyczą indeksów tablicy.


7
W tym przykładzie b-=a=bjest jeszcze krótszy. ?:Sztuką jest nadal pomocne, -=ponieważ ma również niską preferencji.
ugoren

Słuszna uwaga; mój przykład był niepotrzebnie złożony.
breadbox

Inną kwestią jest to, że czasami chcesz odwrócić warunek: dla x>0||(y=3), x>0?0:(y=3)jest bezużyteczny, ale x<1?y=3:0spełnia swoje zadanie.
ugoren

zarówno clang, jak i gcc pozwalają na pusty prawdziwy przypadek w trójce. Jeśli pominięty, jego wartość jest wartością warunku. Na przykładx>5?:y=1
Chris Uzdavinis

19

Każda część kodu, która powtarza się kilka razy, jest kandydatem do zastąpienia przez procesor wstępny.

#define R return

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, i casesą kandydatami; a także wszystko, co jest idomatyczne w kodzie.

Generalnie rezerwuję w tym celu wielkie litery.


1
Krótsza wymiana byłaby -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")'.

15

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 )

main(_){write(read(0,&_,1)&&main());}

Ćwiczenie: Za pomocą tej techniki, aby uzyskać dobry wynik tutaj .


Co rozumiesz przez każdy krok ?
Casey

Casey: Myślę, że mają na myśli, że program coś czyta, działa na nim i zapisuje dane wyjściowe. Można powiedzieć, że w sposób strumieniowy. W przeciwieństwie do podejścia, w którym wszystkie dane wejściowe muszą być odczytywane i obsługiwane jednocześnie.
Joey

Joey ma rację, miałem na myśli to samo, przepraszam, że nie sprawdziłem skrzynki odbiorczej do dziś.
Kichot,

8
Ta manipulacja stosami jest wspaniała.
Andrea Biondo

14

Pętle zwrotne

Jeśli możesz, spróbuj wymienić

for(int i=0;i<n;i++){...}

z

for(int i=n;i--;){...}


12

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:

close(fd);foo=0;   →  foo=close(fd);    /* saves two bytes */
putchar(c);bar=0;  →  bar=!putchar(c);  /* saves one byte  */

12

Przypisz zamiast powrotu.

To nie jest tak naprawdę standardowy C, ale działa z każdym znanym mi kompilatorem i procesorem:

int sqr(int a){return a*a;}

ma taki sam efekt jak:

int sqr(int a){a*=a;}

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


3
Zacytowałem to i zostałem poprawiony, to po prostu nieprawda. Będzie działać tylko z mnożeniem i podziałem oraz po wyłączeniu optymalizacji. Jest tak, ponieważ obie operacje umieszczają swoje wyniki w eax, który jest wspólnym rejestrem zwrotu. Parametry są przechowywane w stosie lub w ekxie lub edxie. Spróbuj sam.
Gaspa79

3
Masz rację, to jest niezdefiniowane zachowanie, zależy to również od kompilatora i architektury, zwykle sprawdzam za pomocą gcc na x86 i armv7 przed opublikowaniem jakiejkolwiek odpowiedzi za pomocą tej sztuczki. I oczywiście, jeśli włączysz optymalizację, każdy inteligentny kompilator po prostu usunie niepotrzebne mnożenie.
GB

3
Widziałem tę pracę z GCC, ale nie z innymi
Albert Renshaw

1
@ Gaspa79: gcc z -O0zawsze 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ś znaczy gcc -O0, nie C i należy oznaczyć odpowiednio swoją odpowiedź, a nie C . Nie działa na żadnym poziomie optymalizacji innym niż -O0tryb debugowania i nie działa z clang IIRC.
Peter Cordes

11
  1. Użyj *azamiast, a[0]aby uzyskać dostęp do pierwszego elementu tablicy.

  2. Operatory relacyjne ( !=, >etc.) dać 0lub 1. 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śli i >= 3i a[3]inaczej.


11
a[i<3?3:1]jest dwa znaki krótsze niż a[1+2*(i<3)].
Reto Koradi,

10

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

#define P printf(

16
Niedopasowane nawiasy nie mają w sobie żadnej wartości. Chodzi o to, aby zdefiniować jak najwięcej powtarzającego się wzoru. Możesz iść dalej, z #define P;printf(.
ugoren

Jak liczy się ten skrócony bajt? Być może podać przykład?
Cyoce,

2
@Cyoce Zobacz na przykład tę odpowiedź .
Jonathan Frech

8

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 ostatniego iw pętli

for(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


Korzystanie z operatora przecinka jest innym sposobem uniknięcia nawiasów klamrowych w niektórych przypadkach.
Peter Cordes

8

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 returni wyliczyć prawie całą płytę podstawową dla zdefiniowania funkcji.

#define D(f,...)f(x){return __VA_ARGS__;}
D(f,x+2)
D(g,4*x-4)
D(main,g(4))

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.

#define D(f,b)f(x){return b;}

7
  1. 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ż w scanf("%d",&t);przypadku, gdy należy również zadeklarować zmienną t.

  2. 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]);}


2
Właściwie używam %*dnie tylko w golfie, ponieważ jest to również przydatne w sytuacjach, w których na przykład chciałby się pominąć nowy wiersz scanf("%[^\n]%*c",str);:)
tomsmeding

6

Wydrukuj znak, a następnie znak powrotu karetki zamiast:

printf("%c\n",c);

lub

putchar(c);putchar('\n'); // or its ascii value, whatever!

po prostu zadeklaruj c jako int i:

puts(&c);

9
Prawdopodobnie warto zauważyć, że zależy to od architektury little-endian. Jeśli c jest int-big-endianem, otrzymasz zwrot karetki. (Z drugiej strony, jeśli c jest char, możesz dostać losowe śmieci po zamiast powrotu karetki.)
breadbox

@breadbox tak, masz całkowitą rację; Właśnie edytowałem: ostatni fragment powinien używać c jako int (co często jest łatwe do zadeklarowania jako takie).
moala

Czy puts(&c)naprawdę działa To niekoniecznie musi być zakończone zerem.
Esolanging Fruit

1
@EsolangingFruit Na little-endian z 32-bitowymi liczbami całkowitymi int 0 ≤ c <256 jest zapisywane jako sekwencja bajtów c 0 0 0 . Kiedy interpretujemy adres c as char *, widzimy ciąg singletonu: znak c , po którym następuje bajt zerowy.
Dennis

6

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:

#define _GNU_SOURCE
#include <stdio.h>

int main(int argc, char** argv) {
  char* foo;
  asprintf(&foo, "%s", argv[1]);
  printf("%s",foo);
}

6

import Jeśli musisz

Jak zauważono w pierwszej odpowiedzi , niektóre kompilatory (zwłaszcza GCC i clang) pozwalają ominąć #includestandardowe 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.


6

Spróbuj cpow()zamiastcos()

Zamiast

double y=cos(M_PI*2*x);

spróbuj czegoś takiego

double y=cpow(-1,x*2);

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).

\ cos 2 \ pi x + j \ sin 2 \ pi x = e ^ {j2 \ pi x} = e ^ {j \ pi 2x} = (- 1) ^ {2x}

Tego rodzaju lewę można wykorzystać do zmniejszenia

double y=cpow(-1,x/2);

w

double y=cpow(1i,x);

dlatego (-1) ^ \ frac {x} {2} = j ^ {\ frac {2x} {2}} = j ^ x


5

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:

r = /* Some random expression */
printf("%d", r);

Zrób to:

printf("%d", r = /* Some random expression */);

Zainicjuj wiele zmiennych razem (jeśli to możliwe)

Zamiast tego:

for(i=0,j=0;...;...){ /* ... */ }

Zrób to:

for(i=j=0;...;...){ /* ... */ }

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ę:

n=2*n+isupper(s[j])?1:0; /* 24 */

Zamiast tego możesz to zrobić:

n=n*2+!!isupper(s[j]); /* 22 */

Inny przykład:

r=R+(memcmp(b+6,"---",3)?R:0); /* 30 */

Może być przepisany jako:

r=R+R*!!memcmp(b+6,"---",3)); /* 29 */

1
możeR*-~!!mxxxx
l4m2

5

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 DeMorgan if (!a||!b){}. To samo dotyczy funkcji bitowych: zamiast ~(a|b)do ~a&~b.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.