Lời khuyên cho việc chơi golf ở C


137

Bạn có lời khuyên chung nào cho việc chơi golf trong C? Tôi đang tìm kiếm những ý tưởng có thể được áp dụng cho các vấn đề về mã golf nói chung ít nhất là cụ thể đối với C (ví dụ: "xóa bình luận" không phải là một câu trả lời). Xin vui lòng gửi một lời khuyên cho mỗi câu trả lời. Ngoài ra, vui lòng bao gồm nếu mẹo của bạn áp dụng cho C89 và / hoặc C99 và nếu nó chỉ hoạt động trên một số trình biên dịch nhất định.


8
Tôi nghĩ rằng gợi ý một câu lớn nhất là: Đọc mã chiến thắng được gửi cho IOCCC.
vsz

Câu trả lời:


107

Sử dụng XOR bitwise để kiểm tra sự bất bình đẳng giữa các số nguyên:

if(a^b)thay vì if(a!=b)lưu 1 ký tự.


73
a-bmang lại cho bạn hiệu quả tương tự.
ugoren

22
Tương tự, bạn có thể sử dụng a*bthay vì a&&b(có độ ưu tiên khác nhau, có thể hoặc không xấu). Nếu bạn biết a / = -b (ví dụ: chúng không được ký) thì a||b==a+b
walpen

3
tốt hơn hết là kết hợp nó với Toán tử Elvis ?:(thay vì nếu): ví dụ như chỉ làm một cái gì đó nếu khác biệt: a^b?_diff_:;
Olivier Dulac

1
@OlivierDulac Có trình biên dịch nào chấp nhận một ternary trống nếu nhánh sai không?
Jonathan Frech

1
@OlivierDulac Bạn có thể kiểm tra. Từ những gì tôi biết, GCC có một ?:nhà điều hành tương đương vớia ? a : b
Chromium

75
  • mainDanh sách đối số của lạm dụng để khai báo một hoặc nhiều biến số nguyên:

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

    (trả lời Bảng chữ cái trong ngôn ngữ lập trình )

    Giải pháp này cũng lạm dụng thực tế là a(aka argc) bắt đầu như 1, với điều kiện chương trình được gọi mà không có đối số.

  • Sử dụng các biến toàn cục để khởi tạo mọi thứ về 0:

    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");}
    

    (câu trả lời cho Anagram Code Golf! )


62

Toán tử dấu phẩy có thể được sử dụng để thực thi nhiều biểu thức trong một khối trong khi tránh dấu ngoặc nhọn:

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");                                                                     

}

Đầu ra: 1 2


Không hoạt động nếu một trong các tuyên bố là break.
Maxim Mikhaylov

9
@MaxLawnboy vì breaklà một tuyên bố, và câu trả lời này là nói về biểu thức.
NieDzejkob

59

Tránh khai báo kiểu đối số chức năng thảm khốc

Nếu bạn đang khai báo một hàm trong đó cả năm đối số là ints, thì cuộc sống là tốt. bạn chỉ có thể viết

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

Nhưng giả sử dcần phải là một char, hoặc thậm chí là một int*. Sau đó, bạn đang say sưa! Nếu một tham số đứng trước một loại, tất cả chúng phải là:

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

Nhưng chờ đã! Có một cách xung quanh vụ nổ thảm khốc này của các nhân vật vô dụng. Nó như thế này:

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

Điều này thậm chí còn tiết kiệm cho một mainkhai báo tiêu chuẩn nếu bạn cần sử dụng các đối số dòng lệnh:

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

ngắn hơn hai byte

