(x ^ 0x1)! = 0 หมายถึงอะไร


183

ฉันพบข้อมูลโค้ดต่อไปนี้

if( 0 != ( x ^ 0x1 ) )
     encode( x, m );

อะไรx ^ 0x1หมายถึง? นี่เป็นเทคนิคมาตรฐานหรือไม่?


97
การทำให้งงงวยโค้ดค่อนข้างเป็นมาตรฐาน
raina77ow

47
สิ่งนี้สามารถแก้ไขได้ด้วยวิธีการเดียวกับสมการ "ปกติ": 0 != (x ^ 1)→ xor ทั้งสองข้างโดย 1 (0 ^ 1) != (x ^ 1 ^ 1)→→ลดความซับซ้อน→1 != x
คะแนน _ ต่ำกว่า

4
ฉันไม่เห็นว่าif (1 != x)การเขียนยากแค่ไหน
Adriano Varoli Piazza

12
ความคิดเห็นต่าง ๆ เป็นครั้งแรกโดย @Spook ชัดเจนชี้ให้เห็นว่าtypeการxไม่ได้รับ - ด้วยเหตุนี้เราไม่ทราบว่านี้เป็นจำนวนเต็มใน C ++ แท็กนี้ปัญหา แน่นอนว่าถ้าเป็น C หรือxเป็นจำนวนเต็มคำตอบคือง่าย แต่ที่ไม่ได้เป็นที่กำหนดและเป็นไปได้ของมากไปoperator ^อยู่
chux - Reinstate Monica

11
ฉันไม่เห็นว่าสิ่งนี้ได้รับ upvotes มากมาย ...
laike9m

คำตอบ:


277

การดำเนินการ XOR ( x ^ 0x1) กลับบิต 0 ดังนั้นการแสดงออกอย่างมีประสิทธิภาพหมายถึง: ถ้าบิต 0 ของ x เป็น 0 หรือบิตอื่น ๆ ของ x คือ 1 แล้วการแสดงออกเป็นจริง

ในทางกลับกันนิพจน์เป็นเท็จถ้า x == 1

ดังนั้นการทดสอบจึงเหมือนกับ:

if (x != 1)

และดังนั้น (arguably) obfuscated โดยไม่จำเป็น


39
เฮ้คุณไม่รู้จักบริบท ถ้า x เป็นแฟล็กบิตบางชนิด IMO จะชัดเจนกว่าในการเขียนเนื่องจากตอนนี้ใช้การดำเนินการ! =
Spook

40
@Spook: มันไม่เพียงแค่ทดสอบการตั้งค่าบิตเดียว - มันทดสอบความกว้างทั้งหมดของ x หากคุณต้องการทดสอบเพียงบิตเดียวก็จะมีสำนวนที่ชัดเจนเช่นการใช้บิตและ
พอล R

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

31
จริง ๆ แล้ว Spook นั้นถูกต้อง การทดสอบ (x! = 1) ไม่เทียบเท่า รหัสสามารถเป็น C ++ (และใน C ++, ^ สามารถเป็นโอเปอเรเตอร์ที่ทำสิ่งใดก็ได้) ดังนั้นคุณจึงไม่ทราบบริบท @Spook นั้นถูกต้อง
xryl669

82
@TheThom уєтуσυωяιтєιи¢яαятєχт
bobobobo

78
  • ^เป็นการดำเนินการXOR ระดับบิต
  • 0x1อยู่1ในรูปแบบเลขฐานสิบหก
  • x ^ 0x1จะกลับส่วนสุดท้ายของx(อ้างอิงจากตารางความจริงของแฮคเกอร์ในลิงค์ด้านบนหากไม่ชัดเจนสำหรับคุณ)

ดังนั้นเงื่อนไข(0 != ( x ^ 0x1 ))จะเป็นจริงถ้าxมากกว่า 1 หรือถ้าบิตสุดท้ายของx0 ซึ่งเหลือเพียง x == 1 เป็นค่าที่เงื่อนไขจะเป็นเท็จ ดังนั้นจึงเท่ากับ

