Giao diện
📦 Variables & Data Types
Trong C++, mỗi biến có kiểu dữ liệu cố định quyết định kích thước bộ nhớ và phạm vi giá trị. Hiểu rõ types là bước đầu tiên để tối ưu performance.
Primitive Types — Các kiểu nguyên thủy
Integer Types
cpp
#include <iostream>
#include <climits> // Chứa INT_MAX, INT_MIN, etc.
int main() {
// Signed integers (có dấu)
short s = 32767; // Thường 2 bytes
int i = 2147483647; // Thường 4 bytes
long l = 2147483647L; // Ít nhất 4 bytes
long long ll = 9223372036854775807LL; // Ít nhất 8 bytes
// Unsigned integers (không dấu)
unsigned int ui = 4294967295U; // Gấp đôi range của int
std::cout << "sizeof(short): " << sizeof(short) << " bytes" << std::endl;
std::cout << "sizeof(int): " << sizeof(int) << " bytes" << std::endl;
std::cout << "sizeof(long): " << sizeof(long) << " bytes" << std::endl;
std::cout << "sizeof(long long): " << sizeof(long long) << " bytes" << std::endl;
std::cout << "INT_MAX: " << INT_MAX << std::endl;
std::cout << "INT_MIN: " << INT_MIN << std::endl;
return 0;
}Bảng kích thước tiêu chuẩn (x64):
| Type | Size (bytes) | Range |
|---|---|---|
short | 2 | -32,768 → 32,767 |
int | 4 | -2.1B → 2.1B |
long | 4 hoặc 8 | Platform-dependent |
long long | 8 | ±9.2 × 10¹⁸ |
unsigned int | 4 | 0 → 4.3B |
⚠️ Integer Overflow
cpp
int x = INT_MAX; // 2147483647
x = x + 1; // 💥 Overflow! x = -2147483648 (undefined in signed)
unsigned int u = 0;
u = u - 1; // u = 4294967295 (wraps around)Floating-Point Types
cpp
#include <iostream>
#include <iomanip> // Cho std::setprecision
#include <cfloat> // FLT_MAX, DBL_MAX
int main() {
float f = 3.14159265358979f; // 4 bytes, ~7 chữ số precision
double d = 3.14159265358979; // 8 bytes, ~15-16 chữ số precision
long double ld = 3.14159265358979L; // 8-16 bytes (platform-dependent)
std::cout << std::setprecision(20);
std::cout << "float: " << f << std::endl;
std::cout << "double: " << d << std::endl;
std::cout << "long double: " << ld << std::endl;
std::cout << "\nSizes:" << std::endl;
std::cout << "sizeof(float): " << sizeof(float) << " bytes" << std::endl;
std::cout << "sizeof(double): " << sizeof(double) << " bytes" << std::endl;
return 0;
}Output:
float: 3.1415927410125732422
double: 3.1415926535897900074
long double: 3.1415926535897900074
Sizes:
sizeof(float): 4 bytes
sizeof(double): 8 bytes🎓 Floating-Point Precision
Lưu ý float chỉ chính xác ~7 chữ số! Với tiền bạc, đừng bao giờ dùng float/double — dùng int (đơn vị cents) hoặc thư viện decimal.
Character & Boolean
cpp
#include <iostream>
int main() {
// char — 1 byte, lưu 1 ký tự hoặc số -128 → 127
char c = 'A';
char num = 65; // Cũng là 'A' (ASCII code)
std::cout << "char c: " << c << std::endl; // A
std::cout << "char num: " << num << std::endl; // A
std::cout << "int(c): " << static_cast<int>(c) << std::endl; // 65
// bool — thường 1 byte, chỉ true/false
bool isActive = true;
bool isEmpty = false;
std::cout << "sizeof(bool): " << sizeof(bool) << " bytes" << std::endl;
std::cout << "true as int: " << isActive << std::endl; // 1
std::cout << "false as int: " << isEmpty << std::endl; // 0
// bool trong điều kiện
int x = 42;
if (x) { // Bất kỳ non-zero nào cũng là true
std::cout << "x is truthy!" << std::endl;
}
return 0;
}Type Modifiers
cpp
#include <iostream>
int main() {
// signed (mặc định cho int)
signed int a = -100;
// unsigned — không âm, range gấp đôi
unsigned int b = 4000000000U;
// short — nhỏ hơn
short int c = 30000;
// long — lớn hơn
long int d = 2000000000L;
long long int e = 9000000000000000000LL;
// Có thể bỏ "int"
unsigned u = 100;
long l = 200L;
std::cout << "unsigned int max: " << b << std::endl;
std::cout << "long long: " << e << std::endl;
return 0;
}Modern C++: auto Keyword (C++11)
auto cho phép compiler suy luận kiểu từ giá trị khởi tạo:
cpp
#include <iostream>
#include <vector>
#include <string>
int main() {
// auto suy luận từ literal
auto x = 42; // int
auto y = 3.14; // double
auto c = 'A'; // char
auto str = "Hello"; // const char* (KHÔNG phải std::string!)
// auto với std::string
auto name = std::string("PENALGO"); // std::string
// auto RẤT hữu ích với iterators
std::vector<int> numbers = {1, 2, 3, 4, 5};
// Thay vì:
// std::vector<int>::iterator it = numbers.begin();
// Dùng auto:
for (auto it = numbers.begin(); it != numbers.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// Range-based for với auto (C++11)
for (auto& num : numbers) { // auto& để modify
num *= 2;
}
for (const auto& num : numbers) { // const auto& để read-only
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}📌 HPN Standard: Khi nào dùng auto?
| Situation | Recommendation |
|---|---|
| Iterator types | ✅ Luôn dùng auto |
| Lambda expressions | ✅ Bắt buộc auto |
| Complex template types | ✅ Dùng auto cho readable |
Literals đơn giản (auto x = 42;) | ⚠️ Có thể, nhưng explicit type rõ ràng hơn |
| Function return type | ⚠️ Cân nhắc — có thể khó đọc |
Fixed-Width Integer Types (C++11)
Để đảm bảo kích thước chính xác trên mọi platform:
cpp
#include <iostream>
#include <cstdint> // int8_t, int16_t, int32_t, int64_t
int main() {
// Guaranteed exact width
int8_t a = 127; // Exactly 8 bits (1 byte)
int16_t b = 32767; // Exactly 16 bits (2 bytes)
int32_t c = 2147483647; // Exactly 32 bits (4 bytes)
int64_t d = 9223372036854775807LL; // Exactly 64 bits (8 bytes)
// Unsigned versions
uint8_t ua = 255;
uint16_t ub = 65535;
uint32_t uc = 4294967295U;
uint64_t ud = 18446744073709551615ULL;
std::cout << "int8_t size: " << sizeof(int8_t) << std::endl; // 1
std::cout << "int32_t size: " << sizeof(int32_t) << std::endl; // 4
std::cout << "int64_t size: " << sizeof(int64_t) << std::endl; // 8
return 0;
}📌 Best Practice
- API/Network data: Dùng
int32_t,uint64_tđể đảm bảo cross-platform - Loop counters:
inthoặcsize_t(cho array indices) - IDs từ database:
int64_thoặcuint64_t
Type Casting
cpp
#include <iostream>
int main() {
// Implicit conversion (tự động)
int i = 42;
double d = i; // 42.0 — OK, không mất data
double pi = 3.14159;
int truncated = pi; // 3 — ⚠️ Mất phần thập phân!
// Explicit casting với static_cast (Modern C++)
double result = static_cast<double>(5) / 2; // 2.5
int rounded = static_cast<int>(pi); // 3
// C-style cast (không khuyên dùng)
int c_style = (int)pi; // Works nhưng less safe
std::cout << "d: " << d << std::endl;
std::cout << "truncated: " << truncated << std::endl;
std::cout << "result: " << result << std::endl;
return 0;
}🐛 Spot the Bug: Type Edition
🐛 Bug Hunt Challenge
Output của chương trình sau là gì?
cpp
#include <iostream>
int main() {
unsigned int a = 10;
int b = -20;
if (a + b > 0) {
std::cout << "Positive!" << std::endl;
} else {
std::cout << "Negative or zero!" << std::endl;
}
return 0;
}💡 Gợi ý
Khi signed gặp unsigned trong biểu thức, chuyện gì xảy ra?
🔍 Giải thích chi tiết
Output: Positive! 😱
Tại sao? Khi mix signed và unsigned:
b(-20) được convert sang unsigned- -20 trong unsigned 32-bit = 4294967276
- 10 + 4294967276 = 4294967286 > 0 → Positive!
Fix:
cpp
if (static_cast<int>(a) + b > 0) { ... }
// Hoặc tốt hơn: dùng cùng type cho cả hai📚 Tổng kết
| Concept | Key Takeaway |
|---|---|
| Primitive types | int, float, double, char, bool — biết size của từng loại |
| sizeof() | Cho biết kích thước chính xác (bytes) |
| Type modifiers | signed, unsigned, short, long — thay đổi range |
| auto (C++11) | Compiler suy luận type — useful cho iterators |
| Fixed-width | int32_t, uint64_t — guaranteed size |
| Mixing signed/unsigned | ⚠️ Nguy hiểm! Avoid hoặc cast explicitly |
➡️ Tiếp theo
Bạn đã hiểu Data Types. Tiếp theo: Pointers & References — Phần quan trọng nhất của C++!