main(int c,char**v){

Tôi đã rất ngạc nhiên khi phát hiện ra điều này, vì cho đến nay tôi chưa gặp nó trên PPCG.


6
Tại sao trên trái đất làm việc đó ??
Nathaniel

30
Rõ ràng đây được gọi là phong cách K & R và nó đi trước ANSI C sau một thập kỷ.
Dennis

Lưu ý rằng việc sử dụng các tính năng K & R và các tính năng mới hơn (giả sử '99) cùng nhau hoặc có thể không thực hiện được. Phụ thuộc vào trình biên dịch của bạn.
dmckee

5
@dmckee nói đúng. C99 không cho phép int ngầm định, vì vậy bạn phải sử dụng -std=gnu99và bây giờ bạn không thể mang theo được. Trong clc-speak, bạn thậm chí không viết mã "C" mỗi se, mà là "Gnu99-C". 'Vòng ở đây chúng tôi chủ yếu bỏ qua điều đó, nhưng thật tốt khi đề cập đến nó nếu bạn đăng mã dành riêng cho trình biên dịch. Đôi khi người ta thực sự làm tải và thực hiện các chương trình của chúng ta. :)
luser droog 13/07/2015

@luserdroog: Bạn có thể sử dụng -std=c89để nói với gcc hoặc clang để biên dịch mã của bạn theo tiêu chuẩn cũ hơn, điều này cho phép ẩn int chỉ với một cảnh báo.
Peter Cordes

37

Thay vì> = và <= bạn chỉ có thể sử dụng phép chia số nguyên (/) khi các giá trị được so sánh ở trên 0, giúp lưu một ký tự. Ví dụ:

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

Tất nhiên vẫn có thể thu nhỏ được, ví dụ chỉ sử dụng> và ^ (một cách thông minh để tránh viết && hoặc || trong một số trường hợp).

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

Ví dụ, thủ thuật chia số nguyên rất hữu ích để quyết định xem một số có nhỏ hơn 100 hay không, vì điều này sẽ lưu một ký tự:

a<100 vs 99/a

Điều này cũng tốt trong trường hợp cần ưu tiên cao hơn.


Bạn có thể viếtputchar(c>31&c<127?c:46);
Jin X

37

Một số trình biên dịch nhất định, chẳng hạn như GCC, cho phép bạn bỏ qua các kiểu cơ bản #include, param và return cho main.

Sau đây là chương trình C89 và C99 hợp lệ biên dịch (có cảnh báo) với GCC:

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

Lưu ý rằng #includefor stdio.h bị thiếu, kiểu trả về cho mainbị thiếu và khai báo kiểu cho ibị thiếu.


17
Về mặt kỹ thuật, nó không hợp lệ theo các tiêu chuẩn vì chính chấp nhận không hoặc hai tham số, không phải một. Không phải ai cũng quan tâm đến mã golf.
Konrad Borowski

Gọi printf()(hoặc bất kỳ chức năng nào) mà không có nguyên mẫu gây ra hành vi không xác định . GCC không biên dịch chuẩn C theo mặc định. Nếu bạn gọi gcc ở chế độ C89 ( gcc -ansi -pedantic) hoặc chế độ C99 ( gcc -std=c99 -pedantic), bạn sẽ nhận được khá nhiều khiếu nại, ít nhất là trong trường hợp sau.
Nisse Engstrom

@ NisseEngström: các quy ước gọi về triển khai C chính thống giúp an toàn khi gọi các hàm matrixdic mà không cần nguyên mẫu. Vì vậy, hầu hết các triển khai C đều xác định hành vi.
Peter Cordes

29

Toán tử điều kiện ternary ?:thường có thể được sử dụng như là một thay thế cho các câu lệnh đơn giản if- elsevới mức tiết kiệm đáng kể.

Không giống như c ++ tương đương , toán tử không chính thức mang lại một giá trị , nhưng một số trình biên dịch (đáng chú ý là gcc) sẽ cho phép bạn thoát khỏi nó, đây là một phần thưởng tuyệt vời.


Bổ sung: Nếu bạn chỉ cần một if, nhưng không phải là một cái khác thì ternary vẫn có thể hữu ích.
Casey

9
&&||cũng có thể được sử dụng: if(x==3)f()trở thành với đề xuất của bạn x==3?f():0và có thể được cải thiện hơn nữa x==3&&f(). Nhưng hãy cẩn thận với quyền ưu tiên của toán tử - nếu f()được thay thế bằng y=1, thì &&giải pháp yêu cầu thêm một dấu ngoặc đơn.
ugoren

1
Tôi chưa bao giờ nhận ra rằng gcc ?:mang lại một giá trị. Tôi có thể sử dụng nó trong mã sản xuất? lol
Jeff Burdges

4
@ugoren: x==3&&f()có thể được chơi gôn thêmx^3||f()
fgrieu

@fgrieu, vâng, mặc dù nó không chính xác là chủ đề ở đây ( câu trả lời này cho thấy điều đó).
ugoren

27

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

Bits là tốt đẹp.

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

Nhưng với các ưu tiên khác nhau và không thay đổi x như ++ và -. Ngoài ra, bạn có thể sử dụng điều này trong các trường hợp thực sự cụ thể: ~ 9 ngắn hơn -10.

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

Đó là bí truyền hơn, nhưng tôi đã có dịp sử dụng nó. Nếu bạn không quan tâm đến ngắn mạch

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

Cũng thế:

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

5
Mẹo cuối cùng ( (x/y) == (x>=y)) thực sự hữu ích.
ugoren

24

Sử dụng lambdas (không thể di chuyển)

Thay vì

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

hoặc (chỉ gcc)

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

hoặc (llvm với khối hỗ trợ)

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

thử một cái gì đó như

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

... Trong đó chuỗi trích dẫn chứa các hướng dẫn ngôn ngữ máy của chức năng "lambda" của bạn (tuân thủ tất cả các yêu cầu ABI của nền tảng).

Điều này hoạt động trong môi trường trong đó các hằng chuỗi được đánh dấu thực thi. Theo mặc định, điều này đúng trong Linux và OSX nhưng không phải Windows.

Một cách ngớ ngẩn để học cách viết các hàm "lambda" của riêng bạn là viết hàm trong C, biên dịch nó, kiểm tra nó với một cái gì đó giống như objdump -Dvà sao chép mã hex tương ứng thành một chuỗi. Ví dụ,

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

... khi được biên dịch với gcc -Os -cmục tiêu Linux x86_64 sẽ tạo ra một cái gì đó như

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

GNU CC goto:

Bạn có thể gọi trực tiếp các "hàm lambda" này nhưng nếu mã bạn đang gọi không lấy tham số và sẽ không trả về, bạn có thể sử dụng gotođể lưu một vài byte. Vì vậy, thay vì

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

hoặc (nếu môi trường của bạn không có glyphs tiếng Ả Rập)

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

Thử

goto*&L"ﻫ";

hoặc là

goto*&L"\xfeeb";

Trong ví dụ này, eb felà ngôn ngữ máy x86 cho một cái gì đó giống như for(;;);và là một ví dụ đơn giản về thứ gì đó không lấy tham số và sẽ không trả về :-)

Hóa ra bạn có thể gotomã trở lại cho cha mẹ đang gọi.

#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));
}