if (x != 1)

PS นรกของวิธีการใช้เงื่อนไขง่าย ๆ เช่นนี้ฉันอาจเพิ่ม อย่าทำอย่างนั้น และถ้าคุณต้องเขียนโค้ดซับซ้อนแสดงความคิดเห็น ฉันขอร้องคุณ.


7
มันไม่ได้x==0; 4 ^ 0x1เป็นจริง แต่4==0เห็นได้ชัดว่าผิด
Fred Foo

4
"เงื่อนไขดูเหมือนจะเท่ากับif (x == 0)" ไม่เท่ากับx != 1หรือไม่
Andrew-Dufresne

6
การอนุมานความเท่าเทียมที่xเป็นประเภทหนึ่ง ถ้ามันเป็นfloatหรือแล้วผมเชื่อว่าการแสดงออกจะให้ผลผลิตจริงสำหรับdouble 1.0 <= x < 2.0และถ้าxเป็นประเภทที่ผู้ใช้กำหนดนิพจน์อาจคืนค่าจริงถ้าxเป็นวันเกิดของยูโกะจิงโจ้นักแต่งเพลงชื่อดังหรือหมายเลขใด ๆ ที่มีตัวเลขอย่างน้อยสามหลักด้วยราคาชาปัจจุบันที่เป็นสกุลเงินดอลลาร์ในประเทศจีน
supercat

4
@supercat ไม่มีoperator^สำหรับ/float double
ปุย

1
@supercat: เมื่อมีการเรียกการแปลงจาก floating-point เป็นจำนวนเต็มการแปลงจะเป็นนัย (ไม่จำเป็นต้องใช้ไวยากรณ์ของการส่ง) แต่ไม่มีการแปลงเกิดจากผู้ประกอบการระดับบิตพวกเขาก็ล้มเหลวสำหรับประเภทจุดลอย
Ben Voigt

49

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

^เป็นตัวดำเนินการXOR ระดับบิตใน c, c ++ และ c #

Bitwise XOR ใช้รูปแบบบิตสองบิตที่มีความยาวเท่ากันและดำเนินการตรรกะเอกสิทธิ์หรือการดำเนินการกับแต่ละคู่ของบิตที่สอดคล้องกัน

Exclusive OR เป็นการดำเนินการทางตรรกะที่เอาต์พุตเป็นจริงเมื่อใดก็ตามที่อินพุตทั้งสองต่างกัน (หนึ่งเป็นจริงและอีกอันเป็นเท็จ)

ตารางความจริงของxor ข :

a           b        a xor b
----------------------------
1           1           0
1           0           1
0           1           1
0           0           0

ดังนั้นขออธิบายการ0 == ( x ^ 0x1 )แสดงออกในระดับไบนารี:

             what? xxxxxxxx (8 bits)
               xor 00000001 (hex 0x1 or 0x01, decimal 1)    
             gives 00000000
---------------------------
the only answer is 00000001

ดังนั้น:

   0 == ( x ^ 0x1 )    =>    x == 1
   0 != ( x ^ 0x1 )    =>    x != 1

34

มันเป็นตัวดำเนินการพิเศษหรือ (XOR) เพื่อให้เข้าใจถึงวิธีการทำงานคุณสามารถเรียกใช้รหัสง่ายๆนี้ได้

    std::cout << "0x0 ^ 0x0 = " << ( 0x0 ^ 0x0 ) << std::endl;
    std::cout << "0x0 ^ 0x1 = " << ( 0x0 ^ 0x1 ) << std::endl;
    std::cout << "0x1 ^ 0x0 = " << ( 0x1 ^ 0x0 ) << std::endl;
    std::cout << "0x1 ^ 0x1 = " << ( 0x1 ^ 0x1 ) << std::endl;

ผลลัพธ์จะเป็น

0x0 ^ 0x0 = 0
0x0 ^ 0x1 = 1
0x1 ^ 0x0 = 1
0x1 ^ 0x1 = 0

