ทำไม #define TRUE (1 == 1) ในมาโคร C บูลีนแทนที่จะเป็นเพียง 1


160

ฉันเคยเห็นคำจำกัดความใน C

#define TRUE (1==1)
#define FALSE (!TRUE)

จำเป็นหรือไม่ ประโยชน์ที่ได้จากการกำหนด TRUE เป็น 1 คืออะไรและ FALSE เป็น 0


35
และอื่น ๆ : #define TRUE (’/’/’/’); #define FALSE (’-’-’-’)(นำมาจากcoding-guidelines.com/cbook/cbook1_1.pdfหน้า 871)
osgx

2
ไม่มันเป็นความหวาดระแวงโดย clueless ^ Wunderinformed จริง ๆ ใน C, 1 และ 0 ทำได้ในทุกสถานการณ์เหมือนกัน
Jens

@osgx มันหมายความว่าอะไร?
mrgloom

คำตอบ:


155

วิธีการนี้จะใช้booleanประเภทจริง(และแก้ไขให้trueและfalse) หากคอมไพเลอร์สนับสนุน (โดยเฉพาะ C ++)

อย่างไรก็ตามจะเป็นการดีกว่าถ้าตรวจสอบว่ามีการใช้งาน C ++ (ผ่านทาง__cplusplusมาโคร) และใช้งานจริงtrueหรือfalseไม่

ในคอมไพเลอร์ C, นี้จะเทียบเท่ากับและ0 (โปรดทราบว่าการลบวงเล็บจะทำให้เกิดความเสียหายเนื่องจากคำสั่งของการดำเนินการ)1


7
ไม่ถูกต้องไม่ใช้บูลที่นี่ ผลมาจากการเป็น1==1 int(ดูstackoverflow.com/questions/7687403/... .)
จ้า

4
@ Mat: แม้จะอยู่ใน C ++ ด้วยbooleanประเภทนี้?
SLAK

9
คำถามคือแท็ก C แต่แน่นอนใน C ++ ผู้ประกอบการเชิงสัมพันธ์กลับมาหรือtrue false
Mat

5
@ Mat: ฉันเดาว่ารหัสดังกล่าวเขียนในส่วนหัว C เพื่อให้เล่นได้ดีกับ C ++
SLaks

20
@SLaks หากต้องการเล่นที่ดีกับ C ++ มันจะเป็น#define TRUE trueและ#define FALSE falseเมื่อใดก็ตามที่__cplusplusมีการกำหนดไว้
Nikos C.

137

คำตอบคือพกพาได้ ค่าตัวเลขของTRUEและFALSEไม่สำคัญ อะไรคือสิ่งที่สำคัญก็คือว่าคำสั่งเช่นif (1 < 2)ประเมินif (TRUE)และงบเหมือนif (1 > 2)ประเมินif (FALSE)ประเมิน

ได้รับใน C (1 < 2)ประเมิน1และ(1 > 2)ประเมิน0ดังนั้นอย่างที่คนอื่นพูดไม่มีความแตกต่างในทางปฏิบัติเท่าที่เกี่ยวข้องกับคอมไพเลอร์ แต่โดยการให้คอมไพเลอร์กำหนดTRUEและFALSEตามกฎของตัวเองคุณกำลังทำให้ความหมายชัดเจนสำหรับโปรแกรมเมอร์และคุณรับประกันความมั่นคงภายในโปรแกรมของคุณและไลบรารี่อื่น ๆ (สมมติว่าไลบรารี่อื่นนั้นเป็นไปตามมาตรฐาน C ... ประหลาดใจ)