Ví dụ trên (có thể biên dịch và chạy trên Linux với gcc -O) rất nhạy cảm với bố cục ngăn xếp.

EDIT: Tùy thuộc vào chuỗi công cụ của bạn, bạn có thể phải sử dụng -zexecstackcờ biên dịch.

Nếu nó không rõ ràng ngay lập tức, câu trả lời này chủ yếu được viết cho các lols. Tôi không chịu trách nhiệm cho việc chơi golf tốt hơn hoặc tồi tệ hơn hoặc kết quả tâm lý bất lợi từ việc đọc này.


2
Tôi vừa mới viết một tập lệnh để đọc các phần của hàm C từ tiêu chuẩn và in một lambda C. Có thể đáng để đề cập trong câu trả lời của bạn, có thể chỉ là tốt đẹp cho bạn để xem vì bạn đã dạy tôi làm điều này ngay từ đầu.
MD XF

23

Sử dụng con trỏ thay vì con trỏ. Snag the brk()ở đầu và sử dụng nó như là một con trỏ cơ sở .

char*m=brk();

Sau đó tạo #define để truy cập bộ nhớ.

#define M [m]

Mtrở thành một hậu tố *được áp dụng cho số nguyên. (Thủ thuật a [x] == x [a] cũ.)

Nhưng, còn nhiều hơn thế! Sau đó, bạn có thể có con trỏ đối số và trả về trong các hàm ngắn hơn macro (đặc biệt nếu bạn viết tắt 'return'):

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