ดังนั้นการแสดงออกนี้

0 != ( x ^ 0x1 )

จะเท่ากับจริงเฉพาะเมื่อ x! = 0x1

มันไม่เปลี่ยน x ตัวมันเอง มันจะตรวจสอบว่า x เท่ากับ 0 หรือ 1 rxpression นี้สามารถเปลี่ยนเป็น

if ( x != 0x1 )

19

มันตรวจสอบว่าxจริง ๆ แล้วไม่0x1... xorไอเอ็นจีxด้วย0x1จะส่งผลใน 0 เฉพาะถ้าxเป็น0x1... นี่คือเคล็ดลับเก่าที่ใช้กันมากในภาษาแอสเซมบลี


เร็วกว่านี้!= 1ไหม
Fiddling Bits

2
ในสมัยโบราณในขณะที่ทำประกอบการเพิ่มประสิทธิภาพด้วยตนเอง (x86) ถ้าผมจำได้อย่างถูกต้องxorวิธีการที่มีรหัสเครื่องน้อยลงและกำลังดำเนินการได้เร็วกว่ากำหนดที่สอดคล้องกับ0... แต่คำถามนี้มีxorและเปรียบเทียบดังนั้นฉันอาจจะคิดว่า!=อาจจะมี ได้เร็วขึ้น ฉันไม่แน่ใจ แต่ต้องดูคอมไพเลอร์ที่สร้างขึ้น
Ferenc Deak

10
@BitFiddlingCodeMonkey: ไม่ถ้า XOR เร็วกว่าการทดสอบความเท่าเทียมระดับท้องถิ่นคอมไพเลอร์ของคุณจะปล่อย XORs เพื่อทดสอบความเท่าเทียมกัน ดังนั้น XORs จึงไม่เคยเร็วกว่าการทดสอบความเสมอภาคในการปรับแต่งคอมไพเลอร์ กฎ 101 ของการเขียนโค้ดอย่างรวดเร็วคือ "อย่าพยายามช่วยคอมไพเลอร์คุณจะจบลงด้วยการสร้างโค้ดที่อ่านไม่ได้ซึ่งช้ากว่าในทางปฏิบัติ"
Matt

@fritzone IIRC เทคนิค XOR เกี่ยวข้องกับการบันทึกการลงทะเบียนมากขึ้นการบันทึกการลงทะเบียน b / c ตัวอักษรสามารถเข้ารหัสโดยตรงใน OP ในบางกรณีและความแตกต่างเล็กน้อยกับสถานะสถานะ (แต่การชุมนุมส่วนใหญ่ของฉันเปิดอยู่ 68k และ DSP)
mpdonadio

1
@MPD: โดยปกติจะทำอย่างไรกับการล้างการลงทะเบียน (อย่างน้อย 386+) xor eax, eaxตั้งค่า eax เป็นศูนย์ในสองไบต์ แต่mov eax, 0ใช้เวลาสามหรือหกไบต์ (ขึ้นอยู่กับการเข้ารหัส) และใช้เวลาในการถอดรหัสนานกว่ารูปแบบxorเล็กน้อย
แมตต์

18

ตัว^ดำเนินการคือ bitor xor และ0x1เป็นตัวเลขที่1เขียนเป็นค่าคงที่เลขฐานสิบหก

ดังนั้นx ^ 0x1ประเมินเป็นค่าใหม่ที่เหมือนกับxแต่มีบิตที่สำคัญน้อยที่สุดพลิก

รหัสไม่ได้ทำอะไรมากไปกว่าการเปรียบเทียบ x กับ 1 ในรูปแบบที่ซับซ้อนและคลุมเครือ


11

ตัวดำเนินการ xor (แบบเอกสิทธิ์เฉพาะบุคคลหรือ) มักใช้เพื่อสลับหนึ่งบิตขึ้นไป การดำเนินการคือการถามว่าหนึ่งในบิตของ excactly เป็นหนึ่งหรือไม่สิ่งนี้นำไปสู่ตารางความจริงต่อไปนี้ (A และ B เป็นอินพุต, Y คือเอาต์พุต):