บางประวัติ
พื้นฐานบางอย่างที่กำหนดไว้FALSEเป็น0และเป็นTRUE -1เช่นเดียวกับภาษาที่ทันสมัยมากพวกเขาตีความคุ้มค่าที่ไม่ใช่ศูนย์ใด ๆ ที่เป็นTRUEแต่พวกเขาประเมิน-1นิพจน์บูลีนที่มีความเป็นจริง NOTการดำเนินการของพวกเขาถูกนำไปใช้โดยการเพิ่ม 1 และพลิกสัญญาณเพราะมันมีประสิทธิภาพในการทำเช่นนั้น ดังนั้น 'ไม่ X' -(x+1)กลายเป็น ผลข้างเคียงของสิ่งนี้คือค่าที่5ประเมินได้TRUEแต่NOT 5ประเมิน-6ซึ่งก็เป็นเช่นกันTRUE! การค้นหาข้อผิดพลาดประเภทนี้ไม่สนุก

ปฏิบัติที่ดีที่สุด
ได้รับการพฤตินัยกฎที่ศูนย์จะถูกตีความว่าเป็นFALSEและใด ๆที่ไม่ใช่ศูนย์ค่าถูกตีความว่าเป็นTRUEคุณควรไม่เคยเปรียบเทียบบูลกำลังมองหาการแสดงออกไปTRUEFALSEหรือ ตัวอย่าง:

if (thisValue == FALSE)  // Don't do this!
if (thatValue == TRUE)   // Or this!
if (otherValue != TRUE)  // Whatever you do, don't do this!

ทำไม? เพราะโปรแกรมเมอร์หลายคนใช้ช็อตคัตในการรักษาints เป็นbools พวกเขาไม่เหมือนกัน แต่โดยทั่วไปคอมไพเลอร์อนุญาต ตัวอย่างเช่นมันถูกกฎหมายอย่างสมบูรณ์แบบที่จะเขียน

if (strcmp(yourString, myString) == TRUE)  // Wrong!!!

นั่นดูถูกต้องและคอมไพเลอร์จะยอมรับมันอย่างมีความสุข แต่มันอาจจะไม่ทำในสิ่งที่คุณต้องการ นั่นเป็นเพราะค่าส่งคืนของstrcmp()คือ

      0 ถ้าyourString == myString
    <0 ถ้าyourString < myString
    > 0 ถ้าyourString > myString

ดังนั้นบรรทัดข้างต้นจะส่งกลับTRUEเฉพาะเมื่อyourString > myStringเฉพาะเมื่อ

วิธีที่ถูกต้องในการทำเช่นนี้คือ

// Valid, but still treats int as bool.
if (strcmp(yourString, myString))

หรือ

// Better: lingustically clear, compiler will optimize.
if (strcmp(yourString, myString) != 0)

ในทำนองเดียวกัน:

if (someBoolValue == FALSE)     // Redundant.
if (!someBoolValue)             // Better.
return (x > 0) ? TRUE : FALSE;  // You're fired.
return (x > 0);                 // Simpler, clearer, correct.
if (ptr == NULL)                // Perfect: compares pointers.
if (!ptr)                       // Sleazy, but short and valid.
if (ptr == FALSE)               // Whatisthisidonteven.

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

มันไม่คุ้มค่าใช่มั้ย


6
(1==1)1ไม่มีแบบพกพามากกว่า กฎของคอมไพเลอร์คือกฎของภาษา C ซึ่งมีความชัดเจนและไม่น่าสงสัยเกี่ยวกับความหมายของความเท่าเทียมกันและตัวดำเนินการสัมพันธ์ ผมไม่เคยเห็นคอมไพเลอร์จะได้รับสิ่งนี้ไม่ถูกต้อง
Keith Thompson

1
ที่จริงแล้วค่าที่คืนมาstrcmpนั้นน้อยกว่าค่าที่เท่ากันหรือมากกว่านั้น 0 ไม่รับประกันว่าจะเป็น -1, 0 หรือ 1 และมีแพลตฟอร์มในรูปแบบที่ไม่คืนค่าเหล่านั้นเพื่อให้ได้ความเร็วในการใช้งาน ดังนั้นหากเป็นเช่นstrcmp(a, b) == TRUEนั้นa > bแต่ความหมายกลับกันอาจไม่เกิดขึ้น
Maciej Piechotka

