ฉันได้เห็นคำจำกัดความของแมโครดังต่อไปนี้ในหนังสือการเข้ารหัส
#define TRUE '/'/'/'
#define FALSE '-'-'-'
ไม่มีคำอธิบายที่นั่น
กรุณาอธิบายให้ฉันวิธีการเหล่านี้จะทำงานเป็นและTRUE
FALSE
ฉันได้เห็นคำจำกัดความของแมโครดังต่อไปนี้ในหนังสือการเข้ารหัส
#define TRUE '/'/'/'
#define FALSE '-'-'-'
ไม่มีคำอธิบายที่นั่น
กรุณาอธิบายให้ฉันวิธีการเหล่านี้จะทำงานเป็นและTRUE
FALSE
คำตอบ:
ลองมาดูกัน: '/' / '/'
หมายถึงchar
ตัวอักษร/
หารด้วยchar
อักษร'/'
ตัวเอง TRUE
ผลที่ได้คือหนึ่งซึ่งเสียงที่เหมาะสมสำหรับ
และ'-' - '-'
หมายถึงchar
ตัวอักษร'-'
ลบออกจากตัวเอง นี่คือศูนย์ ( FALSE
)
ปัญหานี้มีอยู่สองประการ: อันดับแรกไม่สามารถอ่านได้ การใช้งาน1
และ0
ดีกว่าอย่างแน่นอน นอกจากนี้เนื่องจาก TartanLlama และ KerrekSB ได้ชี้ให้เห็นว่าหากคุณจะใช้คำจำกัดความดังกล่าวโปรดเพิ่มวงเล็บไว้รอบ ๆ เพื่อที่คุณจะได้ไม่ต้องประหลาดใจ:
#include <stdio.h>
#define TRUE '/'/'/'
#define FALSE '-'-'-'
int main() {
printf ("%d\n", 2 * FALSE);
return 0;
}
นี่จะพิมพ์ค่าของchar
ตัวอักษร'-'
(45 บนระบบของฉัน)
ด้วยวงเล็บ:
#define TRUE ('/'/'/')
#define FALSE ('-'-'-')
โปรแกรมพิมพ์เลขศูนย์อย่างถูกต้องแม้ว่ามันจะไม่สมเหตุสมผลที่จะคูณค่าความจริงด้วยจำนวนเต็ม แต่มันก็เป็นเพียงตัวอย่างของข้อผิดพลาดที่ไม่คาดคิดชนิดหนึ่งที่สามารถกัดคุณได้หากคุณไม่ได้วงเล็บมาโครของคุณ
if
แทนการคูณTRUE
ด้วยจำนวนเต็ม
notx = TRUE- x;
และทำงานได้ดี ยกเว้นว่าTRUE-FALSE
เป็น -44 (สมมติว่า ASCII)
มันเป็นอีกวิธีหนึ่งในการเขียน
#define TRUE 1
#define FALSE 0
การแสดงออก'/'/'/'
จะแบ่งค่าถ่านของ'/'
ตัวเองซึ่งจะให้ 1 เป็นผล
การแสดงออก'-'-'-'
จะลดค่าถ่านของ'-'
ตัวเองซึ่งจะทำให้ 0 เป็นผล
เครื่องหมายวงเล็บทั่วทั้งdefine
นิพจน์หายไปซึ่งอาจทำให้เกิดข้อผิดพลาดในรหัสโดยใช้มาโครเหล่านี้ คำตอบของเจย์นั้นค่อนข้างดี
ตัวอย่างของสถานการณ์ "ชีวิตจริง" ที่การลืมวงเล็บอาจเป็นอันตรายคือการใช้มาโครเหล่านี้ร่วมกับตัวดำเนินการส่งแบบ C หากมีคนตัดสินใจที่จะแปลงการแสดงออกเหล่านี้bool
ใน C ++ เช่น:
#include <iostream>
#define TRUE '/'/'/'
#define FALSE '-'-'-'
int main() {
std::cout << "True: " << (bool) TRUE << std::endl;
std::cout << "False: " << (bool) FALSE << std::endl;
return 0;
}
นี่คือสิ่งที่เราได้รับ:
True: 0
False: -44
ดังนั้น(bool) TRUE
จริงจะประเมินfalse
และจะประเมินการ(bool) FALSE
true
มันเทียบเท่ากับการเขียน
#define TRUE 1
#define FALSE 0
สิ่งที่แสดงออก'/'/'/'
ไม่จริงคือการแบ่งตัวละคร/
(สิ่งที่ค่าตัวเลขที่เป็น) 1
ด้วยตัวเองดังนั้นมันจะกลายเป็น
ในทำนองเดียวกันการแสดงออก'-'-'-'
ลบตัวอักษรจากตัวเองและประเมินผลการ-
0
มันจะดีกว่าที่จะเขียน
#define TRUE ('/'/'/')
#define FALSE ('-'-'-')
เพื่อหลีกเลี่ยงการเปลี่ยนแปลงค่าโดยไม่ตั้งใจเมื่อใช้กับตัวดำเนินการลำดับความสำคัญสูงกว่าอื่น ๆ
เจย์ตอบแล้วว่าทำไมค่าของการแสดงออกเหล่านี้และ0
1
เพื่อประโยชน์ทางประวัติศาสตร์การแสดงออกเหล่านี้'/'/'/'
และ'-'-'-'
มาจากหนึ่งในรายการการประกวด C Code นานาชาติครั้งที่ 1 ในปี 1984 :
int i;main(){for(;i["]<i;++i){--i;}"];read('-'-'-',i+++"hell\
o, world!\n",'/'/'/'));}read(j,i,p){write(j/p+p,i---j,i/i);}
(ลิงก์ไปยังโปรแกรมที่นี่มีคำใบ้ว่าโปรแกรมนี้ทำอะไรในหน้า IOCCC ด้านบน)
นอกจากนี้หากฉันจำได้อย่างถูกต้องว่านิพจน์เหล่านี้เป็นมาโครที่ทำให้ยุ่งเหยิงสำหรับTRUE
และFALSE
ยังถูกกล่าวถึงในหนังสือ"Obfuscated C และ Mysteries อื่น ๆ "โดย Don Libes (1993)
มันเป็นอย่างเฮฮาสำหรับการเขียนแมโครสำหรับและTrue
False
เนื่องจากมีการให้คำอธิบายมากมาย/
หมายถึง 1 ไบต์ (ตาม ASCII) เมื่อหารด้วยตัวมันเองก็จะให้คุณ1
ซึ่งจะได้รับการปฏิบัติเช่นTrue
เดียวกันและก็ -
เป็นจำนวนไบต์อีกครั้งเมื่อลบค่าเดียวกันที่ให้คุณ0
ซึ่งจะถูกตีความว่าเป็นfalse
#define TRUE '/'/'/'
#define FALSE '-'-'-'
ดังนั้นเราสามารถแทนที่/
หรือ-
ด้วยตัวอักษรใด ๆ ที่เราต้องการตัวอย่างเช่น:
#define TRUE '!'/'!'
#define FALSE 'o'-'o'
จะรักษาความหมายเช่นเดียวกับการแสดงออกเดิม
เริ่มจากความจริงกันก่อน คุณสามารถอ่านได้'/' / '/'
ซึ่งหมายถึง "ตัวอักษร '/' หารด้วยตัวอักษร '/'" เนื่องจากอักขระแต่ละตัวใน C เป็นค่าตัวเลข (หนึ่งไบต์) จึงสามารถอ่านได้ว่า "ค่า ASCII ของอักขระ '/' หารด้วยค่า ASCII ของอักขระเดียวกันนั้น" ซึ่งหมายถึง 1 (เพราะเห็นได้ชัดว่า x / x คือ 1) ดังนั้นTRUE
คือ 1
สำหรับFALSE
เหตุผลเดียวกัน: '-'-'-'
อ่าน'-' - '-'
คือ "ค่า ASCII ของ '-' ลบค่า ASCII ของ '-'" ซึ่งคือ 0 ดังนั้นจึงFALSE
เป็น 0
นี่เป็นวิธีที่น่ารังเกียจในการระบุอย่างชัดเจน
'/'/'/'
เป็น 1 สำหรับใด ๆชุดตัวอักษรที่ถูกต้องไม่ว่าจะเป็น'/' == 47
(เป็นอยู่ใน ASCII) หรือ'/' == 97
(มันเป็นใน EBCDIC) หรือค่าอื่น ๆ
'/'
0
ค่านั้นสงวนไว้สำหรับตัวอักษร Null