เคล็ดลับสำหรับการเล่นกอล์ฟใน C.


137

คุณมีเคล็ดลับทั่วไปสำหรับการเล่นกอล์ฟใน C หรือไม่? ฉันกำลังมองหาแนวคิดที่สามารถนำไปใช้กับปัญหารหัสกอล์ฟโดยทั่วไปซึ่งอย่างน้อยค่อนข้างเฉพาะเจาะจงกับ C (เช่น "ลบความคิดเห็น" ไม่ใช่คำตอบ) กรุณาโพสต์หนึ่งเคล็ดลับต่อคำตอบ นอกจากนี้โปรดระบุว่าเคล็ดลับของคุณใช้กับ C89 และ / หรือ C99 และใช้ได้กับคอมไพเลอร์บางตัวเท่านั้น


8
ฉันคิดว่าคำใบ้ประโยคเดียวที่ใหญ่ที่สุดคือ: อ่านรหัสที่ชนะที่ส่งไปยัง IOCCC
vsz

คำตอบ:


107

ใช้ bitwise XOR เพื่อตรวจสอบความไม่เท่าเทียมกันระหว่างจำนวนเต็ม:

if(a^b)แทนที่จะif(a!=b)บันทึก 1 ตัวอักษร


73
a-bให้เอฟเฟกต์แบบเดียวกันกับคุณ
ugoren

22
ในทำนองเดียวกันคุณสามารถใช้a*bแทนa&&b(มีแนวโน้มที่แตกต่างกันอาจหรือไม่เลว) ถ้าคุณรู้ a / = -b (เช่นพวกเขาไม่ได้ลงชื่อ) ดังนั้นa||b==a+b
walpen

3
ดีกว่ายังรวมเข้ากับ Elvis Operator ?:(แทนถ้า): สำหรับตัวอย่างเพียงทำบางสิ่งบางอย่างถ้า differents: a^b?_diff_:;
Olivier Dulac

1
@OlivierDulac มีคอมไพเลอร์ที่ยอมรับ ternary ที่ว่างเปล่าหากเป็นสาขาที่ผิดหรือเปล่า
Jonathan Frech

1
@OlivierDulac คุณสามารถตรวจสอบได้ จากสิ่งที่ฉันรู้ GCC มี?:โอเปอเรเตอร์ซึ่งเทียบเท่ากับa ? a : b
Chromium

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

    (ตอบกลับAnagram Code Golf! )


62

ตัวดำเนินการคอมม่าสามารถใช้เพื่อดำเนินการหลายนิพจน์ในบล็อกเดียวในขณะที่หลีกเลี่ยงการจัดฟัน:

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ไม่ทำงานถ้าหนึ่งของงบการเงินคือ
Maxim Mikhaylov

9
@ MaxLawnboy เพราะbreakเป็นคำพูดและคำตอบนี้พูดถึงการแสดงออก
NieDzejkob

59

หลีกเลี่ยงการประกาศประเภทอาร์กิวเมนต์ฟังก์ชันหายนะ

หากคุณกำลังประกาศฟังก์ชั่นที่ข้อโต้แย้งทั้งห้าคือints ดังนั้นชีวิตก็ดี คุณสามารถเขียนได้

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


6
ทำไมบนโลกถึงใช้งานได้
นาธาเนียล

30
เห็นได้ชัดว่าสิ่งนี้เรียกว่าสไตล์ K&R และนำหน้า ANSI C ในทศวรรษหน้า
เดนนิส