2
@ KeithThompson - บางที "การพกพา" เป็นคำที่ผิด แต่ความจริงก็คือ (1 == 1) เป็นค่าบูลีน 1 ไม่ได้
Adam Liss

2
@ AdamLiss: ใน C (1==1)และ1เป็นทั้งประเภทคงที่ของการแสดงออกที่intมีค่า 1 พวกเขาจะเหมือนกันมีความหมาย ฉันคิดว่าคุณสามารถเขียนโค้ดที่เหมาะสำหรับผู้อ่านที่ไม่ทราบ แต่ท้ายที่สุดแล้วมันจะจบที่ไหน?
Keith Thompson

2
'ไม่ใช่' 5 คือ, -6, ที่ระดับบิต
woliveirajr

51

(1 == 1)เคล็ดลับจะเป็นประโยชน์สำหรับการกำหนดTRUEในลักษณะที่มีความโปร่งใสกับ C ที่ยังให้การพิมพ์ที่ดีกว่าใน C ++ รหัสเดียวกันสามารถตีความได้ว่าเป็น C หรือ C ++ หากคุณกำลังเขียนในภาษาที่เรียกว่า "Clean C" (ซึ่งคอมไพล์เป็น C หรือ C ++) หรือถ้าคุณกำลังเขียนไฟล์ส่วนหัวของ API ที่สามารถใช้โดยโปรแกรมเมอร์ C หรือ C ++

ใน C หน่วยแปล1 == 1มีตรงความหมายเหมือนกัน1; และมีความหมายเหมือนกัน1 == 0 0อย่างไรก็ตามใน c ++ หน่วยแปลมีประเภท1 == 1 boolดังนั้นTRUEมาโครนิยามวิธีดังกล่าวจึงรวมเข้ากับ C ++ ได้ดีขึ้น

ตัวอย่างของวิธีการที่จะบูรณาการที่ดีขึ้นเป็นที่สำหรับฟังก์ชั่นเช่นถ้าfooมีเกินสำหรับintและboolจากนั้นfoo(TRUE)จะเลือกboolเกินพิกัด หากTRUEมีการกำหนดเช่นเดียว1แล้วมันจะไม่ทำงานอย่างใน C ++ foo(TRUE)จะต้องการintเกินพิกัด

แน่นอน C99 แนะนำbool, trueและfalseและเหล่านี้สามารถนำมาใช้ในส่วนหัวของไฟล์ที่ทำงานร่วมกับ C99 และซี

อย่างไรก็ตาม:

  • การฝึกฝนนี้เพื่อกำหนดTRUEและFALSEเป็น(0==0)และ(1==0)ถือกำเนิด C99
  • ยังคงมีเหตุผลที่ดีที่จะอยู่ห่างจาก C99 และทำงานกับ C90

หากคุณกำลังทำงานใน C ผสมและโครงการ c ++ และไม่ต้องการ C99 กำหนดกรณีที่ต่ำกว่าtrue, falseและboolแทน

#ifndef __cplusplus
typedef int bool;
#define true (0==0)
#define false (!true)
#endif

ที่ถูกกล่าวว่า0==0เคล็ดลับคือ (is?) ใช้โดยโปรแกรมเมอร์บางคนแม้ในรหัสที่ไม่เคยตั้งใจที่จะทำงานร่วมกับ C ++ ในทางใดทางหนึ่ง นั่นไม่ได้ซื้ออะไรเลยและแนะนำว่าโปรแกรมเมอร์มีความเข้าใจผิดเกี่ยวกับการทำงานของบูลีนในซี


ในกรณีที่คำอธิบาย C ++ ไม่ชัดเจนนี่คือโปรแกรมทดสอบ:

#include <cstdio>

void foo(bool x)
{
   std::puts("bool");  
}

void foo(int x)
{
   std::puts("int");  
}

int main()
{
   foo(1 == 1);
   foo(1);
   return 0;
}

ผลลัพธ์:

bool
int

