Bài 5 · Cơ bản · 22 phút· Cập nhật 11/06/2026
Biến & kiểu dữ liệu
Biên soạn bởi Nguyễn Anh Tuấn
Khai báo biến với char, short, int, long long, float, double; xem sizeof, signed/unsigned, overflow và cách chọn kiểu dữ liệu phù hợp cho tiền tệ
Một biến (variable) là một vùng lưu trữ được đặt tên để gọi và giữ một giá trị.
Khai báo gồm ba phần: [kiểu dữ liệu] + [tên biến] + (tuỳ chọn) [giá trị khởi tạo].
Quy ước đặt tên biến:
- ▸phải bắt đầu bằng chữ hoặc ký tự gạch dưới “_”
- ▸có thể chứa chữ, số và ký tự gạch dưới “_”
- ▸không được chứa các từ khoá của C như: int, char, if, for v.v...
- ▸phân biệt chữ IN HOA và in thường: fullname và fullName là 2 biến khác nhau
bien.c - ví dụ
#include <stdio.h>
int main(void) {
int tuoi = 15; // khai báo + gán giá trị khởi tạo
printf("Tuoi: %d\n", tuoi);
tuoi = 16; // gán lại giá trị mới
printf("Sang nam: %d\n", tuoi);
return 0;
} Kết quả khi chạy
Tuoi: 15 Sang nam: 16
- ▸Khai báo biến: <kiểu dữ liệu> <tên biến> = <giá trị khởi tạo>; vd int tuoi = 15;
- ▸%d trong printf là “chỗ để điền một số int”.
- ▸Gán lại (tuoi = 16;) ghi đè giá trị cũ trong hộp.
C có vài nhóm kiểu cơ bản như: số nguyên (int), số thực (float) và ký tự (char).
Với số nguyên, int là kiểu mặc định, còn short, long và long long là những cách thu hẹp hoặc mở rộng miền giá trị.
Kích thước cụ thể phụ thuộc vào compiler và platform, nên C cung cấp sizeof để kiểm tra kích thước thực tế trên máy tính.
| Kiểu | Dùng khi nào? | Tối thiểu theo chuẩn C | Điển hình macOS/Linux 64-bit |
|---|---|---|---|
| char | một byte, ký tự hoặc dữ liệu nhỏ | ít nhất 8 bit | 1 byte |
| short | số nguyên nhỏ, ít dùng hơn `int` | ít nhất 16 bit | 2 byte |
| int | số nguyên mặc định, dùng nhiều nhất | ít nhất 16 bit | 4 byte |
| long | số nguyên lớn hơn `int` trên nhiều hệ Unix | ít nhất 32 bit | 8 byte trên macOS/Linux, 4 byte trên Windows 64-bit |
| long long | số nguyên rất lớn, ví dụ tổng tiền theo xu | ít nhất 64 bit | 8 byte |
| float | số có phần thập phân, độ chính xác thấp hơn | theo chuẩn floating của implementation | 4 byte, khoảng 6-7 chữ số có nghĩa |
| double | số có phần thập phân, lựa chọn mặc định hơn `float` | ít nhất rộng bằng `float` | 8 byte, khoảng 15-16 chữ số có nghĩa |
Số nguyên dùng signed/unsigned char, short, int, long, long long; số có phần thập phân dùng float / double. Kích thước trên là điển hình trên macOS/Linux 64-bit; chuẩn C chỉ bảo đảm mức tối thiểu.
kich-thuoc.c - tự hỏi máy của mèo con
#include <limits.h>
#include <stdio.h>
int main(void) {
printf("CHAR_BIT = %d\n", CHAR_BIT);
printf("char : %zu byte\n", sizeof(char));
printf("short : %zu byte\n", sizeof(short));
printf("int : %zu byte\n", sizeof(int));
printf("long : %zu byte\n", sizeof(long));
printf("long long : %zu byte\n", sizeof(long long));
printf("float : %zu byte\n", sizeof(float));
printf("double : %zu byte\n", sizeof(double));
return 0;
} - ▸`sizeof` trả về số byte, in bằng `%zu`.
- ▸Chuẩn C quy định mức tối thiểu, không cam kết mọi máy tính cấp phát biến có cùng kích thước.
- ▸Khi cần đúng số bit, về sau mèo con sẽ gặp các kiểu trong `<stdint.h>` như `int32_t`, `uint64_t`.
Với số nguyên, C cho phép ghép modifier vào int. Viết short cũng là short int, viết long long cũng là long long int. Modifier không làm số "tốt hơn"; nó chỉ đổi miền giá trị và lượng bộ nhớ cần dùng.
| Modifier | Ý nghĩa | Ví dụ |
|---|---|---|
| signed | có dấu âm/dương; mặc định với hầu hết kiểu số nguyên | signed int x; |
| unsigned | không âm, dùng toàn bộ bit cho phía 0 trở lên | unsigned int view_count; |
| short | ít nhất 16 bit, thường nhỏ hơn hoặc bằng `int` | short days; |
| long | ít nhất 32 bit, có thể rộng hơn `int` | long population; |
| long long | ít nhất 64 bit, dùng khi miền giá trị lớn | long long cents; |
modifier.c
#include <stdio.h>
int main(void) {
short days = 365;
int score = 10;
long population = 98000000L;
long long cents = 19999LL;
unsigned int views = 4000000000U;
printf("%hd %d %ld %lld %u\n", days, score, population, cents, views);
return 0;
} Kết quả khi chạy
365 10 98000000 19999 4000000000
- ▸`short`, `long`, `long long` có thể bỏ chữ `int` vì compiler tự hiểu.
- ▸`unsigned` không có số âm; phù hợp cho bit mask, byte thô, hoặc bộ đếm không bao giờ âm.
- ▸Format specifier phải khớp kiểu: `%hd` cho `short`, `%ld` cho `long`, `%lld` cho `long long`, `%u` cho `unsigned int`.
Điều thú vị: char chính là một số nhỏ. Trên môi trường ASCII/UTF-8 compatible mà mèo con thường dùng, mỗi ký tự ứng một số: 'A' = 65, 'B' = 66, 'a' = 97. Vì vậy bạn có thể làm toán trên ký tự:
ascii.c
#include <stdio.h>
int main(void) {
char c = 'A';
printf("%c la ma so %d\n", c, c); // in cả chữ và số
c = c + 1; // sang ký tự kế tiếp
printf("%c la ma so %d\n", c, c);
return 0;
} Kết quả khi chạy
A la ma so 65 B la ma so 66
Cùng một byte, hai cách đọc
Vì mỗi kiểu chỉ có một số byte cố định trên từng máy, nó chỉ chứa được một khoảng giá trị. Vượt khoảng đó là tràn số (overflow). Với kiểu unsigned, giá trị “quay vòng” theo modulo; với kiểu có dấu, tràn là hành vi không xác định (undefined behavior). Ví dụ dưới dùng giả định phổ biến: unsigned char có 8 bit.
tran.c
#include <stdio.h>
int main(void) {
unsigned char b = 255; // 1 byte: chứa 0..255
printf("%u\n", (unsigned)b); // 255
b = b + 1; // vượt 255 -> quay vòng
printf("%u\n", (unsigned)b); // 0
return 0;
} Kết quả khi chạy
255 0
- ▸Chọn kiểu đủ lớn cho dữ liệu: số dân thì dùng int/long, không dùng char.
- ▸unsigned = số không âm; với 8 bit, unsigned char thường là 0..255, còn signed char thường là -128..127.
- ▸Đây đúng là “tràn số 8-bit” bạn đã gặp ở khoá Máy tính hoạt động thế nào.
Trong nhóm số thực, float/double lưu giá trị bằng nhị phân hữu hạn bit, nên nhiều số thập phân chỉ là giá trị gần đúng. Hệ quả nguy hiểm nhất là khi xử lý tiền. Thử cộng 0.10 nhiều lần - để ý nó trông đúng nhưng không đúng:
- ▸float/double là số GẦN đúng - đừng so hai số thực bằng ==.
- ▸ĐỪNG lưu tiền bằng float/double; lưu số nguyên ở đơn vị nhỏ nhất: long long so_xu, chỉ /100 khi hiển thị.
- ▸Đổi tiền tệ: nhân tỉ giá rồi làm tròn một lần theo quy ước nhất quán.
Trung thực: kết quả float còn tuỳ máy
Khi một giá trị không bao giờ đổi (số Pi, số lớn nhất…), hãy đặt tên cho nó như một hằng số - vừa dễ đọc vừa tránh gõ sai. Hai cách hay dùng:
hang.c
#include <stdio.h>
#define PI 3.14159 // thay thế văn bản (không có kiểu)
int main(void) {
const int MAX = 100; // biến chỉ-đọc, có kiểu int
printf("%.2f %d\n", PI, MAX);
return 0;
} Kết quả khi chạy
3.14 100
- ▸const: một biến có kiểu nhưng KHÔNG cho sửa giá trị.
- ▸#define: bộ tiền xử lý dán thẳng giá trị vào trước khi biên dịch.
- ▸Quy ước: tên hằng số viết HOA cho dễ nhận ra.
Câu hỏi thường gặp
C là ngôn ngữ “định kiểu tĩnh” (statically typed): phải nói trước biến thuộc kiểu gì để compiler kiểm tra cách dùng và sinh mã truy cập đúng số byte. Python gắn kiểu với giá trị lúc chạy nên tiện hơn khi thử nhanh, nhưng chậm hơn.
Chúng là modifier của nhóm số nguyên. short là cách viết ngắn của short int, long là long int, và long long là long long int. Chúng đổi kích thước tối thiểu và miền giá trị mà biến chứa được.
char lưu một số nhỏ, thường là 1 byte. “Chữ” là cách hiển thị số đó qua bảng mã của máy; trên môi trường ASCII/UTF-8 compatible, số 65 hiện ra là “A”. Vì vậy bạn có thể làm phép tính trên char như trên số.
Là toán tử cho biết một kiểu hay biến chiếm bao nhiêu byte, vd sizeof(int) thường là 4 trên máy hiện đại. Hữu ích để kiểm tra kích thước trên máy của bạn, vì chuẩn C chỉ đặt mức tối thiểu.
Vì máy lưu số thực bằng một số hữu hạn bit (nhị phân). Nhiều số thập phân như 0.1 không biểu diễn chính xác được bằng nhị phân, nên bị làm tròn nhẹ. Đó là lý do đừng so sánh hai số float bằng dấu “==”.
Bằng số nguyên ở đơn vị nhỏ nhất: long long so_xu (hoặc đồng, nếu tiền không có hào) - chỉ chia 100 khi hiển thị. Không dùng float/double: sai số làm tròn tích luỹ qua nhiều giao dịch thành lệch số tiền, và so sánh == dễ thất bại. Khi đổi tiền tệ: nhân tỉ giá rồi làm tròn một lần theo quy ước nhất quán.
Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.
Trả lời vài câu để chắc rằng em đã nắm bài.
Trong C, một biến char thực chất lưu cái gì?
- 1
Khai báo một biến
Viết một câu lệnh khai báo biến số nguyên tên “diem” mang giá trị 8.
Hoàn thành khi:
int diem = 8; - 2
In kích thước kiểu
Viết chương trình in
sizeof(char),sizeof(short),sizeof(int),sizeof(long),sizeof(long long),sizeof(float),sizeof(double)trên máy của mèo con.Hoàn thành khi: Dùng
%zucho kết quảsizeof, chạy được với bộ cờ của khoá, và nói được kích thước nào đúng như bảng trong bài, kích thước nào khác. - 3
char là số nào?
Chữ “a” (thường) có mã ASCII là bao nhiêu? Dùng công cụ ở Bước 2 (gõ số) hoặc tự tra.
Hoàn thành khi: 97 - gõ 97 vào công cụ, kiểu
signed charvàunsigned charđều báo vừa và hiệna. - 4
Bẫy float với tiền
Dùng công cụ Bước 6: cộng 0.10 một nghìn lần. Giá trị in ra sau khi làm tròn là bao nhiêu, còn giá trị trong máy là bao nhiêu?
Hoàn thành khi: In ra “100.00” trông đúng, nhưng giá trị trong máy là 99.9999999999986 - so sánh
==100.00 là sai. - 5
Tràn số (nối với khoá Máy tính)
Một biến
unsigned charđang là 255 (1 byte). Cộng thêm 1 thì nó thành mấy?Hoàn thành khi: 0 - quay vòng, đúng như “tràn số” của một byte 8-bit ở khoá “Máy tính hoạt động thế nào?”.
- 6
Chọn kiểu số nguyên
Cần lưu số xu của một đơn hàng 199.99. Mèo con chọn kiểu nào:
int,long long, hayfloat? Vì sao?Hoàn thành khi: Chọn
long long so_xu = 19999;hoặcintnếu biết chắc miền giá trị nhỏ; không chọnfloatvì tiền cần lưu bằng số nguyên đơn vị nhỏ nhất.