โปรดทราบว่าการใช้คุณสมบัติ K&R และคุณลักษณะที่ใหม่กว่า (พูด '99) ไม่สามารถใช้ร่วมกันได้หรืออาจเป็นไปไม่ได้ ขึ้นอยู่กับคอมไพเลอร์ของคุณ
dmckee

5
@dmckee ถูกต้อง C99 ไม่อนุญาตให้ใช้ int โดยปริยายดังนั้นคุณต้องใช้งาน-std=gnu99และตอนนี้คุณไม่ได้พกพา ใน clc-speak คุณไม่ได้เขียนรหัส "C" ต่อ se แต่ใช้ "Gnu99-C" 'รอบที่นี่เราส่วนใหญ่ไม่สนใจ แต่ก็เป็นเรื่องดีที่จะพูดถึงมันถ้าคุณโพสต์รหัสที่เฉพาะเจาะจงคอมไพเลอร์ บางครั้งคนจริงทำดาวน์โหลดและรันโปรแกรมเหล่านี้ของเรา :)
luser droog

@luserdroog: คุณสามารถใช้-std=c89เพื่อบอก gcc หรือเสียงดังกราวเพื่อรวบรวมรหัสของคุณตามมาตรฐานที่เก่ากว่าซึ่งจะช่วยให้ int นัยโดยมีคำเตือนเท่านั้น
Peter Cordes

37

แทน> = และ <= คุณสามารถใช้การหารจำนวนเต็ม (/) เมื่อค่าที่เปรียบเทียบนั้นสูงกว่าศูนย์ซึ่งจะบันทึกอักขระหนึ่งตัว ตัวอย่างเช่น:

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

ซึ่งแน่นอนว่ายังคงลดขนาดลงได้ใช้ตัวอย่างเช่น just> และ ^ (วิธีที่ชาญฉลาดเพื่อหลีกเลี่ยงการเขียน && หรือ | | ในบางกรณี)

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

เคล็ดลับการแบ่งจำนวนเต็มเป็นตัวอย่างที่มีประโยชน์ในการตัดสินใจว่าตัวเลขน้อยกว่า 100 หรือไม่เนื่องจากนี่เป็นการบันทึกอักขระ:

a<100 vs 99/a

นี่ก็เป็นสิ่งที่ดีในกรณีที่จำเป็นต้องมีลำดับความสำคัญสูงกว่า


คุณสามารถเขียนputchar(c>31&c<127?c:46);
Jin X

37

คอมไพเลอร์บางตัวเช่น GCC อนุญาตให้คุณละเว้นประเภทพื้นฐาน#includeพารามิเตอร์และประเภทส่งคืนmainได้

ต่อไปนี้เป็นโปรแกรม C89 และ C99 ที่ถูกต้องซึ่งรวบรวม (พร้อมคำเตือน) กับ GCC:

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

ขอให้สังเกตว่า#includeสำหรับ stdio.h หายไปชนิดส่งคืนสำหรับที่mainขาดหายไปและการประกาศประเภทสำหรับiหายไป


17
ในทางเทคนิคมันไม่ถูกต้องตามมาตรฐานที่เป็นหลักยอมรับพารามิเตอร์ศูนย์หรือสองไม่ใช่หนึ่ง ไม่ใช่ว่าทุกคนสนใจในรหัสกอล์ฟ
Konrad Borowski

โทรprintf()(หรือฟังก์ชั่น variadic) โดยต้นแบบที่ทำให้เกิดพฤติกรรมที่ไม่ได้กำหนด GCC ไม่ได้รวบรวมมาตรฐาน C เป็นค่าเริ่มต้น หากคุณเรียกใช้ gcc ในโหมด C89 ( gcc -ansi -pedantic) หรือโหมด C99 ( gcc -std=c99 -pedantic) คุณจะได้รับการร้องเรียนค่อนข้างน้อยอย่างน้อยก็ในกรณีหลัง
Nisse Engström

@ NisseEngström: อนุสัญญาการเรียกร้องให้เกิดการใช้งาน C หลักทำให้ปลอดภัยในการเรียกใช้ฟังก์ชัน Variadic ที่ไม่มีต้นแบบ ดังนั้นการใช้งาน C ส่วนใหญ่จะกำหนดพฤติกรรม
Peter Cordes

29

ผู้ประกอบการที่มีเงื่อนไข ternary ?:มักจะสามารถใช้เป็นขาตั้งในที่เรียบง่ายif- elseงบที่เงินออมมาก

ซึ่งแตกต่างจากc ++ ที่เทียบเท่ากันตัวดำเนินการไม่ได้ให้ค่า lvalue อย่างเป็นทางการแต่คอมไพเลอร์บางตัว (โดยเฉพาะอย่างยิ่ง gcc) จะช่วยให้คุณหนีไปได้ซึ่งเป็นโบนัสที่ดี


นอกจากนี้: หากคุณต้องการเพียง if แต่ไม่ใช่อย่างอื่น ternary ยังคงมีประโยชน์
Casey

9
&&และ||ยังสามารถใช้: if(x==3)f()กลายเป็นกับคำแนะนำของคุณและสามารถนำมาปรับปรุงให้ดีขึ้นต่อไปx==3?f():0 x==3&&f()แต่ระวังด้วยความสำคัญของโอเปอเรเตอร์ - หากf()ถูกแทนที่ด้วยy=1ดังนั้นการ&&แก้ปัญหาต้องใช้วงเล็บชุดพิเศษ
ugoren

1
ฉันไม่เคยรู้เลยว่า gcc ?:จะให้ผลที่คุ้มค่า ฉันสามารถใช้สิ่งนั้นในรหัสการผลิตได้หรือไม่ lol
Jeff Burdges

4
@ugoren: x==3&&f()สามารถเล่นกอล์ฟเพิ่มเติมได้ที่x^3||f()
fgrieu

@fgrieu ใช่แม้ว่ามันจะไม่ตรงกับหัวข้อที่นี่ ( คำตอบนี้แสดงให้เห็น)
ugoren

27

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

บิตเป็นสิ่งที่ดี

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

แต่มีลำดับความสำคัญต่างกันและอย่าเปลี่ยน x like ++ และ - นอกจากนี้คุณสามารถใช้สิ่งนี้ในกรณีเฉพาะจริง ๆ : ~ 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   

5
เคล็ดลับสุดท้าย ( (x/y) == (x>=y)) มีประโยชน์จริงๆ
ugoren

24

ใช้ lambdas (unportable)

แทน

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

... โดยที่สตริงที่ยกมามีคำแนะนำภาษาเครื่องของฟังก์ชั่น "แลมบ์ดา" ของคุณ (เป็นไปตามข้อกำหนด ABI ของแพลตฟอร์มทั้งหมด)

ใช้งานได้ในสภาพแวดล้อมที่ค่าคงที่ของสตริงถูกทำเครื่องหมายว่าสามารถเรียกทำงานได้ โดยค่าเริ่มต้นสิ่งนี้เป็นจริงใน Linux และ OSX แต่ไม่ใช่ Windows

วิธีหนึ่งที่โง่ที่จะเรียนรู้ที่จะเขียนฟังก์ชั่น "แลมบ์ดา" ของคุณเองคือการเขียนฟังก์ชั่นใน 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

GNU CC goto:

คุณสามารถเรียก "ฟังก์ชั่นแลมบ์ดา" เหล่านี้โดยตรง แต่ถ้ารหัสที่คุณโทรไม่ได้รับพารามิเตอร์และจะไม่ส่งคืนคุณสามารถใช้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) มีความไวต่อเค้าโครงสแต็ก