สำหรับคำถามจากความคิดเห็นว่าฟังก์ชัน C ++ มากเกินไปเกี่ยวข้องกับการเขียนโปรแกรม C และ C ++ แบบผสมอย่างไร สิ่งเหล่านี้แสดงให้เห็นถึงความแตกต่างของประเภท เหตุผลที่ถูกต้องสำหรับการต้องการtrueค่าคงที่ที่จะboolรวบรวมเมื่อ C ++ สำหรับการวินิจฉัยที่สะอาด ที่ระดับการเตือนสูงสุดคอมไพเลอร์ C ++ อาจเตือนเราเกี่ยวกับการแปลงถ้าเราผ่านจำนวนเต็มเป็นboolพารามิเตอร์ เหตุผลหนึ่งในการเขียนใน Clean C ไม่เพียง แต่รหัสของเราจะพกพาได้มากกว่า (เพราะคอมไพเลอร์ของ C ++ ไม่เพียง แต่คอมไพเลอร์ C เท่านั้น) แต่เราสามารถได้รับประโยชน์จากความคิดเห็นการวินิจฉัยของคอมไพเลอร์ C ++


3
ยอดเยี่ยมและด้อยค่าคำตอบ ไม่ชัดเจนเลยว่าความหมายทั้งสองTRUEนั้นจะแตกต่างกันภายใต้ C ++
user4815162342

4
ฟังก์ชั่นโอเวอร์โหลดเกี่ยวข้องกับโค้ดที่รวบรวมทั้ง C และ C ++ อย่างไร
Keith Thompson

@ KeithThompson ไม่เพียง แต่เกี่ยวกับการบรรทุกเกินพิกัด แต่การพิมพ์ที่เหมาะสมโดยทั่วไปการบรรทุกเกินพิกัดเป็นเพียงตัวอย่างที่ใช้งานได้จริงที่สุดเมื่อมีการเล่น แน่นอนรหัส c ++ โดยไม่ต้อง overloads แม่แบบและทุกสิ่งที่ "ซับซ้อน" สิ่งที่ถอดออกได้สำหรับ"C-เข้ากันได้"ไม่ได้โดดสนใจมากเกี่ยวกับประเภทที่ทุกคน แต่ที่ไม่ได้หมายถึงหนึ่งควรจะโค่นล้มความคิดข้อ จำกัด ประเภทในภาษาที่กำหนด .
Christian Rau

1
@ChristianRau: คุณหมายถึงอะไร "ไม่ค่อยสนใจประเภทมากนัก" ประเภทคือศูนย์กลางของภาษา C; ทุกการแสดงออกค่าและวัตถุในโปรแกรม C มีประเภทที่กำหนดไว้อย่างดี หากคุณต้องการกำหนดสิ่งที่แตกต่างใน C และ C ++ (ในกรณีที่ไม่ค่อยเกิดขึ้นซึ่งคุณจำเป็นต้องเขียนโค้ดที่คอมไพล์เป็นทั้ง C และ C ++) คุณสามารถใช้#ifdef __cplusplusเพื่อแสดงเจตนาของคุณได้ชัดเจนยิ่งขึ้น
Keith Thompson

@KeithThompson ใช่ฉันรู้ว่าวิธีการที่สำคัญประเภท มันเป็นเพียงแค่ว่าไม่มีทุกสิ่งที่ประเภทตระหนักถึงเช่นการบรรทุกเกินพิกัดและแม่สิ่งที่ต้องการความแตกต่างระหว่างboolและintไม่เรื่องมากในทางปฏิบัติเนื่องจากพวกเขาเป็นโดยปริยายแปลงสภาพให้แก่กันและกัน (และในCจริง"เดียวกัน"ทราบคำพูด แม้ว่า) และมีสถานการณ์ไม่มากนักที่คุณจำเป็นต้องแยกแยะระหว่างสองสถานการณ์นี้ "ไม่มาก"อาจจะหนักเกินไป"น้อยกว่าเมื่อเปรียบเทียบกับรหัสที่ใช้แม่แบบและการใช้งานมากเกินไป"อาจจะดีกว่านี้
Christian Rau