Để tạo một con trỏ từ một con trỏ, bạn trừ con trỏ cơ sở, thu được một ptrdiff_t, mà cắt thành một int, tổn thất là yer biz.

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

Kỹ thuật này được sử dụng trong câu trả lời của tôi để Viết một trình thông dịch cho phép tính lambda chưa được đánh dấu .


21

Xác định tham số thay vì biến.

f(x){int y=x+1;...}

f(x,y){y=x+1;...}

Bạn không cần phải thực sự vượt qua tham số thứ hai.

Ngoài ra, bạn có thể sử dụng quyền ưu tiên của toán tử để lưu dấu ngoặc đơn.
Ví dụ, (x+y)*2có thể trở thành x+y<<1.


Hoặc chỉ x+y*2, tiết kiệm một char khác.
Braden hay nhất

4
@ B1KMusic, x+y*2không giống nhau, do quyền ưu tiên của nhà điều hành.
ugoren 2/214

Phải rồi, lol. Đó sẽ là x + (y * 2). Tôi đã cố định vào x+y<<1ví dụ, giả sử nó được đánh giá là x+(y<<1), và đề nghị *2thay thế. Tôi không biết các hoạt động của bithift được đánh giá như vd(x+y)<<2
Braden Best

20

Vì thông thường EOF == -1, hãy sử dụng toán tử bitwise NOT để kiểm tra EOF: while(~(c=getchar()))hoặc while(c=getchar()+1)sửa đổi giá trị của c ở mọi nơi


1
Tôi không biết rõ về C, nhưng sẽ không while(1+c=getchar())hiệu quả?
ɐɔıʇǝɥʇuʎs

6
@ ıʇǝɥʇuʎs Số Toán tử cộng +có độ ưu tiên cao hơn toán tử gán =, do đó, 1+c=getchar()tương đương với (1+c)=getchar(), không biên dịch vì (1+c)không phải là giá trị.
ace_HongKongInependence 19/05/2015

19

Toán tử ternary ?:khác thường ở chỗ nó có hai mảnh riêng biệt. Bởi vì điều này, nó cung cấp một chút lỗ hổng cho các quy tắc ưu tiên toán tử tiêu chuẩn. Điều này có thể hữu ích để tránh dấu ngoặc đơn.

Lấy ví dụ sau:

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

Cách tiếp cận chơi golf thông thường là thay thế ifbằng &&, nhưng vì mức độ ưu tiên thấp của toán tử dấu phẩy, bạn cần thêm một cặp dấu ngoặc đơn:

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

Mặc dù vậy, phần giữa của toán tử ternary không cần dấu ngoặc đơn:

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

Nhận xét tương tự áp dụng cho các mục con mảng.


7
Trong ví dụ này, b-=a=bthậm chí còn ngắn hơn. Các ?:Bí quyết là vẫn hữu ích, -=bởi vì còn có sở thích thấp.
ugoren

Điểm tốt; ví dụ của tôi rất phức tạp.
hộp bánh mì

Một điểm khác là đôi khi bạn muốn lật điều kiện: vì x>0||(y=3), x>0?0:(y=3)vô dụng, nhưng x<1?y=3:0thực hiện công việc.
ugoren

cả clang và gcc đều cho phép một trường hợp thực sự trống trong ternary. Nếu bỏ qua, giá trị của nó là giá trị của điều kiện. Ví dụ:x>5?:y=1
Chris Uzdavinis

19

Bất kỳ phần nào trong mã của bạn lặp lại nhiều lần là một ứng cử viên để thay thế bằng bộ xử lý trước.

#define R return

là trường hợp sử dụng rất phổ biến nếu mã của bạn liên quan đến nhiều hơn một vài hàm. Từ khóa hơi dài khác như while, double, switch, và casecũng là ứng cử viên; cũng như bất cứ điều gì là idomatic trong mã của bạn.

Tôi thường bảo lưu ký tự viết hoa cho mục đích này.