แก้ไข: ขึ้นอยู่กับ toolchain ของคุณคุณอาจต้องใช้การ-zexecstackรวบรวมธง

หากไม่ชัดเจนในทันทีคำตอบนี้ถูกเขียนขึ้นสำหรับ lols เป็นส่วนใหญ่ ฉันไม่รับผิดชอบต่อการตีกอล์ฟที่ดีขึ้นหรือแย่ลงหรือผลลัพธ์ทางจิตวิทยาที่ไม่พึงประสงค์จากการอ่านนี้


2
ฉันเพิ่งเขียนสคริปต์เพื่ออ่านบางส่วนของฟังก์ชั่น C จากมาตรฐานในและพิมพ์แลมบ์ดา C อาจจะคุ้มค่าที่จะพูดถึงในคำตอบของคุณอาจเป็นเรื่องดีที่คุณจะได้เห็นตั้งแต่คุณสอนให้ฉันทำสิ่งนี้ตั้งแต่แรก
MD XF

23

ใช้เคอร์เซอร์แทนพอยน์เตอร์ ขัดขวางbrk()จุดเริ่มต้นและใช้เป็นตัวชี้พื้นฐาน

char*m=brk();

จากนั้นสร้าง #define สำหรับการเข้าถึงหน่วยความจำ