A    B    Y
0    0    0
0    1    1
1    0    1
1    1    0

ตอนนี้จุดประสงค์ของรหัสนี้ดูเหมือนว่าจะมีการตรวจสอบว่า excatly บิตสุดท้ายคือ 1, และคนอื่น ๆ คือ 0, if ( x != 1 )เท่าเทียมกันนี้ เหตุผลสำหรับวิธีการปิดบังนี้อาจเป็นเพราะมีการใช้เทคนิคการจัดการบิตก่อนหน้านี้และอาจใช้สถานที่อื่นในโปรแกรม


8

^เป็นค่าที่เหมาะสมในxor operator cในกรณีของคุณ x คือ xor'ed ด้วย 1 ตัวอย่างเช่นxมีค่า 10 10d ^ 1d ===> 1010b ^ 0001b = 1011b, 1011b == 11dดังนั้นเงื่อนไขจะกลายเป็นจริง


สะกดผิดในคำตอบของคุณ 10 != 1010
Fiddling Bits

@BitFiddlingCodeMonkey: พิมพ์ผิดในความคิดเห็นของคุณ:10 (decimal) == 1010 (binary)
Paul R

6
@ พอลใครจะรู้ได้ยังไงว่าทศนิยมคืออะไรและไบนารีอะไรถ้าคุณไม่ใส่bอะไรลงไป?
Fiddling Bits

0b1010 จะไม่เป็นวิธีการทำสิ่งที่ใช่หรือไม่?
TankorSmash

8

การทดสอบระดับบิตดูเหมือนจะเป็นการทำให้งงงวยโดยเจตนา แต่หากข้อมูลอ้างอิงเป็นข้อมูลองค์กรจากระบบเมนเฟรมของ IBM อาจเป็นเพราะรหัสนั้นถูกเขียนขึ้นเพื่อสะท้อนเอกสารต้นฉบับ รูปแบบข้อมูลของ IBM กลับไปสู่ยุค 1960 และเข้ารหัสธงเป็นบิตเดียวภายในคำเพื่อบันทึกที่เก็บข้อมูล เมื่อมีการปรับเปลี่ยนรูปแบบไบต์แฟล็กถูกเพิ่มที่ท้ายระเบียนที่มีอยู่เพื่อรักษาความเข้ากันได้ย้อนหลัง ตัวอย่างเช่นเอกสารสำหรับเรคคอร์ด SMF อาจแสดงรหัสภาษาแอสเซมบลีเพื่อทดสอบบิตสามบิตภายในสามคำที่แตกต่างกันในบันทึกเดียวเพื่อตัดสินใจว่าข้อมูลนั้นเป็นไฟล์อินพุต ฉันรู้น้อยมากเกี่ยวกับ TCP / IP internals แต่คุณอาจพบค่าสถานะบิตที่นั่นเช่นกัน


7

ตัวดำเนินการ ^ คือ bitwise-xor (ดู &, |) ผลลัพธ์สำหรับคู่บิตคือ

0 ^ 0 == 0
0 ^ 1 == 1
1 ^ 0 == 1
1 ^ 1 == 0

ดังนั้นการแสดงออก

( x ^ 0x1 )

กลับค่า / พลิกบิตที่ 0 ของ x (ปล่อยให้บิตอื่นไม่เปลี่ยนแปลง)

พิจารณาว่า x สามารถมีค่านอกเหนือจาก 0x0 และ 0x1 หรือไม่ เมื่อ x เป็นฟิลด์บิตเดียวมันสามารถมีค่า 0x0 และ 0x1 เท่านั้น แต่เมื่อ x เป็น int (char / short / long / etc) บิตที่อยู่นอกเหนือจาก bit0 สามารถส่งผลต่อผลลัพธ์ของนิพจน์ได้