1
Một sự thay thế ngắn hơn sẽ được -DR=return. Lưu ý rằng nếu bạn bao gồm một số ký tự nhất định, có thể cần phải có dấu ngoặc đơn hoặc dấu ngoặc kép xung quanh định nghĩa -DP='puts("hello")'.

15

Nếu chương trình của bạn đang đọc hoặc viết trên từng bước một, hãy luôn cố gắng sử dụng chức năng đọcghi thay vì getchar ()putchar () .

Ví dụ ( Reverse stdin và đặt trên thiết bị xuất chuẩn )

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

Bài tập: Sử dụng kỹ thuật này để đạt điểm cao tại đây .


Bạn có ý nghĩa gì trong từng bước cơ bản ?
Casey

Casey: Tôi đoán họ có nghĩa là nếu chương trình đang đọc một cái gì đó, hoạt động trên nó và viết đầu ra. Trong một cách truyền phát, có thể nói như vậy. Trái ngược với cách tiếp cận trong đó tất cả các đầu vào phải được đọc và xử lý cùng một lúc.
Joey

Joey nói đúng, ý tôi là như vậy, xin lỗi tôi đã không kiểm tra hộp thư đến của mình cho đến hôm nay.
Quixotic

8
Thao tác ngăn xếp đó là tuyệt đẹp.
Andrea Biondo

14

Vòng lặp ngược

Nếu bạn có thể, hãy cố gắng thay thế

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

với

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


12

Sử dụng các giá trị trả về cho công cụ không. Nếu bạn gọi một số hàm và hàm đó trả về 0 trong các điều kiện bình thường, thì bạn có thể đặt nó ở vị trí không có số 0. Tương tự như vậy nếu bạn biết hàm sẽ trả về khác không, với việc thêm một tiếng nổ. Rốt cuộc, bạn không thực hiện xử lý lỗi thích hợp trong một mã golf trong mọi trường hợp, phải không?

Ví dụ:

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

12

Chỉ định thay vì trả lại.

Đây không thực sự là tiêu chuẩn C, nhưng hoạt động với mọi trình biên dịch và CPU mà tôi biết:

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

có tác dụng tương tự như:

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

Bởi vì đối số đầu tiên được lưu trữ vào cùng một thanh ghi CPU với giá trị trả về.

Lưu ý: Như đã lưu ý trong một nhận xét, đây là hành vi không xác định và không được đảm bảo để hoạt động cho mọi hoạt động. Và bất kỳ tối ưu hóa trình biên dịch sẽ chỉ bỏ qua nó.

X-Macros

Một tính năng hữu ích khác: X-Macros có thể giúp bạn khi bạn có một danh sách các biến và bạn cần thực hiện một số thao tác liên quan đến tất cả chúng:

https://en.wikipedia.org/wiki/X_Macro


3
Tôi đã trích dẫn điều này và tôi đã được sửa chữa, Điều này chỉ đơn giản là không đúng sự thật. Nó sẽ chỉ hoạt động với phép nhân và chia và khi tắt tối ưu hóa. Điều này là do cả hai hoạt động xảy ra để đưa kết quả của họ vào eax, đó là đăng ký chung để trả về. Các tham số được lưu trữ trong ngăn xếp hoặc ecx hoặc edx. Hãy thử nó.
Gaspa79

3
Bạn nói đúng, đó là hành vi không xác định, nó cũng phụ thuộc vào trình biên dịch và kiến ​​trúc, tôi thường kiểm tra với gcc trên x86 và armv7 trước khi đăng bất kỳ câu trả lời nào bằng thủ thuật này. Và tất nhiên nếu bạn kích hoạt tối ưu hóa, bất kỳ trình biên dịch thông minh nào cũng sẽ xóa các phép nhân không cần thiết.
GB

3
Tôi đã thấy công việc này với GCC nhưng không phải là người khác
Albert Renshaw