#define M [m]

Mกลายเป็น postfix ที่*ใช้กับจำนวนเต็ม (เคล็ดลับแบบเก่า [x] == x [a])

แต่มีอีกมาก! จากนั้นคุณสามารถมีตัวชี้ args และ return ในฟังก์ชันที่สั้นกว่าแมโคร (โดยเฉพาะถ้าคุณย่อ 'return'):

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

ในการสร้างเคอร์เซอร์จากตัวชี้คุณจะลบตัวชี้ฐานโดยยอมให้ ptrdiff_t ซึ่งตัดเข้าสู่ int การสูญเสียคือ yer biz

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

เทคนิคนี้ใช้ในคำตอบของฉันในการเขียนล่ามสำหรับแคลคูลัสแลมบ์ดาที่ไม่ได้พิมพ์ออกมา


21

กำหนดพารามิเตอร์แทนตัวแปร

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

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

คุณไม่จำเป็นต้องผ่านพารามิเตอร์ตัวที่สอง

นอกจากนี้คุณสามารถใช้ตัวดำเนินการที่มีความสำคัญเพื่อบันทึกวงเล็บ
ยกตัวอย่างเช่นจะกลายเป็น(x+y)*2x+y<<1


หรือเพียงแค่x+y*2บันทึกตัวละครอื่น
Braden สุดยอด

4
@ B1KMusic x+y*2ไม่เหมือนกันเนื่องจากความสำคัญของผู้ให้บริการ
ugoren

ใช่แล้ว นั่นจะเป็น x + (y * 2) ฉันได้รับการแก้ไขในx+y<<1ตัวอย่างสมมติว่ามันกำลังได้รับการประเมินx+(y<<1)และแนะนำให้ใช้*2แทน ฉันไม่ทราบว่ามีการประเมินการทำงานของบิตเชนเช่น(x+y)<<2
Braden Best

20

เนื่องจากโดยทั่วไปEOF == -1ให้ใช้ตัวดำเนินการ bitwise NOT เพื่อตรวจสอบ EOF: while(~(c=getchar()))หรือwhile(c=getchar()+1)และปรับเปลี่ยนค่าของ c ในทุก ๆ ที่


1
ฉันไม่รู้จัก C ดีพอ แต่จะไม่while(1+c=getchar())ทำงานเหรอ
ɐɔıʇǝɥʇuʎs

6
@ ɐɔıʇǝɥʇuʎsไม่ผู้ดำเนินการเพิ่ม+มีความสำคัญสูงกว่าผู้ดำเนินการที่ได้รับมอบหมาย=ดังนั้นจึง1+c=getchar()เท่ากับ(1+c)=getchar()ซึ่งไม่ได้รวบรวมเพราะ(1+c)ไม่ใช่ lvalue
ace_HongKongIndependence

19

ผู้ประกอบการที่?:ผิดปกติในการที่มันมีสองชิ้นแยกกัน ด้วยเหตุนี้จึงมีช่องโหว่เล็กน้อยสำหรับกฎสำคัญกว่าของผู้ให้บริการมาตรฐาน สิ่งนี้มีประโยชน์ในการหลีกเลี่ยงวงเล็บ