การแสดงออกตามที่กำหนดช่วยให้บิตข้าง bit0 ส่งผลกระทบต่อผล

if ( 0 != ( x ^ 0x1 ) )

ซึ่งมีความจริงที่เท่าเทียมกันเท่ากับการแสดงออก (เรียบง่าย)

if ( x ^ 0x1 )

โปรดทราบว่าการแสดงออกนี้จะตรวจสอบเพียงบิต 0

if( 0x1 & ( x ^ 0x1 ) )

ดังนั้นการแสดงออกที่นำเสนอคือการรวมสองการตรวจสอบการแสดงออก

if( ( x & ~0x1 )  //look at all bits besides bit0
||  ( x ^ 0x1 ) ) //combine with the xor expression for bit0

ผู้แต่งตั้งใจจะตรวจสอบเพียงบิต 0 และตั้งใจใช้นิพจน์นี้หรือไม่

if( 0x1 & ( x ^ 0x1 ) )

หรือผู้แต่งตั้งใจจะคอมไพล์ค่าสำหรับ bit1-bitN และ xor ของ bit0 หรือไม่?


7

ฉันกำลังเพิ่มคำตอบใหม่เพราะไม่มีใครอธิบายวิธีการได้คำตอบอย่างสังหรณ์ใจ

ผกผันของมี+ การผกผันของคือ-
^^มี

คุณจะทำอย่างไรแก้0 != x - 1สำหรับx? คุณ+ 1ทั้งสองด้าน: →0 + 1 != x - 1 + 1 คุณจะทำอย่างไรแก้สำหรับ? คุณทั้งสองด้าน: →1 != x
0 != x ^ 1x^ 10 ^ 1 != x ^ 1 ^ 11 != x


6

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

อย่างใดถอดรหัสจะต้องสามารถอนุมานได้ว่าค่าเหล่านี้จะหายไป หากพวกเขาอยู่ในตอนท้ายของโครงสร้างบางอย่างมันอาจจะมีการสื่อสารผ่านทางlengthค่าที่มีอยู่เสมอ


4

XOR มีประโยชน์ใน C # flag enum ในการลบแฟล็กเดี่ยวออกจากค่า enum จำเป็นต้องใช้ตัวดำเนินการ xor (การอ้างอิงที่นี่ )

ตัวอย่าง:

[Flags]
enum FlagTest { None 0x0, Test1 0x1, Test2 0x2, Test3 0x4}

FlagTest test = FlagTest.Test2 | FlagTest.Test3;
Console.WriteLine(test); //Out: FlagTest.Test2 | FlagTest.Test3
test = test ^ FlagTest.Test2;
Console.WriteLine(test); //Out: FlagTest.Test3

แต่คำถามนั้นเกี่ยวกับ C ++ ไม่ใช่ C #
theDmi

@theDmi: แต่ยังเกี่ยวกับการจัดการบิตและ bitmask ตั้งค่าสถานะ enum ใน C # แน่นอนเกี่ยวกับ bitmask
Igrek

รวมทั้งจะมีประโยชน์ใน C ++ เช่นกัน ดู: Stack 1 และ Stack 2
Igrek

คำตอบนี้ดูเหมือนจะไม่มีอะไรเกี่ยวข้องกับคำถามต้นฉบับนอกเหนือจากการอภิปรายของผู้ดำเนินการ XOR ระดับบิต
Paul R

4

มีคำตอบที่ดีมากมาย แต่ฉันชอบคิดในวิธีที่ง่ายกว่า

if ( 0 != ( x ^ 0x1 ) );

ก่อนอื่นเลย คำสั่ง if เป็นเท็จเฉพาะถ้าอาร์กิวเมนต์มีค่าเป็นศูนย์ ซึ่งหมายความว่าการเปรียบเทียบไม่เท่ากับศูนย์นั้นไม่มีจุดหมาย

if ( a != 0 );
// Same as
if ( a );

ดังนั้นเราจึง:

if ( x ^ 0x1 );

