Ch4: Operations on Data¶
TL;DR: Arithmetic and Shift Operators: C++ เตรียมตัวดำเนินการพื้นฐานทั้งคณิตศาสตร์, Compound Assignment, Increment, Decrement, และ Bitwise เพื่อใช้จัดการสถานะหน่วยความจำได้โดยตรง | ทุกประเภทข้อมูลตัวเลขพื้นฐานจะมีขอบเขตที่แน่นอนซึ่งกำหนดโดย Standard Library (
std::numeric_limits) และจำนวน Bit ที่ใช้แทนค่าในระบบเลขฐานสอง — ชนิดข้อมูลแบบมีเครื่องหมาย (Signed Types) จะใช้ Two's Complement ส่วนชนิดข้อมูลแบบไม่มีเครื่องหมาย (Unsigned Types) จะมีการเวียนกลับครบรอบ (Wrap Around)
⚡ Quick Reference¶
#include <iostream>
int main() {
int a = 10;
int b = 3;
// a + b, a - b, a * b, a / b, a % b - การดำเนินการทางคณิตศาสตร์พื้นฐาน
int sum = a + b;
int diff = a - b;
int prod = a * b;
int quot = a / b;
int rem = a % b;
// ++a, a++, --a, a-- - ตัวดำเนินการเพิ่มค่าและลดค่า
int x1 = ++a; // Prefix: เพิ่มค่าก่อนแล้วค่อยคืนผลลัพธ์
int x2 = a++; // Postfix: คืนผลลัพธ์ก่อนแล้วค่อยเพิ่มค่า
// a += b, a -= b - ตัวดำเนินการกำหนดค่าแบบผสม
a += b;
// a < b, a > b, a == b - ตัวดำเนินการเปรียบเทียบค่า
bool eq = (a == b);
// a && b, a || b, !a - ตัวดำเนินการทางตรรกศาสตร์
bool res = (a > 5 && b < 10);
// std::ios::boolalpha, std::noboolalpha - แสดงค่าความจริงเป็นข้อความ true/false หรือตัวเลข 1/0
std::cout << std::boolalpha << res << std::noboolalpha << std::endl;
return 0;
}
ขีดจำกัดตัวเลขและการแทนค่าเลขฐานสอง (Numeric Limits & Binary Representation)¶
#include <iostream>
#include <limits>
#include <bitset>
#include <cstdint>
int main() {
// std::numeric_limits<T> - ค้นหาค่า min/max/precision สำหรับชนิดข้อมูลตัวเลขใดๆ ในขั้นตอน Compile-time
std::cout << "int min: " << std::numeric_limits<int>::min() << '\n';
std::cout << "int max: " << std::numeric_limits<int>::max() << '\n';
std::cout << "size_t max: " << std::numeric_limits<size_t>::max() << '\n';
// std::numeric_limits<T>::digits - จำนวน Bit ที่ใช้สำหรับเก็บค่า (ไม่รวม Bit แสดงเครื่องหมาย)
std::cout << "int digits: " << std::numeric_limits<int>::digits << '\n';
// Fixed-width integer types (<cstdint>) - รับประกันขนาด Bit ที่แน่นอนในทุกแพลตฟอร์ม
int32_t a{100};
uint8_t b{255};
// std::bitset<N> - แสดงข้อมูลดิบในรูปแบบเลขฐานสองของตัวแปร
std::bitset<8> bits(b);
std::cout << "255 as bits: " << bits << '\n';
// Two's complement: ตัวเลขจำนวนเต็มลบแบบมีเครื่องหมายจะแสดงผลโดยการกลับบิตทั้งหมดแล้วบวกด้วย 1
int8_t neg{-1};
std::bitset<8> negBits(static_cast<uint8_t>(neg));
std::cout << "-1 as bits: " << negBits << '\n'; // 11111111
return 0;
}
🧠 Core Concepts¶
- Arithmetic and Shift Operators: C++ เตรียมตัวดำเนินการพื้นฐานทั้งคณิตศาสตร์, Compound Assignment, Increment, Decrement, และ Bitwise เพื่อใช้จัดการสถานะหน่วยความจำได้โดยตรง
- Constant Correctness (
const,constexpr,constinit): การกำหนดค่าคงที่เพื่อความปลอดภัย ประกอบด้วยconst(เป็น Read-only ตอน Runtime),constexpr(ประมวลผลค่าคงที่ตอน Compile), และconstinit(การันตีการเซ็ตค่าเริ่มต้นตอน Compile แต่ตัวแปรยังสามารถเปลี่ยนค่าได้ตอน Runtime) - Integral Promotion & Type Conversion: ในการคำนวณทางคณิตศาสตร์ ชนิดข้อมูลขนาดเล็กอย่าง
shortและcharจะถูกโปรโมตเป็นintโดยอัตโนมัติ และควรใช้static_castในการทำ Type Conversion เพื่อเลี่ยงการเกิด Warning จากการลดรูปข้อมูล std::numeric_limits<T>: คลาสคุณลักษณะ (Trait Class) ที่สามารถตรวจสอบได้ในขั้นตอน Compile-time เพื่อดึงค่าต่ำสุด (min), ค่าสูงสุด (max), ความแม่นยำ (precision) และคุณสมบัติอื่นๆ ของชนิดข้อมูลตัวเลขใดๆ — ซึ่งปลอดภัยกว่าการเขียนตัวเลขแบบเจาะจง (Hardcoding Magic Numbers) เช่น2147483647- Two's Complement: รูปแบบเลขฐานสองมาตรฐานสำหรับเก็บข้อมูลจำนวนเต็มแบบมีเครื่องหมาย (Signed Integers) โดยบิตที่มีนัยสำคัญสูงสุด (Most Significant Bit) จะทำหน้าที่เป็นบิตแสดงเครื่องหมาย (Sign Bit) ส่วนค่าติดลบจะถูกจัดเก็บด้วยการกลับบิตทั้งหมด (Invert Bits) ของค่าบวกนั้นๆ แล้วบวกด้วย 1
- Fixed-Width Types: ไลบรารี
<cstdint>มีการเตรียมชนิดข้อมูล เช่นint32_t,uint8_t,int64_tที่รับประกันขนาดความกว้างบิตที่แน่นอนโดยไม่ขึ้นกับแพลตฟอร์ม แตกต่างจากintหรือlongที่ขนาดอาจเปลี่ยนแปลงได้ตามแต่ตัวคอมไพเลอร์หรือสถาปัตยกรรมของเครื่อง
⚠️ Pitfalls (Quick Scan)¶
| ข้อผิดพลาด | วิธีแก้ |
|---|---|
| ลืมใส่ Semicolon ปิดท้ายคำสั่ง | ใส่เครื่องหมาย ; ปิดท้ายคำสั่งเสมอ (Ch 4.3, 5.2) |
การใช้ Block Comment ซ้อนกัน (/ / ... / /) |
ห้ามคอมเมนต์แบบบล็อกซ้อนกัน ให้ใช้คอมเมนต์บรรทัดเดียว (//) แทน (Ch 4.2) |
| การหารด้วยศูนย์ (Division by zero) | ตรวจสอบเสมอว่าตัวหารไม่ใช่ศูนย์ก่อนทำการหาร (Ch 4.3) |
ลืมเขียน Directive #include ที่จำเป็น |
ตรวจเช็คและทำการ #include Header ที่จำเป็นไว้ด้านบนของไฟล์เสมอ (Ch 4.1) |
| การกำหนดค่าเริ่มต้นแบบ Functional ด้วยชนิดข้อมูลที่แคบกว่า (Narrowing) | ใช้ Braced Initialization {} เพื่อดักจับปัญหาการลดรูปข้อมูลตอน Compile (6.2, 7.2) |
| การใช้ตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้น | กำหนดค่าเริ่มต้นให้ตัวแปรเสมอ หรือใช้สัญลักษณ์ {} เพื่อทำ Zero-init (5.2, 9.2) |
การใช้ std::cin >> รับค่าข้อมูลที่มีช่องว่าง |
ใช้ std::getline(cin, var) เมื่อต้องการรับข้อมูล String ที่มีช่องว่าง (7.2) |
| การใช้เลข 0 นำหน้าเลขฐานสิบ | ห้ามใส่เลข 0 นำหน้าจำนวนเต็มเลขฐานสิบโดยเด็ดขาด (6.2) |
เข้าใจผิดว่า std::setw จะจัดระเบียบทุกค่าที่ตามมา |
ต้องเรียกใช้ std::setw หน้าตัวแปรทุกครั้งที่จะแสดงผลตาราง (5.8) |
| การลดรูปข้อมูลโดยนัย (Implicit Narrowing) ในการกำหนดค่า | ใช้ static_cast<int>() เพื่อแสดงเจตนาแปลงข้อมูลชัดเจน หรือใช้ Braced initialization (7.2) |
| ปัญหาค่าเกินขีดจำกัด (Overflow) ใน Unsigned type | ตรวจสอบขอบเขตด้วย std::numeric_limits และใช้ประเภทข้อมูลที่ใหญ่พอ (7.4) |
| ปัญหาค่าต่ำกว่าขีดจำกัด (Underflow) ใน Unsigned type | ตรวจสอบก่อนเสมอว่าตัวแปรมีค่ามากกว่าศูนย์ก่อนทำการลดค่า (7.4) |
การนำข้อมูลประเภท short หรือ char มาคำนวณทางคณิตศาสตร์ |
ทำความเข้าใจเรื่องการโปรโมตข้อมูล และ Cast ผลลัพธ์กลับหากต้องการชนิดข้อมูลเดิม (5.11) |
การกำหนดค่าคงที่ constinit จากตัวแปรธรรมดาที่รู้ผลตอน Runtime |
ตรวจสอบให้แน่ใจว่าค่าที่ป้อนให้ constinit เป็นชนิด constexpr (6.5) |
การประกาศใช้ constexpr คู่กับ constinit ในตัวแปรเดียวกัน |
เลือกตัวใดตัวหนึ่ง: constexpr สำหรับค่าคงที่เปลี่ยนไม่ได้ หรือ constinit สำหรับตัวแปรที่เริ่มตอน Compile แต่แก้ไขได้ (6.5) |
การใช้เงื่อนไขตรวจสอบที่ไม่ใช่ค่าคงที่ใน static_assert |
ระบุเฉพาะเงื่อนไขและตัวแปรที่สามารถคำนวณเสร็จสิ้นได้ตั้งแต่ช่วงคอมไพล์เท่านั้น (6.4) |
| เข้าใจผิดว่าการเปรียบเทียบตรรกะจะพิมพ์ข้อความ true/false เสมอ | ใส่คำสั่ง std::cout << std::boolalpha เพื่อปรับแต่งการแสดงผล (5.6) |
| เข้าใจผิดว่าเลขฐานสิบหกจะพิมพ์ตัวอักษรพิมพ์ใหญ่เสมอ | ใช้ std::uppercase ร่วมด้วยเพื่อบังคับแสดงตัวพิมพ์ใหญ่ (5.8) |
| ตั้งค่าความละเอียดทศนิยมเกินขีดจำกัดประเภทข้อมูล | ห้ามตั้งค่าทศนิยมใน setprecision เกิน 7 หลักสำหรับ float และ 15 หลักสำหรับ double (5.8) |
กำหนดค่า min/max แบบเจาะจงลงไปตรงๆ (เช่น 2147483647) |
ใช้ std::numeric_limits<T>::max() แทน |
ทึกทักว่า int จะมีขนาด 4 Bytes เสมอในทุกแพลตฟอร์ม |
ใช้ชนิดข้อมูลแบบระบุความกว้างคงที่ (int32_t) เมื่อต้องการขนาดที่แน่นอน |
| เปรียบเทียบข้อมูลแบบมีเครื่องหมายและไม่มีเครื่องหมายโดยตรง | แปลงชนิดข้อมูลอย่างชัดเจน (Explicit Cast) หรือใช้ชนิดข้อมูลประเภทเดียวกันเพื่อเลี่ยงการแปลงค่าโดยอัตโนมัติ |
| คิดว่าเลขติดลบแค่สลับบิตเครื่องหมาย (Sign Bit) เท่านั้น | ทำความเข้าใจระบบ Two's Complement ว่าเป็นการกลับบิตทั้งหมดและบวก 1 |
ใช้ int8_t/uint8_t ใน std::cout โดยคาดหวังว่าจะแสดงตัวเลข |
ชนิดข้อมูลเหล่านี้มักเป็นนามแฝง (Alias) ของ char ต้องแปลงเป็น int ก่อน ไม่เช่นนั้นจะแสดงผลเป็นตัวอักษร |
📖 Full Details¶
Cause → Effect → Fix พร้อม timestamp (คลิกเพื่อดู)
* **ลืมใส่ Semicolon ปิดท้ายคำสั่ง** -> **เกิด Compile-time error แจ้งเตือนเรื่องลืมใส่ Semicolon ก่อนสั่ง Return** -> **ใส่เครื่องหมาย `;` ปิดท้ายคำสั่งเสมอ (Ch 4.3, 5.2)** * **การใช้ Block Comment ซ้อนกัน (`/* /* ... */ */`)** -> **เกิด Compile error เนื่องจากหาจุดปิดคอมเมนต์ไม่เจอ** -> **ห้ามคอมเมนต์แบบบล็อกซ้อนกัน ให้ใช้คอมเมนต์บรรทัดเดียว (`//`) แทน (Ch 4.2)** * **การหารด้วยศูนย์ (Division by zero)** -> **Compiler แจ้งเตือนและโปรแกรมจะแครชในจังหวะ Runtime เมื่อดึงผลลัพธ์ไปใช้** -> **ตรวจสอบเสมอว่าตัวหารไม่ใช่ศูนย์ก่อนทำการหาร (Ch 4.3)** * **ลืมเขียน Directive `#include` ที่จำเป็น** -> **เกิด Compile error แจ้งว่า `namespace std has no member`** -> **ตรวจเช็คและทำการ `#include` Header ที่จำเป็นไว้ด้านบนของไฟล์เสมอ (Ch 4.1)** * **การกำหนดค่าเริ่มต้นแบบ Functional ด้วยชนิดข้อมูลที่แคบกว่า (Narrowing)** -> **ทศนิยมจะถูกปัดทิ้งอย่างเงียบๆ โดยไม่มี Warning แจ้งเตือน** -> **ใช้ Braced Initialization `{}` เพื่อดักจับปัญหาการลดรูปข้อมูลตอน Compile (6.2, 7.2)** * **การใช้ตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้น** -> **ตัวแปรจะเก็บค่าขยะจากหน่วยความจำ ทำให้เกิด Undefined behavior** -> **กำหนดค่าเริ่มต้นให้ตัวแปรเสมอ หรือใช้สัญลักษณ์ `{}` เพื่อทำ Zero-init (5.2, 9.2)** * **การใช้ `std::cin >>` รับค่าข้อมูลที่มีช่องว่าง** -> **ข้อมูลสายอักขระจะถูกตัดตอนเจอช่องว่างแรก และข้อมูลส่วนที่เหลือจะค้างอยู่ใน Buffer** -> **ใช้ `std::getline(cin, var)` เมื่อต้องการรับข้อมูล String ที่มีช่องว่าง (7.2)** * **การใช้เลข 0 นำหน้าเลขฐานสิบ** -> **Compiler จะตีความเลขดังกล่าวเป็นเลขฐานแปด (Octal) ซึ่งให้ผลลัพธ์ที่ไม่ถูกต้อง** -> **ห้ามใส่เลข 0 นำหน้าจำนวนเต็มเลขฐานสิบโดยเด็ดขาด (6.2)** * **เข้าใจผิดว่า `std::setw` จะจัดระเบียบทุกค่าที่ตามมา** -> **ช่องว่างจัดเรียงจะส่งผลกับข้อมูลตัวถัดไปเพียงตัวเดียวเท่านั้น** -> **ต้องเรียกใช้ `std::setw` หน้าตัวแปรทุกครั้งที่จะแสดงผลตาราง (5.8)** * **การลดรูปข้อมูลโดยนัย (Implicit Narrowing) ในการกำหนดค่า** -> **ตัวเลขทศนิยมจะถูกตัดเศษทิ้งกลายเป็นจำนวนเต็ม ทำให้สูญเสียข้อมูล** -> **ใช้ `static_cast📎 Repo Files¶
05.OperationsOnData/5.2.BasicOperations/main.cpp05.OperationsOnData/5.3.PrecedenceAndAssociativity/main.cpp05.OperationsOnData/5.4.PrefixPostfixIncrementDecrement/main.cpp05.OperationsOnData/5.5.CompoundAssignmentOperators/main.cpp05.OperationsOnData/5.6.RelationalOperators/main.cpp05.OperationsOnData/5.7.LogicalOperators/main.cpp05.OperationsOnData/5.8.OutputFormatting/main.cpp05.OperationsOnData/5.9.NumericLimits/main.cpp05.OperationsOnData/5.10.MathFunctions/main.cpp05.OperationsOnData/5.11.WeirdIntegralTypes/main.cpp06.LiteralsAndConstants/6.2Literals/main.cpp06.LiteralsAndConstants/6.3Constants/main.cpp06.LiteralsAndConstants/6.4ConstantExpressions/main.cpp06.LiteralsAndConstants/6.5constinit/main.cpp07.ConversionsOverflowAndUnderflow/7.2ImplicitDataConversions/main.cpp07.ConversionsOverflowAndUnderflow/7.3ExplicitDataConversions/main.cpp07.ConversionsOverflowAndUnderflow/7.4OverflowAndUnderflow/main.cpp08.BitwiseOperators/8.2PrintingIntegersInBinary/main.cpp08.BitwiseOperators/8.3ShiftOperators/main.cpp08.BitwiseOperators/8.4LogicalBitwiseOperators/main.cpp08.BitwiseOperators/8.5CompoundBitwiseOperators/main.cpp08.BitwiseOperators/8.6Masks/main.cpp08.BitwiseOperators/8.7MasksExample/main.cpp08.BitwiseOperators/8.8PackingColorInformation/main.cpp09.VariableLifetimeAndScope/9.2VariableScope/main.cpp07.ConversionsOverflowAndUnderflow/main.cpp08.BitwiseOperators/main.cpp