นำตัวอย่างต่อไปนี้:

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 */

ความคิดเห็นที่คล้ายกันนี้ใช้กับตัวห้อยของอาร์เรย์


7
ในตัวอย่างนี้b-=a=bจะยิ่งสั้นลง ?:เคล็ดลับคือยังคงเป็นประโยชน์-=เพราะยังมีการตั้งค่าที่ต่ำ
ugoren

จุดดี; ตัวอย่างของฉันซับซ้อนโดยไม่จำเป็น
breadbox

ประเด็นก็คือว่าบางครั้งคุณต้องการที่จะพลิกเงื่อนไข: สำหรับx>0||(y=3), x>0?0:(y=3)ไม่มีประโยชน์ แต่x<1?y=3:0ไม่ทำงาน
ugoren

ทั้งเสียงดังกราวและ gcc อนุญาตให้มีกรณีจริงที่ว่างเปล่าในไตรภาค หากไม่ระบุค่าจะเป็นค่าของเงื่อนไข ตัวอย่างเช่นx>5?:y=1
Chris Uzdavinis

19

ส่วนใด ๆ ของรหัสของคุณที่ทำซ้ำหลาย ๆ ครั้งเป็นตัวเลือกให้แทนที่ด้วยตัวประมวลผลล่วงหน้า

#define R return

เป็นกรณีที่ใช้กันอย่างแพร่หลายหากรหัสของคุณเกี่ยวข้องกับฟังก์ชันมากกว่าสองฟังก์ชัน คำหลักอื่น ๆ เช่น longish while, double, switchและcaseนอกจากนี้ยังมีผู้สมัคร; รวมถึงสิ่งใดก็ตามที่เป็นรหัสในรหัสของคุณ

โดยทั่วไปฉันจองตัวอักษรตัวพิมพ์ใหญ่เพื่อจุดประสงค์นี้


1
การทดแทนที่สั้นกว่าก็-DR=returnคือ -DP='puts("hello")'โปรดทราบว่าถ้าคุณมีอักขระบางอย่างก็อาจจะกลายเป็นความจำเป็นต้องมีคำพูดเดียวหรือสองรอบกำหนด

15

ถ้าโปรแกรมของคุณกำลังอ่านหรือเขียนในหนึ่งในแต่ละขั้นตอนพื้นฐานมักจะพยายามที่จะใช้อ่านและเขียนฟังก์ชั่นแทนgetchar ()และputchar ()

ตัวอย่าง ( ย้อนกลับ stdin และวางบน stdout )

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

การออกกำลังกาย: ใช้เทคนิคนี้จะได้รับคะแนนดีที่นี่


ในแต่ละขั้นตอนคุณหมายถึงอะไร
Casey

Casey: ฉันคิดว่าพวกเขาหมายถึงว่าโปรแกรมกำลังอ่านบางอย่างทำงานกับมันและเขียนเอาต์พุต ในลักษณะการสตรีมดังนั้นต้องบอกว่า ตรงข้ามกับวิธีการป้อนข้อมูลทั้งหมดที่จะต้องอ่านและจัดการในครั้งเดียว
Joey

โจอี้พูดถูกฉันหมายถึงเหมือนกันขอโทษที่ฉันไม่ได้เช็คอินกล่องจดหมายของฉันจนถึงวันนี้
Quixotic

8
การจัดการกองซ้อนนั้นงดงาม
Andrea Biondo

14

ย้อนกลับลูป

หากทำได้ให้ลองเปลี่ยนใหม่

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

กับ

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


12

ใช้ประโยชน์จากค่าส่งคืนเป็นศูนย์ หากคุณเรียกใช้ฟังก์ชันและฟังก์ชันนั้นคืนค่าศูนย์ภายใต้สภาวะปกติคุณสามารถวางฟังก์ชันนี้ในตำแหน่งที่คาดว่าจะเป็นศูนย์ ในทำนองเดียวกันถ้าคุณรู้ว่าฟังก์ชั่นจะกลับมาที่ไม่ใช่ศูนย์ด้วยการเพิ่มปัง ท้ายที่สุดคุณไม่ได้จัดการข้อผิดพลาดที่เหมาะสมในการเล่นกอล์ฟในกรณีใด ๆ ใช่ไหม?