1
@ Gaspa79: gcc -O0luôn chọn cách đánh giá các biểu thức trong thanh ghi giá trị trả về. Tôi đã xem ít nhất x86, ARM và MIPS (trên gcc.godbolt.org ) và gcc dường như không còn cách nào để làm điều đó -O0. Nhưng hãy nhớ rằng nếu bạn tận dụng lợi thế này, ngôn ngữ mà bạn đang lập trình trong là gcc -O0, không phải C , và bạn nên đặt tên câu trả lời của bạn cho phù hợp, không phải là C . Nó không thành công ở bất kỳ mức tối ưu hóa nào ngoài -O0chế độ gỡ lỗi và không hoạt động với clang IIRC.
Peter Cordes

11
  1. Sử dụng *athay vì a[0]truy cập phần tử đầu tiên của một mảng.

  2. Toán tử quan hệ ( !=, >, vv) cho 0hay 1. Sử dụng điều này với các toán tử số học để đưa ra các mức bù khác nhau tùy thuộc vào điều kiện là đúng hay sai: a[1+2*(i<3)]sẽ truy cập a[1]nếu i >= 3a[3]nếu không.


11
a[i<3?3:1]là hai ký tự ngắn hơn a[1+2*(i<3)].
Reto Koradi

10

Bạn có thể xem xét tài liệu lưu trữ của IOCCC (cuộc thi mã C bị xáo trộn quốc tế).

Một mẹo đáng chú ý là #define macro có phần mở rộng có dấu ngoặc / dấu ngoặc không cân bằng, như

#define P printf(

16
Các dấu ngoặc không khớp không có giá trị trong chính chúng. Vấn đề là xác định càng nhiều mẫu lặp lại càng tốt. Bạn có thể muốn đi xa hơn, với #define P;printf(.
ugoren

Làm thế nào để rút ngắn số byte này? Có lẽ cung cấp một ví dụ?
Cyoce

2
@Cyoce Xem ví dụ câu trả lời này .
Jonathan Frech

8

for(int i=0;i<n;i++){a(i);b(i);} có thể được thực hiện ngắn hơn một vài cách:

for(int i=0;i<n;){a(i);b(i++);} -1 để di chuyển ++đến cuối cùng itrong vòng lặp

for(int i=0;i<n;b(i++))a(i); -3 thêm để di chuyển tất cả trừ một câu lệnh vào đầu và ra khỏi vòng lặp chính, loại bỏ các dấu ngoặc


Sử dụng toán tử dấu phẩy là một cách khác để tránh niềng răng trong một số trường hợp.
Peter Cordes

8

Đi chức năng!

Nếu bạn có thể giảm vấn đề của mình thành các hàm đơn giản có cùng chữ ký và được định nghĩa là các biểu thức đơn, thì bạn có thể làm tốt hơn #define r returnvà loại bỏ gần như tất cả các bảng mẫu để xác định hàm.

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

Kết quả chương trình là giá trị trạng thái của nó được trả về cho HĐH hoặc shell hoặc IDE kiểm soát.

Việc sử dụng __VA_ARGS__cho phép bạn sử dụng toán tử dấu phẩy để giới thiệu các điểm chuỗi trong các biểu thức hàm này . Nếu điều này là không cần thiết, macro có thể ngắn hơn.

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

7
  1. sử dụng scanf("%*d ");để đọc đầu vào giả. (trong trường hợp đầu vào là vô nghĩa trong chương trình tiếp theo) thì nó ngắn hơn scanf("%d",&t);nơi bạn cũng cần khai báo biến t.

  2. lưu trữ các ký tự trong mảng int tốt hơn nhiều so với mảng ký tự. thí dụ.

    s[],t;main(c){for(scanf("%*d ");~(c=getchar());s[t++]=c)putchar(s[t]);}


2
Trên thực tế, tôi %*dkhông chỉ sử dụng trong Golf vì nó cũng hữu ích trong các tình huống, ví dụ như người ta muốn bỏ qua một dòng mới trong scanf("%[^\n]%*c",str);:)
tomsmeding

6

In một ký tự sau đó vận chuyển trở lại, thay vì:

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

hoặc là

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

đơn giản, khai báo c là một int và:

puts(&c);

9
Có lẽ đáng để chỉ ra rằng điều này phụ thuộc vào một kiến ​​trúc cuối cùng nhỏ. Nếu c là một int endian lớn, thì bạn sẽ nhận được vận chuyển trở lại. (Mặt khác, nếu c là char, bạn có thể nhận được rác ngẫu nhiên sau khi thay vì trả lại xe ngựa.)
Breadbox 17/12/13

