Какие общие советы у вас есть для игры в гольф в Си? Я ищу идеи, которые могут быть применены к кодовым проблемам гольфа в целом, которые, по крайней мере, несколько специфичны для C (например, «удалить комментарии» - это не ответ). Пожалуйста, оставьте один совет за ответ. Также, пожалуйста, укажите, относится ли ваш совет к C89 и / или C99 и работает ли он только на определенных компиляторах.
138
Ответы:
Используйте побитовый XOR для проверки неравенства между целыми числами:
if(a^b)
вместо того, чтобыif(a!=b)
сохранить 1 символ.источник
a-b
дает вам тот же эффект.a*b
вместоa&&b
(имеет другой приоритет, может быть или не быть плохим). Если вы знаете / = -b (например, они не подписаны), тоa||b
==a+b
?:
(вместо if): например, просто сделать что-то, если не так:a^b?_diff_:;
?:
оператор, который просто эквивалентенa ? a : b
main
Список аргументов Abuse для объявления одной или нескольких целочисленных переменных:(ответ на Алфавит на языках программирования )
Это решение также использует тот факт, что
a
(akaargc
) начинается как1
, при условии, что программа вызывается без аргументов.Используйте глобальные переменные для инициализации вещей до нуля:
(ответ на Anagram Code Golf! )
источник
Оператор запятой можно использовать для выполнения нескольких выражений в одном блоке, избегая при этом фигурных скобок:
Выходы:
1 2
источник
break
.break
это утверждение, и этот ответ говорит о выражениях.Избегайте катастрофических объявлений типа аргумента функции
Если вы объявляете функцию, в которой все пять аргументов являются
int
s, то жизнь хороша. ты можешь просто написатьНо предположим, что это
d
должно бытьchar
или дажеint*
. Тогда ты облажался! Если одному параметру предшествует тип, все они должны быть:Но ждать! Есть способ обойти этот катастрофический взрыв бесполезных персонажей. Это выглядит так:
Это даже экономит стандартное
main
объявление, если вам нужно использовать аргументы командной строки:на два байта короче
Я был удивлен, обнаружив это, так как я до сих пор не сталкивался с этим на PPCG.
источник
-std=gnu99
и теперь вы не переносимы. В clc-talk вы даже не пишете код «С» как таковой, а «Gnu99-C». Здесь мы в основном игнорируем это, но хорошо упомянуть об этом, если вы публикуете код, специфичный для компилятора. Иногда люди на самом деле сделать для загрузки и выполнения этих программ у нас. :)-std=c89
gcc или clang, чтобы скомпилировать ваш код в соответствии с тем более старым стандартом, который допускает неявное int только с предупреждением.Вместо> = и <= вы можете просто использовать целочисленное деление (/), когда сравниваемые значения выше нуля, что сохраняет один символ. Например:
Что, конечно, все еще можно сжать, используя, например, просто> и ^ (умный способ избежать написания && или || в некоторых случаях).
Трюк с целочисленным делением, например, полезен для определения, является ли число меньше 100, так как это сохраняет символ:
Это также хорошо в тех случаях, когда требуется более высокий приоритет.
источник
putchar(c>31&c<127?c:46);
Некоторые компиляторы, такие как GCC, позволяют опускать базовые
#include
типы s, param и return дляmain
.Ниже приведена действительная программа C89 и C99, которая компилирует (с предупреждениями) GCC:
Обратите внимание, что отсутствует
#include
for stdio.h, отсутствует тип возвращаемого значения formain
и отсутствует объявление типа fori
.источник
printf()
(или любая переменная функция) без прототипа вызывает неопределенное поведение . GCC не компилирует стандарт C по умолчанию. Если вы вызовете gcc в режиме C89 (gcc -ansi -pedantic
) или C99 (gcc -std=c99 -pedantic
), вы получите довольно много жалоб, по крайней мере, в последнем случае.Тройной условный оператор
?:
может часто использоваться в качестве подставки в простыхif
-else
заявления на значительной экономии средств.В отличие от эквивалента в c ++, оператор формально не выдает lvalue , но некоторые компиляторы (в частности, gcc) позволят вам с этим справиться, что является хорошим бонусом.
источник
&&
и||
может также использоваться:if(x==3)f()
становится с вашим предложениемx==3?f():0
, и может быть улучшено доx==3&&f()
. Но будьте осторожны с приоритетом оператора - еслиf()
его заменить наy=1
, то&&
решение требует дополнительного набора скобок.?:
дает lvalue. Могу ли я использовать это в производственном коде? LOLx==3&&f()
можно дальше играть вx^3||f()
http://graphics.stanford.edu/~seander/bithacks.html
Биты хорошие.
Но с разными приоритетами, и не меняйте х, как ++ и -. Также вы можете использовать это в действительно определенных случаях: ~ 9 короче, чем -10.
Это более эзотерично, но у меня была возможность использовать его. Если вас не волнует короткое замыкание
Также:
источник
(x/y) == (x>=y)
) действительно полезен.Используйте лямбды (непортативно)
Вместо
или (только gcc)
или (llvm с поддержкой блоков)
попробуй что-то вроде
... где строка в кавычках содержит инструкции на машинном языке вашей функции "лямбда" (соответствует всем требованиям ABI платформы).
Это работает в средах, в которых строковые константы помечены как исполняемые. По умолчанию это верно для Linux и OSX, но не для Windows.
Один глупый способ научиться писать свои собственные «лямбда-функции» - это написать функцию в C, скомпилировать ее, проверить что-то вроде этого
objdump -D
и скопировать соответствующий шестнадцатеричный код в строку. Например,... при компиляции с
gcc -Os -c
целью Linux x86_64 генерирует что-то вродеGNU CC
goto
:Вы можете вызывать эти «лямбда-функции» напрямую, но если код, который вы вызываете, не принимает параметры и не собирается возвращаться, вы можете использовать
goto
для сохранения несколько байтов. Так что вместоили (если в вашей среде нет арабских символов)
Пытаться
или же
В этом примере
eb fe
это машинный язык x86 для чего-то подобногоfor(;;);
и простой пример чего-то, что не принимает параметров и не собирается возвращать :-)Оказывается, вы можете
goto
написать код, который возвращается к вызывающему родителю.Приведенный выше пример (может компилироваться и запускаться в Linux с
gcc -O
) чувствителен к макету стека.РЕДАКТИРОВАТЬ: В зависимости от вашей цепочки инструментов, вам, возможно, придется использовать
-zexecstack
флаг компиляции.Если это не сразу видно, этот ответ был написан в основном для lols. Я не беру на себя ответственности за лучшее или худшее гольф или неблагоприятные психологические последствия от чтения этого.
источник
Используйте курсоры вместо указателей. Захватите
brk()
в начале и используйте его как указатель базы .Затем создайте #define для доступа к памяти.
M
становится постфиксом,*
применяемым к целым числам. (Старый трюк [x] == x [a].)Но это еще не все! Тогда у вас могут быть аргументы и возвраты указателей в функциях, которые короче макросов (особенно если вы сокращаете «возврат»):
Чтобы сделать курсор из указателя, вы вычитаете базовый указатель, получая ptrdiff_t, который усекается в int, потери - это ваш бизнес.
Эта техника используется в моем ответе на « Напиши интерпретатор для нетипизированного лямбда-исчисления» .
источник
Определите параметры вместо переменных.
f(x){int y=x+1;...}
f(x,y){y=x+1;...}
Вам не нужно фактически передавать второй параметр.
Также вы можете использовать приоритет оператора для сохранения скобок.
Например,
(x+y)*2
может статьx+y<<1
.источник
x+y*2
, сохраняя еще один символ.x+y*2
не то же самое, из-за приоритета оператора.x+y<<1
примере, предполагая, что он оценивается какx+(y<<1)
, и предложил*2
вместо этого. Я не знал, что операции с бит-смещением оценивались как, например,(x+y)<<2
Так как обычно
EOF == -1
, используйте побитовый оператор NOT для проверки EOF:while(~(c=getchar()))
илиwhile(c=getchar()+1)
и изменяйте значение c в каждом местеисточник
while(1+c=getchar())
сработает?+
имеет более высокий приоритет, чем оператор присваивания=
, поэтому1+c=getchar()
эквивалентен(1+c)=getchar()
, который не компилируется, потому что(1+c)
не является lvalue.Тернарный оператор
?:
необычен тем, что состоит из двух отдельных частей. Из-за этого он представляет собой небольшую лазейку для стандартных правил приоритета операторов. Это может быть полезно для избежания скобок.Возьмите следующий пример:
Обычная игра в гольф подход заключается в замене
if
с&&
, но из-за низкого приоритета оператора запятая, вам требуется дополнительная пара скобок:Средняя часть троичного оператора не нуждается в скобках:
Подобные комментарии относятся к подпискам массива.
источник
b-=a=b
еще короче.?:
Трюк еще полезно,-=
потому что также имеет низкое предпочтение.x>0||(y=3)
,x>0?0:(y=3)
бесполезно, ноx<1?y=3:0
делает работу.x>5?:y=1
Любая часть вашего кода, которая повторяется несколько раз, является кандидатом на замену препроцессором.
Это очень распространенный случай использования, если ваш код включает в себя более пары функций. Другие длинноватые ключевые слова , такие как
while
,double
,switch
, иcase
также являются кандидатами; а также все, что является сомнительным в вашем коде.Я обычно резервирую заглавные буквы для этой цели.
источник
-DR=return
. Обратите внимание, что если вы включаете определенные символы, может возникнуть необходимость в одинарных или двойных кавычках вокруг определения-DP='puts("hello")'
.Если ваша программа читает или пишет по одному на каждом шаге, всегда старайтесь использовать функцию чтения и записи вместо getchar () и putchar () .
Пример ( поменять стандартный ввод и поместить на стандартный вывод )
Упражнение: Используйте эту технику, чтобы получить хороший результат здесь .
источник
Обратные петли
Если можешь, попробуй заменить
с участием
источник
Если вам когда-либо понадобится вывести один символ новой строки (
\n
), не используйтеputchar(10)
, используйтеputs("")
.источник
Используйте возвращаемые значения для нуля вещи. Если вы вызываете какую-то функцию, и эта функция возвращает ноль при нормальных условиях, то вы можете поместить ее в место, где ожидается ноль. Аналогично, если вы знаете, что функция вернет ненулевое значение с добавлением взрыва. В конце концов, в любом случае вы не выполняете правильную обработку ошибок в кодовом гольфе, верно?
Примеры:
источник
Назначьте вместо возврата.
Это не совсем стандартный C, но он работает с каждым известным мне компилятором и процессором:
имеет такой же эффект как:
Потому что первый аргумент сохраняется в том же регистре процессора, что и возвращаемое значение.
Примечание. Как отмечается в одном комментарии, это неопределенное поведение, которое не гарантируется для каждой операции. И любая оптимизация компилятора просто пропустит это.
X-макросы
Еще одна полезная функция: X-Macros могут помочь вам, когда у вас есть список переменных, и вам нужно выполнить некоторые операции, которые включают все из них:
https://en.wikipedia.org/wiki/X_Macro
источник
-O0
всегда выбирает для вычисления выражений в регистре возвращаемого значения. Я посмотрел, по крайней мере, x86, ARM и MIPS (на gcc.godbolt.org ), и gcc, похоже, изо всех сил пытается это сделать-O0
. Но помните , если вы воспользоваться этим, язык программирования вы в естьgcc -O0
, не C , и вы должны маркировать свой ответ соответственно, а не C . Он не работает на любом уровне оптимизации, кроме-O0
режима отладки, и не работает с Clang IIRC.Используйте
*a
вместоa[0]
доступа к первому элементу массива.Реляционные операторы (
!=
,>
и т. Д.) Дают0
или1
. Используйте это с арифметическими операторами, чтобы задать различные смещения в зависимости от того, является ли условие истинным или ложным:a[1+2*(i<3)]
получит доступ,a[1]
еслиi >= 3
и вa[3]
противном случае.источник
a[i<3?3:1]
на два символа корочеa[1+2*(i<3)]
.Вы можете заглянуть в архив IOCCC (международный конкурс обфусцированного кода C).
Один заметный трюк заключается в том, чтобы #define макросы, чье расширение имеет несбалансированные скобки / скобки, например
источник
#define P;printf(
.for(int i=0;i<n;i++){a(i);b(i);}
короче можно сделать несколькими способами:for(int i=0;i<n;){a(i);b(i++);}
-1 для перемещения++
до последнегоi
в циклеfor(int i=0;i<n;b(i++))a(i);
-3 больше для перемещения всех операторов, кроме одного, в верхнюю часть и из основного цикла, удаления скобокисточник
Работай!
Если вы можете свести свою проблему к простым функциям с такой же сигнатурой и определенным как одиночные выражения, то вы можете добиться большего успеха
#define r return
и вычленить практически весь шаблон для определения функции.Результатом программы является ее значение состояния, возвращаемое ОС или управляющей оболочке или IDE.
Использование
__VA_ARGS__
позволяет вам использовать оператор запятой для введения точек последовательности в эти выражения-функции . Если это не нужно, макрос может быть короче.источник
используйте,
scanf("%*d ");
чтобы прочитать фиктивный ввод. (в случае, если ввод не имеет смысла в дальнейшей программе), он короче, чемscanf("%d",&t);
где вы также должны объявить переменную t.Хранение символов в массиве int намного лучше, чем в массиве символов. пример.
s[],t;main(c){for(scanf("%*d ");~(c=getchar());s[t++]=c)putchar(s[t]);}
источник
%*d
не только в Гольфе, потому что это также полезно в ситуациях, когда, например, хочется пропустить новуюscanf("%[^\n]%*c",str);
Выведите символ, затем возврат каретки вместо:
или же
просто объявите c как int и:
источник
puts(&c)
самом деле работает? Это не обязательно будет завершено нулем.char *
мы видим одноэлементную строку: символ c , за которым следует нулевой байт.Использование
asprintf()
спасает вас от явного размещения, а также измерения длины строки акаchar*
! Возможно, это не слишком полезно для игры в код, но облегчает повседневную работу с массивами символов. Есть еще несколько хороших советов в 21 веке .Пример использования:
источник
import
если вам нужноКак отмечалось в самом первом ответе , некоторые компиляторы (в частности, GCC и clang) позволяют вам не использовать
#include
s для стандартных функций библиотеки.Даже если вы не можете просто удалить
#include
, могут быть другие способы избежать этого , но это не всегда практично или особенно в гольфе.В остальных случаях вы можете использовать
#import<header file>
вместо того,#include<header file>
чтобы сохранить байт. Это расширение GNU, и оно считается устаревшим, но оно работает по крайней мере в gcc 4.8, gcc 5.1 и clang 3.7.источник
Попробуй
cpow()
вместоcos()
Вместо
попробуй что-то вроде
При этом используется формула Эйлера , немного сложный анализ и наблюдение, что присвоение комплекса двойному дает действительную часть (осторожно с вызовами функций с переменными числами и другими тонкостями).
Этот тип трюк может быть использован для уменьшения
в
источник
Вот несколько советов, которые я использовал в своих интересах. Я бесстыдно украл их у других, так что поверь никому, кроме меня:
Объединить назначение с вызовами функций
Вместо этого:
Сделай это:
Инициализируйте несколько переменных вместе (когда это возможно)
Вместо этого:
Сделай это:
Свернуть ноль / ненулевые значения
Это аккуратный трюк, который я подобрал у кого-то здесь (не помню, кто, извините). Если у вас есть целочисленное значение, и вам нужно свернуть его до 1 или 0, вы можете использовать
!!
это легко. Это иногда выгодно для других альтернатив, как?:
.Возьмите эту ситуацию:
Вы могли бы вместо этого сделать это:
Другой пример:
Может быть переписан как:
источник
R*-~!!mxxxx
Знание основных логических равенств может сэкономить пару байтов. Например, вместо того, чтобы
if (!(a&&b)){}
пытаться вместо этого использовать закон Деморганаif (!a||!b){}
. То же самое относится к побитовым функциям: вместо~(a|b)
do~a&~b
.источник