ตัวอย่าง:

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

12

กำหนดแทนการส่งคืน

นี่ไม่ใช่มาตรฐาน C จริงๆ แต่ทำงานได้กับคอมไพเลอร์และซีพียูทุกอย่างที่ฉันรู้:

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

มีผลเช่นเดียวกับ:

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

เพราะอาร์กิวเมนต์แรกถูกเก็บไว้ใน CPU register เหมือนกันกับค่าส่งคืน

หมายเหตุ: ดังที่ระบุไว้ในความคิดเห็นเดียวนี่เป็นพฤติกรรมที่ไม่ได้กำหนดและไม่รับประกันว่าจะสามารถใช้งานได้กับทุกการทำงาน และการเพิ่มประสิทธิภาพคอมไพเลอร์ใด ๆ ก็จะข้ามมัน

X-แมโคร

คุณสมบัติที่มีประโยชน์อื่น: X-Macros สามารถช่วยคุณได้เมื่อคุณมีรายการตัวแปรและคุณจำเป็นต้องดำเนินการบางอย่างที่เกี่ยวข้องกับพวกเขาทั้งหมด:

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


3
ฉันอ้างถึงสิ่งนี้และฉันได้รับการแก้ไขนี่ไม่จริงเลย มันจะทำงานกับการคูณและดิวิชั่นเท่านั้นและเมื่อปิดการปรับให้เหมาะสม เนื่องจากการดำเนินการทั้งสองเกิดขึ้นเพื่อใส่ผลลัพธ์ของพวกเขาใน eax ซึ่งเป็นทะเบียนทั่วไปสำหรับการกลับมา พารามิเตอร์ถูกเก็บไว้ใน stack หรือ ecx หรือ edx ลองด้วยตัวคุณเอง
Gaspa79

3
คุณถูกต้องมันเป็นพฤติกรรมที่ไม่ได้กำหนดขึ้นอยู่กับคอมไพเลอร์และสถาปัตยกรรมฉันมักจะตรวจสอบกับ gcc ใน x86 และ armv7 ก่อนโพสต์คำตอบใด ๆ โดยใช้เคล็ดลับนี้ และแน่นอนถ้าคุณเปิดใช้งานการปรับให้เหมาะสมตัวแปลอัจฉริยะใด ๆ ก็แค่ลบการคูณที่ไม่จำเป็นออกไป
GB

3
ฉันเคยเห็นงานนี้กับ GCC แต่ไม่ใช่คนอื่น ๆ
Albert Renshaw

1
@ Gaspa79: gcc พร้อม-O0เลือกที่จะประเมินค่านิพจน์เสมอในการลงทะเบียนค่าตอบแทน ฉันมองไปที่ x86, ARM และ MIPS อย่างน้อย (ในgcc.godbolt.org ) และ GCC -O0ดูเหมือนว่าจะออกจากทางที่จะทำเช่นนั้นที่ไป แต่จำไว้ว่าถ้าคุณใช้ประโยชน์จากนี้ภาษาที่คุณกำลังเขียนโปรแกรมในมีgcc -O0, ไม่ Cและคุณควรติดป้ายคำตอบของคุณตามไม่ได้เป็น C มันล้มเหลวที่ระดับการปรับให้เหมาะสมอื่น ๆ นอกเหนือจาก-O0โหมดดีบักและไม่ทำงานกับ clang IIRC
Peter Cordes

