Answers:
使用按位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
的参数列表来声明一个或多个整数变量:
main(a){for(;++a<28;)putchar(95+a);}
该解决方案还滥用了a
(aka argc
)以开头的事实1
,前提是该程序不带任何参数调用。
使用全局变量将事物初始化为零:
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");}
逗号运算符可用于在单个块中执行多个表达式,同时避免大括号:
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");
}
输出: 1 2
break
。
break
是一条语句,而这个答案是关于表达式的。
如果您要声明一个函数,其中所有五个参数均为int
s,那么生活就很好。你可以简单地写
f(a,b,c,d,e){
但是假设d
需要是char
,甚至是int*
。然后你就被搞砸了!如果一个参数前面带有类型,则所有参数必须为:
f(int a,int b,int c,int*d,int e){
可是等等!可以解决这种无用字符的灾难性爆炸。它是这样的:
f(a,b,c,d,e) int *d; {
main
如果您需要使用命令行参数,这甚至可以节省标准声明:
main(c,v)char**v;{
比两个字节短
main(int c,char**v){
我很惊讶地发现了这一点,因为到目前为止我还没有在PPCG上遇到它。
-std=gnu99
,现在无法移植。用clc语言来说,您本身甚至不是在编写“ C”代码,而是在编写“ Gnu99-C”。“在这里,我们大多忽略了这一点,但是如果您发布特定于编译器的代码,则值得一提。有时人们实际上确实下载并执行了我们的这些程序。:)
-std=c89
用来告诉gcc或clang根据该旧标准编译代码,该标准仅允许带警告的隐式int。
当比较值大于零时,可以简单地使用整数除法(/)来代替> =和<=,这样可以节省一个字符。例如:
putchar(c/32&&126/c?c:46); //Prints the character, but if it is unprintable print "."
当然,它仍然可以缩小,例如仅使用>和^(在某些情况下避免编写&&或||的聪明方法)。
putchar(c>31^c>126?c:46);
例如,整数除法将有助于确定数字是否小于100,因为这样可以节省字符:
a<100 vs 99/a
这在需要更高优先级的情况下也很好。
putchar(c>31&c<127?c:46);
某些编译器(例如GCC)允许您省略的基本#include
,参数和返回类型main
。
以下是使用GCC编译(带有警告)的有效C89和C99程序:
main(i) { printf("%d", i); }
请注意,#include
缺少for stdio.h,缺少for的返回类型,main
并且缺少for 的类型声明i
。
printf()
没有原型的调用(或任何可变参数函数)会导致未定义的行为。默认情况下,GCC不会编译标准C。如果以C89模式(gcc -ansi -pedantic
)或C99模式(gcc -std=c99 -pedantic
)调用gcc ,至少在后一种情况下,您会收到很多抱怨。
三元有条件的经营者?:
往往可以用作简单的一个立场if
- else
语句在相当大的节约。
与C ++等效项不同,该运算符不会正式产生lvalue,但是某些编译器(尤其是gcc)会让您无法使用它,这是一个不错的收获。
&&
,||
也可以使用:if(x==3)f()
随您的建议而定x==3?f():0
,可以进一步改进为x==3&&f()
。但是请注意运算符的优先级-如果f()
将替换为y=1
,则&&
解决方案需要使用一组额外的括号。
?:
产生一个左值。我可以在生产代码中使用它吗?大声笑
x==3&&f()
可以进一步打高尔夫球x^3||f()
http://graphics.stanford.edu/~seander/bithacks.html
有点不错。
~-x = x - 1
-~x = x + 1
但是具有不同的优先级,并且不要像++和-那样更改x。您也可以在特定情况下使用此功能:〜9比-10短。
if(!(x&y)) x | y == x ^ y == x + y
if(!(~x&y)) x ^ y == x - y
这比较神秘,但是我偶尔会使用它。如果您不关心短路
x*y == x && y
if(x!=-y) x+y == x || y
也:
if(x>0 && y>0) x/y == x>=y
(x/y) == (x>=y)
)非常有用。
代替
f(int*a,int*b){return*a>*b?1:-1;}
...
qsort(a,b,4,f);
或(仅限gcc)
qsort(a,b,4,({int L(int*a,int*b){a=*a>*b?1:-1;}L;}));
或(支持块的llvm)
qsort_b(a,b,4,^(const void*a,const void*b){return*(int*)a>*(int*)b?1:-1;});
尝试类似
qsort(a,b,4,"\x8b\7+\6\xc3");
...其中带引号的字符串包含“ lambda”函数的机器语言指令(符合所有平台ABI要求)。
这在字符串常量被标记为可执行的环境中起作用。默认情况下,在Linux和OSX中为true,但在Windows中为true。
学习编写自己的“ lambda”函数的一种愚蠢的方法是用C编写该函数,对其进行编译,使用类似的方法进行检查objdump -D
并将相应的十六进制代码复制到字符串中。例如,
int f(int*a, int*b){return *a-*b;}
... gcc -Os -c
针对Linux x86_64目标进行编译时会生成类似
0: 8b 07 mov (%rdi),%eax
2: 2b 06 sub (%rsi),%eax
4: c3 retq
goto
:您可以直接调用这些“ lambda函数”,但是如果您所调用的代码不带参数且不会返回,则可以goto
节省一些字节。所以代替
((int(*)())L"ﻫ")();
或者(如果您的环境没有阿拉伯字形)
((int(*)())L"\xfeeb")();
尝试
goto*&L"ﻫ";
要么
goto*&L"\xfeeb";
在此示例中,eb fe
x86机器语言用于类似for(;;);
的东西,并且是不带参数并且不会返回的简单例子:-)
事实证明,您可以goto
编写返回到调用方父级的代码。
#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));
}
上面的示例(可能使用编译并在Linux上运行gcc -O
)对堆栈布局很敏感。
编辑:根据您的工具链,您可能必须使用-zexecstack
编译标志。
如果不是立即可见,则此答案主要是针对lol编写的。通过阅读本文,我对打高尔夫球的好坏不承担任何心理责任。
使用游标而不是指针。brk()
在开始时抓取,并将其用作基本指针。
char*m=brk();
然后为内存访问创建一个#define。
#define M [m]
M
成为*
应用于整数的后缀。(旧的a [x] == x [a]技巧。)
但是,还有更多!然后,您可以拥有指针args并在比宏短的函数中返回(特别是如果您缩写“ return”):
f(x){return x M;} //implicit ints, but they work like pointers
#define f(x) (x M)
要从指针中创建光标,您需要减去基指针,得到一个ptrdiff_t,它会被截断为一个int,损失是多少。
char *p = sbrk(sizeof(whatever)) - m;
strcpy(m+p, "hello world");
我在为无类型的lambda演算编写解释器的答案中使用了此技术。
定义参数而不是变量。
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)
不是左值,因此不会编译。
三元运算符?:
的不同之处在于它具有两个独立的部分。因此,它为标准运算符优先级规则提供了一些漏洞。这对于避免括号很有用。
请看以下示例:
if (t()) a = b, b = 0; /* 15 chars */
通常的高尔夫方法是更换if
用&&
,但因为逗号操作符的优先级比较低,你需要一个额外的对括号:
t() && (a = b, b = 0); /* still 15 chars */
但是,三元运算符的中间部分不需要括号:
t() ? a = b, b = 0 : 0; /* 14 chars */
类似的注释适用于数组下标。
b-=a=b
甚至更短。这个?:
技巧仍然有用,-=
因为它的优先级也很低。
x>0||(y=3)
,x>0?0:(y=3)
没有用,但x<1?y=3:0
可以完成工作。
x>5?:y=1
这不是真正的标准C语言,但是可以与我所知道的每个编译器和CPU一起使用:
int sqr(int a){return a*a;}
具有与以下相同的效果:
int sqr(int a){a*=a;}
因为第一个参数与返回值存储在同一CPU寄存器中。
注意:如一条评论所述,这是未定义的行为,不能保证对每个操作都有效。而且任何编译器优化都将跳过它。
另一个有用的功能:X-Macros在您拥有变量列表并且需要执行一些涉及所有变量的操作时可以为您提供帮助:
-O0
始终选择在返回值寄存器中求值表达式。我至少已经在xcc,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)]
。
用于scanf("%*d ");
读取虚拟输入。(如果输入在其他程序中无意义),它比scanf("%d",&t);
您还需要声明变量t的地方短。
将字符存储在int数组中要比字符数组好得多。例。
s[],t;main(c){for(scanf("%*d ");~(c=getchar());s[t++]=c)putchar(s[t]);}
%*d
不仅在Golf中使用,因为它在scanf("%[^\n]%*c",str);
打印一个字符然后回车,而不是:
printf("%c\n",c);
要么
putchar(c);putchar('\n'); // or its ascii value, whatever!
简单地,将c声明为int并:
puts(&c);
puts(&c)
真的有用吗?那并不一定是空终止的。
char *
,我们看到了一个单字符串:字符Ç,后跟一个空字节。
使用asprintf()
可以节省显式分配,也可以测量字符串的长度char*
!对于代码编程,这可能不太有用,但是使用char数组可以简化日常工作。在21世纪C中还有更多不错的建议。
用法示例:
#define _GNU_SOURCE
#include <stdio.h>
int main(int argc, char** argv) {
char* foo;
asprintf(&foo, "%s", argv[1]);
printf("%s",foo);
}
这是我习惯的一些技巧。我无耻地从别人那里偷了他们,所以要相信我以外的任何人:
结合赋值和函数调用
代替这个:
r = /* Some random expression */
printf("%d", r);
做这个:
printf("%d", r = /* Some random expression */);
一起初始化多个变量(如果可能)
代替这个:
for(i=0,j=0;...;...){ /* ... */ }
做这个:
for(i=j=0;...;...){ /* ... */ }
折叠零/非零值
这是我从这里的某个人那里拿到的一个绝妙的把戏(不记得是谁,对不起)。当您具有整数值并且需要将其折叠为1或0时,可以!!
轻松地做到这一点。有时对于其他替代方案(如)有利?:
。
采取这种情况:
n=2*n+isupper(s[j])?1:0; /* 24 */
您可以改为:
n=n*2+!!isupper(s[j]); /* 22 */
另一个例子:
r=R+(memcmp(b+6,"---",3)?R:0); /* 30 */
可以改写为:
r=R+R*!!memcmp(b+6,"---",3)); /* 29 */
R*-~!!mxxxx