18
#define TRUE (1==1)
#define FALSE (!TRUE)

เทียบเท่ากับ

#define TRUE  1
#define FALSE 0

ในซี

ผลมาจากผู้ประกอบการเชิงสัมพันธ์เป็นหรือ0 รับประกันได้ว่าจะได้รับการประเมินไปและมีการประกันเพื่อรับการประเมินเพื่อ11==11!(1==1)0

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

(C99, 6.6p2) "นิพจน์คงที่สามารถประเมินได้ระหว่างการแปลมากกว่ารันไทม์และอาจถูกนำไปใช้ในสถานที่ใด ๆ ที่อาจมีค่าคงที่"

PC-Lint จะออกข้อความ (506 บูลีนค่าคงที่) หากคุณไม่ได้ใช้ตัวอักษรTRUEและFALSEมาโคร:

สำหรับ C, ควรได้รับการกำหนดให้เป็นTRUE 1อย่างไรก็ตามภาษาอื่น ๆ ใช้ปริมาณที่มากกว่า 1 ดังนั้นโปรแกรมเมอร์บางคนรู้สึกว่า!0มันปลอดภัย

นอกจากนี้ใน C99 stdbool.hคำจำกัดความของมาโครบูลีนtrueและfalse ใช้ตัวอักษรโดยตรง:

#define true   1
#define false  0

1
ฉันสงสัยว่า TRUE จะถูกแทนที่ในทุกการใช้งานด้วย 1 == 1 ในขณะที่ใช้เพียง 1 จะแทนที่ 1 ไม่ใช่วิธีแรกที่จะเปรียบเทียบค่าใช้จ่ายพิเศษ ...
pinkpanther

4
@pinkpanther การแสดงออกคงที่มักจะมีการประเมินในเวลารวบรวมและดังนั้นจึงไม่ก่อให้เกิดค่าใช้จ่ายใด ๆ
ouah

2
1==1รับประกันว่าจะได้รับการประเมินเพื่อ1
ouah

3
@NikosC นั่นเป็นคำถามที่ดี เรื่องนี้เกี่ยวกับรหัสของแบบฟอร์มif(foo == true)ซึ่งจะเปลี่ยนจากการปฏิบัติที่ไม่ดีไปจนถึงรถบั๊กแบน
djechlin

1
+1 สำหรับการชี้ให้เห็นอันตรายในสามารถมีค่าความจริงที่แตกต่างกว่า(x == TRUE) x
Joshua Taylor

12

นอกเหนือจาก C ++ (กล่าวถึงแล้ว) ประโยชน์อีกอย่างหนึ่งก็คือเครื่องมือวิเคราะห์แบบคงที่ คอมไพเลอร์จะทำไปกับความไร้ประสิทธิภาพใด ๆ แต่ตัววิเคราะห์แบบคงที่สามารถใช้ประเภทนามธรรมของตัวเองเพื่อแยกความแตกต่างระหว่างผลการเปรียบเทียบและประเภทจำนวนเต็มอื่น ๆ ดังนั้นจึงรู้โดยปริยายว่า TRUE ต้องเป็นผลของการเปรียบเทียบและไม่ควรสันนิษฐานว่า ด้วยจำนวนเต็ม

เห็นได้ชัดว่า C บอกว่าพวกเขาเข้ากันได้ แต่คุณอาจเลือกที่จะห้ามการใช้คุณสมบัตินั้นโดยเจตนาเพื่อช่วยไฮไลต์ข้อบกพร่อง - ตัวอย่างเช่นที่บางคนอาจสับสน&และ&&หรือพวกเขาทำผิดพลาดต่อหน้าผู้ให้บริการ