11
  1. ใช้*aแทนa[0]การเข้าถึงองค์ประกอบแรกของอาร์เรย์

  2. ผู้ประกอบการเชิงสัมพันธ์ ( !=, >ฯลฯ ) ให้หรือ0 1การใช้งานนี้กับผู้ประกอบการทางคณิตศาสตร์ที่จะให้การชดเชยที่แตกต่างกันขึ้นอยู่กับว่าอยู่ในสภาพที่เป็นจริงหรือเท็จ: a[1+2*(i<3)]จะเข้าถึงa[1]ถ้าi >= 3และa[3]เป็นอย่างอื่น


11
a[i<3?3:1]a[1+2*(i<3)]เป็นตัวละครสองตัวที่สั้นกว่า
Reto Koradi

10

คุณสามารถดูเอกสารสำคัญของ IOCCC ได้ (การประกวดรหัส C ที่ซับซ้อนทั่วโลก)

เคล็ดลับหนึ่งที่น่าสังเกตคือ #define macros ที่การขยายตัวมีวงเล็บปีกกา / วงเล็บไม่สมดุลเช่น

#define P printf(

16
วงเล็บที่ไม่ตรงกันไม่มีค่าในตัวเอง จุดคือการกำหนดรูปแบบการทำซ้ำให้ได้มากที่สุด #define P;printf(คุณอาจต้องการที่จะไปต่อกับ
ugoren

การนับจำนวนไบต์สั้นลงอย่างไร บางทีอาจเป็นตัวอย่าง
Cyoce

2
@Cyoce ดูตัวอย่างคำตอบนี้
Jonathan Frech

8

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 เพิ่มเติมสำหรับการย้ายคำสั่งทั้งหมดยกเว้นหนึ่งคำลงในด้านบนและออกจากลูปหลัก


การใช้เครื่องหมายจุลภาคเป็นอีกวิธีหนึ่งในการหลีกเลี่ยงการจัดฟันในบางกรณี
Peter Cordes

8

ไปใช้งานได้!

หากคุณสามารถลดปัญหาของคุณไปยังฟังก์ชั่นที่เรียบง่ายด้วยลายเซ็นเดียวกันและกำหนดเป็นนิพจน์เดียวคุณสามารถทำได้ดีกว่า#define r returnและแยกส่วนของหม้อไอน้ำทั้งหมดเพื่อกำหนดฟังก์ชั่น

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

ผลลัพธ์ของโปรแกรมคือค่าสถานะที่ส่งคืนไปยังระบบปฏิบัติการหรือการควบคุมเชลล์หรือ IDE

ใช้__VA_ARGS__ช่วยให้คุณสามารถใช้ตัวดำเนินการจุลภาคที่จะแนะนำจุดลำดับเหล่านี้ฟังก์ชั่นการแสดงออก หากไม่ต้องการสิ่งนี้แมโครสามารถสั้นลงได้

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

7
  1. ใช้scanf("%*d ");เพื่ออ่านอินพุตดัมมี่ (ในกรณีที่อินพุตไม่มีความหมายในโปรแกรมเพิ่มเติม) จะสั้นกว่าscanf("%d",&t);ที่คุณต้องประกาศตัวแปร t

  2. การเก็บตัวอักษรใน int array นั้นดีกว่าการจัดเรียงตัวอักษร ตัวอย่าง.

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


2
ที่จริงแล้วฉันใช้%*dไม่เพียง แต่ในสนามกอล์ฟเพราะมันมีประโยชน์ในสถานการณ์ที่ใคร ๆ ก็อยากจะข้ามขึ้นบรรทัดใหม่ในscanf("%[^\n]%*c",str);:)
tomsmeding

6

พิมพ์ตัวละครจากนั้นปัดแคร่กลับคืนแทน:

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

หรือ

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

เพียงประกาศ c เป็น int และ:

puts(&c);