@breadbox yep, bạn hoàn toàn đúng; Tôi vừa chỉnh sửa: đoạn trích cuối nên sử dụng c như một int (thường dễ khai báo như vậy).
moala

puts(&c)thực sự làm việc? Điều đó sẽ không nhất thiết bị chấm dứt.
Trái cây Esolanging

1
@EsolangingFnut Trên một endian nhỏ với ints 32 bit, một int 0 c <256 được lưu dưới dạng chuỗi byte c 0 0 0 . Khi thông dịch địa chỉ của cchar *, chúng ta thấy một chuỗi singleton: ký tự c , theo sau là một byte null.
Dennis

6

Sử dụng asprintf()giúp bạn tiết kiệm phân bổ rõ ràng và cũng đo chiều dài của chuỗi aka char*! Điều này có thể không quá hữu ích cho việc chơi golf mã, nhưng làm giảm bớt công việc hàng ngày với một mảng char. Có một số lời khuyên tốt hơn trong Thế kỷ 21 .

Ví dụ sử dụng:

#define _GNU_SOURCE
#include <stdio.h>

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

6

import tôi muốn có nó

Như đã lưu ý trong câu trả lời đầu tiên , một số trình biên dịch (đáng chú ý là GCC và clang) cho phép bạn thoát khỏi việc bỏ qua #includecác chức năng thư viện tiêu chuẩn.

Ngay cả khi bạn không thể loại bỏ nó #include, có thể có những cách khác để tránh nó , nhưng điều đó không phải lúc nào cũng thực tế hoặc đặc biệt là chơi gôn.

Trong các trường hợp còn lại, bạn có thể sử dụng #import<header file>thay vì #include<header file>để lưu một byte. Đây là một phần mở rộng GNU và nó được coi là không dùng nữa, nhưng nó hoạt động ít nhất trong gcc 4.8, gcc 5.1 và clang 3.7.


6

Hãy thử cpow()thay vìcos()

Thay vì

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

thử một cái gì đó như

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

Điều này sử dụng công thức của Euler , một phân tích phức tạp nhỏ và quan sát rằng việc gán một số phức cho một số mang lại phần thực (cẩn thận với các lệnh gọi hàm biến đổi và các phép tinh tế khác).

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

Loại thủ thuật này có thể được sử dụng để giảm

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

vào

double y=cpow(1i,x);

bởi vì (-1) ^ \ frac {x} {2} = j ^ {\ frac {2x} {2}} = j ^ x


5

Dưới đây là một vài lời khuyên tôi đã sử dụng để lợi thế của tôi. Tôi đã xấu hổ đánh cắp chúng từ những người khác, vì vậy tín dụng cho bất cứ ai trừ tôi:

Kết hợp gán với các lệnh gọi hàm

Thay vì điều này:

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

Làm cái này:

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

Khởi tạo nhiều biến với nhau (khi có thể)

Thay vì điều này:

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

Làm cái này:

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

Thu gọn giá trị 0 / khác không

Đây là một mẹo gọn gàng tôi nhặt được từ một người nào đó ở đây (đừng nhớ ai, xin lỗi). Khi bạn có một giá trị số nguyên và bạn cần thu gọn nó thành 1 hoặc 0, bạn có thể sử dụng !!để làm điều đó một cách dễ dàng. Điều này đôi khi là lợi thế cho các lựa chọn thay thế khác như ?:.

Hãy xem tình huống này:

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

Thay vào đó bạn có thể làm điều này:

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

Một vi dụ khac:

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

Có thể được viết lại thành:

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

1
có lẽR*-~!!mxxxx
l4m2

5

Biết các đẳng thức logic cơ bản có thể có thể lưu một vài byte. Chẳng hạn, thay vì if (!(a&&b)){}thử thay vì sử dụng luật của DeMorgan if (!a||!b){}. Điều tương tự áp dụng cho các hàm bitwise: thay vì ~(a|b)làm ~a&~b.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.