1
นั่นเป็นจุดที่ดีและบางทีบางส่วนของเครื่องมือเหล่านี้ยังสามารถจับรหัสโง่if (boolean_var == TRUE) โดยวิธีการของการขยายตัวif (boolean_var == (1 == 1))ซึ่งต้องขอบคุณข้อมูลประเภทที่เพิ่มขึ้นของโหนดตกอยู่ในรูปแบบ(1 == 1) if (<*> == <boolean_expr>)
Kaz

4

ความแตกต่างที่เป็นจริงคืออะไร 0ได้รับการประเมินไปfalseและได้รับการประเมินเพื่อ1 trueความจริงที่ว่าคุณใช้นิพจน์บูลีน ( 1 == 1) หรือ1เพื่อกำหนดtrueไม่สร้างความแตกต่างใด ๆ intพวกเขาทั้งสองได้รับการประเมินเพื่อ

ขอให้สังเกตว่าstdbool.hไลบรารี่มาตรฐาน C นั้นมีหัวข้อเฉพาะสำหรับการกำหนดบูลีน:


แน่นอนคุณไม่ได้ ... แต่บางคนอาจจะคิดเป็นอย่างอื่นโดยเฉพาะอย่างยิ่งสำหรับตัวเลขที่ติดลบที่ว่าทำไม :)
pinkpanther

อะไร? คุณมีมันย้อนกลับ trueได้รับการประเมินไป1และได้รับการประเมินเพื่อfalse 0C ไม่ทราบเกี่ยวกับประเภทบูลพื้นเมืองพวกเขากำลังเพียง ints
djechlin

ใน C ผู้ประกอบการเชิงสัมพันธ์และความเท่าเทียมกันให้ผลลัพธ์จากประเภทintที่มีค่าหรือ0 1C มีประเภทบูลีนจริง ( _Boolโดยมีแมโครboolกำหนดไว้<stdbool.h>แต่ถูกเพิ่มใน C99 เท่านั้นซึ่งไม่ได้เปลี่ยนซีแมนทิกส์ของโอเปอเรเตอร์เพื่อใช้ชนิดใหม่
Keith Thompson

@djechlin: ในฐานะของมาตรฐานปี 1999 C จะมีชนิดบูลีนพื้นเมือง มันเรียกว่า_Boolและมี<stdbool.h> #define bool _Bool
Keith Thompson

@KeithThompson คุณมีสิทธิเกี่ยวกับการประเมินเป็น1 == 1 intแก้ไข
รองเท้า

3

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

ถ้า ((a> b) == TRUE)

นี่อาจเป็นหายนะหากคุณกำหนด TRUE ด้วยตนเองเป็น 1 ในขณะที่ค่าภายในของ TRUE เป็นอีกหนึ่งค่า


ใน C ตัว>ดำเนินการให้ผลตอบแทน 1 เสมอสำหรับจริง 0 สำหรับเท็จ ไม่มีความเป็นไปได้ที่คอมไพเลอร์ C จะได้รับสิ่งนี้ผิด การเปรียบเทียบความเท่าเทียมกันTRUEและFALSEสไตล์ที่ไม่ดี if (a > b)ดังกล่าวข้างต้นเป็นลายลักษณ์อักษรอย่างชัดเจนมากขึ้นเป็น แต่ความคิดที่ว่าคอมไพเลอร์ C ที่แตกต่างกันสามารถปฏิบัติต่อความจริงและเท็จต่างกันเพียงแค่ไม่ถูกต้อง
Keith Thompson

2
  1. รายการสินค้า

โดยทั่วไปในภาษาการเขียนโปรแกรม C จะมีการกำหนด 1 เป็นจริงและ 0 ถูกกำหนดเป็นเท็จ ดังนั้นทำไมคุณเห็นดังต่อไปนี้ค่อนข้างบ่อย:

#define TRUE 1 
#define FALSE 0

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

#define TRUE (1==1)
#define FALSE (!TRUE)

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


4
ฉันจะไม่เรียกมันว่า "กำลังเล่นอย่างปลอดภัย" - แต่คุณให้ความรู้สึกผิดกับความปลอดภัย
dodgethesteamroller
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.