9
มันอาจจะคุ้มค่าที่จะชี้ให้เห็นว่าสิ่งนี้ขึ้นอยู่กับสถาปัตยกรรมเล็ก ๆ น้อย ๆ หาก c เป็น int-endian ที่มีขนาดใหญ่คุณก็จะได้รับค่าขนส่งคืน (ในทางกลับกันถ้า c เป็น char คุณอาจได้รับขยะสุ่มหลังจากแทนที่จะส่งคืนค่าขนส่ง)
breadbox

@ กล่องจดหมายอ๋อคุณมีสิทธิ์โดยสิ้นเชิง; ฉันเพิ่งแก้ไข: ข้อความที่ตัดตอนมาล่าสุดควรใช้ c เป็น int (ซึ่งมักจะง่ายต่อการประกาศเช่นนี้)
moala

ไม่puts(&c)จริงๆทำงานอย่างไร ที่ไม่จำเป็นต้องถูกยกเลิกด้วย null
แยกผลไม้

1
@EsolangingFruit ในน้อย endian กับ ints 32 บิตเป็น int 0 ≤ค <256จะถูกเก็บไว้เป็นลำดับไบต์ค 0 0 0 เมื่อแปลที่อยู่ของcเป็นchar *เราจะเห็นสตริงเดี่ยว: อักขระcตามด้วย null null
Dennis

6

การใช้asprintf()ช่วยให้คุณจัดสรรชัดเจนและการวัดความยาวของสตริง aka 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);
}

6

import ถ้าคุณต้อง

ดังที่ระบุไว้ในคำตอบแรกๆ คอมไพเลอร์บางตัว (โดยเฉพาะ GCC และเสียงดังกราว) ช่วยให้คุณไม่พลาด#includes สำหรับฟังก์ชั่นไลบรารีมาตรฐาน

แม้ว่าคุณจะไม่สามารถเพียงแค่ลบ#includeอาจจะมีวิธีอื่นที่จะหลีกเลี่ยงได้แต่ที่ไม่ปฏิบัติหรือ golfy โดยเฉพาะอย่างยิ่งเสมอ

ในกรณีที่เหลือคุณสามารถใช้#import<header file>แทน#include<header file>การบันทึกไบต์ นี่คือส่วนขยาย GNU และถือว่าเลิกใช้แล้ว แต่ทำงานอย่างน้อยใน gcc 4.8, gcc 5.1 และ clang 3.7


6

ลองcpow()แทนcos()

แทน

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

ลองสิ่งที่ชอบ

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

สิ่งนี้ใช้สูตรของออยเลอร์การวิเคราะห์ที่ซับซ้อนเล็กน้อยและการสังเกตที่การกำหนดคอมเพล็กซ์ให้สองครั้งให้ผลที่แท้จริง (ระวังการเรียกใช้ฟังก์ชัน Variadic และรายละเอียดปลีกย่อยอื่น ๆ )

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

เคล็ดลับประเภทนี้สามารถใช้เพื่อลด

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

เข้าไป

double y=cpow(1i,x);

เพราะ (-1) ^ \ frac {x} {2} = J ^ {\ frac {2x} {2}} = ^ J x


5

นี่คือเคล็ดลับที่ฉันใช้เพื่อประโยชน์ของฉัน ฉันขโมยของพวกเขาจากคนอื่นอย่างไร้ยางอายดังนั้นให้เครดิตกับทุกคนยกเว้นฉัน:

รวมการมอบหมายกับการเรียกใช้ฟังก์ชัน

แทนสิ่งนี้:

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 */

1
อาจR*-~!!mxxxx
l4m2

5

การทราบความเท่าเทียมกันทางตรรกะขั้นพื้นฐานอาจช่วยให้สามารถบันทึกได้สองสามไบต์ ยกตัวอย่างเช่นแทนที่จะพยายามแทนการใช้กฎหมายของif (!(a&&b)){} DeMorgan if (!a||!b){}เช่นเดียวกับฟังก์ชั่นค่าที่เหมาะสม: แทนที่จะทำ~(a|b)~a&~b


cf เลย กฎเดอมอร์แกน
Jonathan Frech
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.