แฮคเกอร์กับหนึ่ง สิ่งที่ XOR ทำคือตรวจจับบิตที่แตกต่างกัน ดังนั้นถ้าบิตทั้งหมดเท่ากันมันจะคืนค่า 0 เนื่องจาก 0 เป็นเท็จเวลาเดียวที่มันจะคืนค่าเท็จคือถ้าบิตทั้งหมดเหมือนกัน ดังนั้นมันจะเป็นเท็จถ้าข้อโต้แย้งเหมือนกันจริงถ้าพวกเขาแตกต่าง ... เหมือนไม่เท่ากับโอเปอเรเตอร์

if ( x != 0x1 );

หากความจริงความแตกต่างเพียงอย่างเดียวระหว่างสองคือการ!=คืนค่า 0 หรือ 1 ในขณะที่^จะส่งกลับจำนวนใด ๆ แต่ความจริงของผลลัพธ์จะเหมือนกันเสมอ วิธีคิดง่ายๆคือ

(b != c) === !!(b ^ c) // for all b and c

"การทำให้เข้าใจง่าย" สุดท้ายคือการแปลง0x1เป็นทศนิยมซึ่งก็คือ 1 ดังนั้นคำสั่งของคุณจะเทียบเท่ากับ:

if ( x != 1 )

1

^ เป็นตัวดำเนินการXOR ระดับบิต

ถ้า x = 1

          00000001   (x)       (decimal 1)
          00000001   (0x1)     (decimal 1)
XOR       00000000   (0x0)     (decimal 0)

ที่นี่ 0 == (x ^ 0x1)

ถ้า x = 0

          00000000   (x)       (decimal 0)
          00000001   (0x1)     (decimal 1)
XOR       00000001   (0x1)     (decimal 0)

ที่นี่ 0! = (x ^ 0x1)

ตารางความจริงของ xor b:

a           b        a xor b
----------------------------
1           1           0
1           0           1
0           1           1
0           0           0

รหัสนั้นหมายถึง


6
จำเป็นหรือไม่ที่จะต้องโพสต์อีกคำตอบที่ซ้ำกัน?
Mysticial

2
คุณสามารถพูดคำตอบอื่น แต่ไม่ซ้ำกัน ขอโทษถ้าคุณคิด
akbar ali

1

เทคนิคมาตรฐานที่อาจถูกนำมาใช้ที่นี่คือการทำซ้ำสำนวนตามที่ปรากฏในบริบทโดยรอบเพื่อความชัดเจนมากกว่าที่จะทำให้งงงวยโดยการแทนที่ด้วยสำนวนที่ง่ายกว่า แต่ไม่มีความหมายเชิงบริบท

รหัสที่อยู่รอบ ๆ อาจอ้างอิงบ่อยครั้ง(x ^ 1)หรือการทดสอบอาจถามว่า "ถ้าบิต 0 เป็นอีกวิธีหนึ่งบิตมาสก์นี้จะว่างเปล่าหรือไม่"

ระบุว่าเงื่อนไขทำให้บางสิ่งบางอย่างจะเป็น encode() ed อาจเป็นไปได้ว่าในบริบทที่สถานะเริ่มต้นของ 0 บิตได้รับการคว่ำโดยปัจจัยอื่น ๆ และเราต้องการเพียงการเข้ารหัสข้อมูลพิเศษหากบิตใด ๆ เบี่ยงเบนจากค่าเริ่มต้นของพวกเขา )

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


0

ตามที่ฉันเห็นคำตอบจนคิดถึงกฎง่ายๆสำหรับการจัดการXORs โดยไม่ต้องลงรายละเอียดว่าอะไร^และ0xหมายถึงอะไร (และifและ!=อื่น ๆ ) นิพจน์0 != (x^1)สามารถทำใหม่ได้ดังต่อไปนี้โดยใช้ข้อเท็จจริงที่(a^a)==0:

0 != (x^1) <=> [xor left and right side by 1]
(0^1) != (x^1^1) <=>
1 != x
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.