O3. Other 3

Con Trỏ (Pointer)

Biến và Vùng Nhớ

Tổ Chức Bộ Nhớ

Sơ đồ bộ nhớ:

Địa chỉ    Nội dung
┌──────┬──────────┐
│ 0x00 │    ...   │
├──────┼──────────┤
│ 0x01 │    ...   │
├──────┼──────────┤
│ 0x02 │    ...   │
├──────┼──────────┤
│ 0x03 │    ...   │
├──────┼──────────┤
│ ...  │    ...   │
└──────┴──────────┘

Biến và Địa Chỉ

Khi khai báo biến, máy tính cấp phát vùng nhớ cho biến đó.

int main() {
    char ch = 'x';    // 1 byte tại địa chỉ 0x50
    int a = 7;        // 4 bytes tại địa chỉ 0x54
}

Sơ đồ:

Địa chỉ    Biến    Giá trị
┌──────┬──────┬──────────┐
│ 0x50 │  ch  │    'x'   │ 1 byte
├──────┼──────┼──────────┤
│ 0x51 │      │          │
├──────┼──────┼──────────┤
│ 0x52 │      │          │
├──────┼──────┼──────────┤
│ 0x53 │      │          │
├──────┼──────┼──────────┤
│ 0x54 │  a   │    7     │ 4 bytes
│ 0x55 │      │          │
│ 0x56 │      │          │
│ 0x57 │      │          │
└──────┴──────┴──────────┘

Toán Tử & và *

Toán Tử & (Address-of Operator)

int value = 3200;

cout << value;      // 3200 (giá trị)
cout << &value;     // 0x50 (địa chỉ)

Toán Tử * (Dereferencing Operator)

int value = 3200;

cout << *(&value);  // 3200 (giá trị tại địa chỉ của value)

Mối quan hệ:

value      // Giá trị của biến
&value     // Địa chỉ của biến
*(&value)  // Giá trị tại địa chỉ đó = value

Khái Niệm Con Trỏ

Đặc điểm:

  • Con trỏ lưu địa chỉ, không lưu giá trị trực tiếp
  • Con trỏ có kiểu (int*, float*, char*,…)
  • Kích thước con trỏ thường cố định (4 bytes trên hệ 32-bit, 8 bytes trên 64-bit)

Minh họa:

int a = 1000;
int *ptr = &a;  // ptr lưu địa chỉ của a

// ptr "trỏ đến" a
     ptr              a
┌──────────┐    ┌──────────┐
│  0x34    │───→│   1000   │
└──────────┘    └──────────┘
  Địa chỉ: 0x90   Địa chỉ: 0x34

Khai Báo Con Trỏ

Cú pháp:

<kiểu_dữ_liệu> *<tên_con_trỏ>;

Ví dụ:

int *ptrI;        // Con trỏ kiểu int
float *ptrF;      // Con trỏ kiểu float
char *ptrC;       // Con trỏ kiểu char
double *ptrD;     // Con trỏ kiểu double

// Khai báo nhiều con trỏ
int *p1, *p2, *p3;

// LƯU Ý: Dấu * gắn với từng biến
int* p4, p5;      // p4 là con trỏ, p5 là biến int thường!

Khởi Tạo Con Trỏ

Cú pháp:

<kiểu_dữ_liệu> *<tên_con_trỏ> = &<biến>;

Ví dụ:

int a = 10;
int *ptr = &a;    // ptr lưu địa chỉ của a

float x = 3.14;
float *pf = &x;

// SAI: Kiểu không khớp
int a = 10;
float *ptr = &a;  // Lỗi!

Sử Dụng Con Trỏ

Đọc Giá Trị Qua Con Trỏ

int a = 1000;
int *ptr = &a;

cout << ptr;      // Địa chỉ của a (VD: 0x34)
cout << *ptr;     // Giá trị tại địa chỉ đó = 1000

Thay Đổi Giá Trị Qua Con Trỏ

int a = 1000;
int *ptr = &a;

*ptr = 3200;      // Thay đổi giá trị của a qua con trỏ
cout << a;        // 3200

(*ptr)++;         // Tăng giá trị của a lên 1
cout << a;        // 3201

Ví Dụ Chi Tiết

#include <iostream>
using namespace std;

int main() {
    int a;
    int *ptr;
    int value;
    
    a = 3200;
    ptr = &a;
    value = --(*ptr);
    
    cout << "Gia tri a: " << a << endl;           // 3199
    cout << "Gia tri value: " << value << endl;   // 3199
    cout << "Dia chi a: " << &a << endl;          // 0x34
    cout << "Gia tri ptr: " << ptr << endl;       // 0x34
    cout << "Gia tri *ptr: " << *ptr << endl;     // 3199
    
    return 0;
}

Phân tích từng bước:

Bước 1: a = 3200
┌─────────┐
│  3200   │ a (0x34)
└─────────┘

Bước 2: ptr = &a
┌─────────┐      ┌─────────┐
│  0x34   │ ptr →│  3200   │ a
└─────────┘      └─────────┘

Bước 3: value = --(*ptr)
- Giảm *ptr xuống 3199 (a = 3199)
- Gán value = 3199

Phép Gán Con Trỏ

int x = 10;
int *p1 = &x;
int *p2;

p2 = p1;          // p2 cũng trỏ đến x
*p2 = 20;         // Thay đổi x thành 20

cout << x;        // 20
cout << *p1;      // 20
cout << *p2;      // 20

Minh họa:

Ban đầu:
p1 → [27]
p2 → [5]

Sau p2 = p1:
p1 → [27]
p2 → [27]  (cùng trỏ đến 27)

Sau *p2 = *p1:
p1 → [27]
p2 → [27]  (p2 vẫn trỏ chỗ cũ, nhưng giá trị = 27)

Con Trỏ NULL

int *p1 = NULL;         // C-style
int *p2 = nullptr;      // C++11 (khuyến khích)

// Kiểm tra con trỏ NULL
if (p1 == NULL) {
    cout << "Con tro NULL" << endl;
}

Kích Thước Con Trỏ

int a;
double b;
char c;
int *pa;
double *pb;
char *pc;

cout << sizeof(a) << endl;      // 4
cout << sizeof(b) << endl;      // 8
cout << sizeof(c) << endl;      // 1

cout << sizeof(pa) << endl;     // 4 (32-bit) hoặc 8 (64-bit)
cout << sizeof(pb) << endl;     // 4 (32-bit) hoặc 8 (64-bit)
cout << sizeof(pc) << endl;     // 4 (32-bit) hoặc 8 (64-bit)

Từ Khóa const và Con Trỏ

1. Con Trỏ Trỏ Đến Hằng

const int *p;         // Không thể thay đổi giá trị qua p
int const *p;         // Tương đương

int x = 10;
const int *p = &x;

*p = 20;              // SAI: không thể thay đổi giá trị
p = &y;               // OK: có thể trỏ đến nơi khác

2. Con Trỏ Hằng

int * const p;        // Không thể thay đổi địa chỉ mà p trỏ đến

int x = 10;
int * const p = &x;

*p = 20;              // OK: có thể thay đổi giá trị
p = &y;               // SAI: không thể trỏ đến nơi khác

3. Con Trỏ Hằng Trỏ Đến Hằng

const int * const p;  // Không thể thay đổi cả địa chỉ lẫn giá trị

int x = 10;
const int * const p = &x;

*p = 20;              // SAI
p = &y;               // SAI

Bảng tóm tắt:

Khai báoThay đổi giá trịThay đổi địa chỉ
int *p
const int *p
int * const p
const int * const p

Con Trỏ và Hàm

Truyền Con Trỏ Vào Hàm

// Hàm hoán vị hai số (dùng con trỏ)
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 7, y = 8;
    cout << "Truoc: x = " << x << ", y = " << y << endl;
    
    swap(&x, &y);
    
    cout << "Sau: x = " << x << ", y = " << y << endl;
    return 0;
}

Hàm Nhập Giá Trị Qua Con Trỏ

void nhapGiaTri(int *p) {
    cout << "Nhap gia tri: ";
    cin >> *p;
}

int main() {
    int a;
    nhapGiaTri(&a);
    cout << "Gia tri a = " << a << endl;
    return 0;
}

Con Trỏ và Mảng

Mảng và Hằng Con Trỏ

int arr[6] = {5, 6, 9, 4, 1, 2};

cout << arr;         // Địa chỉ phần tử đầu tiên
cout << &arr[0];     // Tương đương

Sơ đồ:

     arr (0x10)
┌────┬────┬────┬────┬────┬────┐
│ 5  │ 6  │ 9  │ 4  │ 1  │ 2  │
└────┴────┴────┴────┴────┴────┘
0x10 0x14 0x18 0x1C 0x20 0x24

Truy Xuất Mảng Qua Con Trỏ

int arr[6] = {5, 6, 9, 4, 1, 2};
int *p = arr;

// Các cách truy xuất tương đương:
arr[i]       *(arr + i)     p[i]       *(p + i)
&arr[i]      arr + i        &p[i]      p + i

Ví dụ:

int arr[6] = {5, 6, 9, 4, 1, 2};
int *p = arr;

cout << arr[2];      // 9
cout << *(arr + 2);  // 9
cout << p[2];        // 9
cout << *(p + 2);    // 9

cout << &arr[2];     // 0x18
cout << arr + 2;     // 0x18
cout << &p[2];       // 0x18
cout << p + 2;       // 0x18

Phép Toán Số Học Trên Con Trỏ

int arr[6] = {5, 6, 9, 4, 1, 2};
int *p = &arr[2];    // p trỏ đến arr[2]

p + 1;               // Trỏ đến arr[3]
p - 1;               // Trỏ đến arr[1]
p + 2;               // Trỏ đến arr[4]

int *p1 = &arr[1];
int *p2 = &arr[5];
p2 - p1;             // 4 (khoảng cách 4 phần tử)

Các phép toán cho phép:

  • Cộng/trừ số nguyên: p + n, p - n
  • So sánh: p1 == p2, p1 < p2, p1 >= p2
  • Tăng/giảm: p++, p--, p += n, p -= n
  • Hiệu hai con trỏ: p2 - p1

Các phép toán KHÔNG cho phép:

  • Cộng hai con trỏ: p1 + p2
  • Nhân/chia: p * n, p / n

Duyệt Mảng Qua Con Trỏ

int arr[6] = {5, 6, 9, 4, 1, 2};
int *p = arr;

// Cách 1: Dùng chỉ số
for (int i = 0; i < 6; i++) {
    cout << *(p + i) << " ";
}

// Cách 2: Tăng con trỏ
for (int i = 0; i < 6; i++) {
    cout << *p << " ";
    p++;
}

// Cách 3: Dùng con trỏ kết thúc
int *end = arr + 6;
for (int *p = arr; p < end; p++) {
    cout << *p << " ";
}

Truyền Mảng Vào Hàm

// Cách 1: Dùng mảng
void xuatMang(int arr[], int n) {
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
}

// Cách 2: Dùng con trỏ (tương đương)
void xuatMang(int *arr, int n) {
    for (int i = 0; i < n; i++) {
        cout << *(arr + i) << " ";
    }
}

// Cách 3: Tăng con trỏ
void xuatMang(int *arr, int n) {
    for (int i = 0; i < n; i++, arr++) {
        cout << *arr << " ";
    }
}

Con Trỏ và Chuỗi

char str[] = "Hello";
char *p = str;

// Duyệt chuỗi
while (*p != '\0') {
    cout << *p;
    p++;
}

// Chuyển thành chữ hoa
for (int i = 0; str[i] != '\0'; i++) {
    str[i] = toupper(str[i]);
}

// Hoặc dùng con trỏ
p = str;
while (*p) {
    *p = toupper(*p);
    p++;
}

Con Trỏ và Struct

struct DIEM {
    int x;
    int y;
};

DIEM d = {10, 20};
DIEM *p = &d;

// Cách 1: Dùng toán tử .
(*p).x = 30;
(*p).y = 40;

// Cách 2: Dùng toán tử -> (khuyến khích)
p->x = 30;
p->y = 40;

cout << p->x << ", " << p->y << endl;

Cấp Phát Động (Dynamic Memory Allocation)

Cấp Phát Tĩnh vs Động

Cấp Phát Tĩnh

int a;                // Biến tự động
int arr[100];         // Mảng tĩnh
SINHVIEN sv;          // Struct tĩnh

Nhược điểm:

  • Phải biết trước kích thước khi lập trình
  • Không thể thay đổi kích thước
  • Tốn bộ nhớ nếu cấp phát dư

Cấp Phát Động

int *p = new int;           // Cấp phát 1 số nguyên
int *arr = new int[n];      // Cấp phát mảng n phần tử (n nhập từ bàn phím)

Ưu điểm:

  • Cấp phát khi chạy chương trình
  • Kích thước linh hoạt
  • Sử dụng bộ nhớ hiệu quả hơn

Cấu Trúc Bộ Nhớ Chương Trình

┌─────────────────────┐
│   STACK             │ ← Biến cục bộ, tham số hàm
│   (LIFO)            │   Tự động quản lý
├─────────────────────┤
│                     │
│   Vùng trống        │
│                     │
├─────────────────────┤
│   HEAP              │ ← Cấp phát động
│   (RAM + VM)        │   Lập trình viên quản lý
├─────────────────────┤
│   Biến toàn cục     │ ← Biến global, static
│   & static          │
├─────────────────────┤
│   Mã chương trình   │ ← Code, hằng số
└─────────────────────┘

Toán Tử new

Cú pháp:

<kiểu> *<con_trỏ> = new <kiểu>;
<kiểu> *<con_trỏ> = new <kiểu>(<giá_trị_khởi_tạo>);
<kiểu> *<con_trỏ> = new <kiểu>[<số_phần_tử>];

Ví dụ:

// Cấp phát 1 biến
int *p = new int;
*p = 100;

// Cấp phát và khởi tạo
int *q = new int(99);
cout << *q;            // 99

// Cấp phát mảng
int n;
cout << "Nhap n: ";
cin >> n;
int *arr = new int[n];

// Kiểm tra cấp phát thành công
if (arr == NULL) {
    cout << "Khong du bo nho!" << endl;
    exit(1);
}

Toán Tử delete

Cú pháp:

delete <con_trỏ>;              // Giải phóng 1 biến
delete[] <con_trỏ>;            // Giải phóng mảng

Ví dụ:

int *p = new int;
*p = 100;
delete p;
p = NULL;              // Tránh con trỏ lạc

int *arr = new int[100];
delete[] arr;
arr = NULL;

Typedef với Con Trỏ

typedef int* IntPtr;

IntPtr p1, p2;         // Cả hai đều là con trỏ int
int *p3, p4;           // p3 là con trỏ, p4 là int thường!

Mảng Động 1 Chiều

Cấp Phát

#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "Nhap so phan tu: ";
    cin >> n;
    
    // Cấp phát mảng động
    int *arr = new int[n];
    
    // Kiểm tra
    if (arr == NULL) {
        cout << "Khong du bo nho!" << endl;
        return 1;
    }
    
    // Sử dụng như mảng thường
    for (int i = 0; i < n; i++) {
        arr[i] = i + 1;
    }
    
    // Xuất
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    
    // Giải phóng
    delete[] arr;
    arr = NULL;
    
    return 0;
}

Truyền Mảng Động Vào Hàm

// Cách 1: Hàm trả về con trỏ
int* nhapMang(int n) {
    int *p = new int[n];
    if (p == NULL) return NULL;
    
    for (int i = 0; i < n; i++) {
        cout << "Nhap phan tu " << i << ": ";
        cin >> p[i];
    }
    return p;
}

int main() {
    int n;
    cin >> n;
    
    int *arr = nhapMang(n);
    if (arr == NULL) {
        cout << "Loi cap phat!" << endl;
        return 1;
    }
    
    // Sử dụng arr...
    
    delete[] arr;
    return 0;
}

// Cách 2: Truyền tham chiếu con trỏ
void nhapMang(int *&p, int n) {
    p = new int[n];
    if (p == NULL) return;
    
    for (int i = 0; i < n; i++) {
        cout << "Nhap phan tu " << i << ": ";
        cin >> p[i];
    }
}

int main() {
    int n, *arr;
    cin >> n;
    
    nhapMang(arr, n);
    
    // Sử dụng arr...
    
    delete[] arr;
    return 0;
}

Mảng Động 2 Chiều

Cấp phát:

int m, n;
cout << "Nhap so dong, cot: ";
cin >> m >> n;

// Cấp phát mảng con trỏ
int **matrix = new int*[m];
if (matrix == NULL) {
    cout << "Loi cap phat!" << endl;
    return 1;
}

// Cấp phát từng dòng
for (int i = 0; i < m; i++) {
    matrix[i] = new int[n];
    if (matrix[i] == NULL) {
        // Giải phóng các dòng đã cấp phát
        for (int j = 0; j < i; j++) {
            delete[] matrix[j];
        }
        delete[] matrix;
        cout << "Loi cap phat!" << endl;
        return 1;
    }
}

// Sử dụng
for (int i = 0; i < m; i++) {
    for (int j = 0; j < n; j++) {
        matrix[i][j] = i * n + j;
    }
}

Giải phóng:

// Giải phóng từng dòng
for (int i = 0; i < m; i++) {
    delete[] matrix[i];
}

// Giải phóng mảng con trỏ
delete[] matrix;
matrix = NULL;

Sơ đồ:

matrix
┌────┐
│ p0 │──→ [a00][a01][a02][a03]
├────┤
│ p1 │──→ [a10][a11][a12][a13]
├────┤
│ p2 │──→ [a20][a21][a22][a23]
└────┘

Cấp Phát Động cho Struct

// Cấp phát 1 struct
SINHVIEN *sv = new SINHVIEN;
sv->mssv = "2151063";
sv->hoten = "Nguyen Van A";

delete sv;

// Cấp phát mảng struct
int n;
cin >> n;
SINHVIEN *danhsach = new SINHVIEN[n];

for (int i = 0; i < n; i++) {
    cout << "Nhap sinh vien " << i + 1 << ":\n";
    cin >> danhsach[i].mssv;
    cin.ignore();
    cin.getline(danhsach[i].hoten, 50);
}

delete[] danhsach;

Memory Leak (Rò Rỉ Bộ Nhớ)

// SAI: Memory leak
void func() {
    int *p = new int[1000];
    // Quên delete[]
}  // Vùng nhớ không được giải phóng!

// ĐÚNG:
void func() {
    int *p = new int[1000];
    // Xử lý...
    delete[] p;
    p = NULL;
}

Hậu quả:

  • Bộ nhớ bị chiếm dụng không cần thiết
  • Chương trình chạy lâu sẽ hết RAM
  • Hệ thống chậm lại

Cấp Phát Động trong C (Tham khảo)

malloc()

void* malloc(size_t size);

// Ví dụ
int *p = (int*)malloc(sizeof(int));
int *arr = (int*)malloc(10 * sizeof(int));

if (p == NULL) {
    printf("Khong du bo nho!");
}

calloc()

void* calloc(size_t num, size_t size);

// Ví dụ (tự động khởi tạo = 0)
int *arr = (int*)calloc(10, sizeof(int));

realloc()

void* realloc(void *ptr, size_t newsize);

// Ví dụ
int *arr = (int*)malloc(10 * sizeof(int));
arr = (int*)realloc(arr, 20 * sizeof(int));  // Mở rộng lên 20

free()

void free(void *ptr);

// Ví dụ
int *p = (int*)malloc(sizeof(int));
free(p);
p = NULL;

Bài Tập Thực Hành

Bài Tập Con Trỏ Cơ Bản

Bài Tập Cấp Phát Động


Tập Tin (File)

Giới Thiệu

Tại Sao Cần Tập Tin?

Giải pháp: Lưu trữ dữ liệu trên ổ cứng (HDD, SSD) dưới dạng tập tin.

Khái Niệm Tập Tin

Đặc điểm:

  • Dãy byte liên tục
  • Có tên và phần mở rộng
  • Cho phép đọc/ghi dữ liệu
  • Lưu trữ lâu dài

Phân Loại Tập Tin

Theo Nội Dung

  • Tập tin văn bản (Text file):

    • Chỉ chứa ký tự
    • Có thể đọc bằng Notepad, editor
    • VD: .txt, .cpp, .h
  • Tập tin nhị phân (Binary file):

    • Chứa dữ liệu dạng byte
    • Không đọc được bằng text editor
    • VD: .exe, .jpg, .mp3, .dat

Theo Cách Sử Dụng

  • Text stream: Xử lý theo từng dòng văn bản
  • Binary stream: Xử lý theo từng byte

Quy Tắc Đặt Tên Tập Tin

Cú pháp:

<tên_tập_tin>.<phần_mở_rộng>

Quy tắc:

  • Tên tập tin: bắt buộc, tối đa 128 ký tự
  • Ký tự cho phép: A-Z, a-z, 0-9, khoảng trắng, @#$%^()!
  • Phần mở rộng: không bắt buộc, thường 3-4 ký tự

Ví dụ:

  • dulieu.txt
  • SinhVien2024.dat
  • Bao cao 01.docx

Đường Dẫn Tập Tin

Đường dẫn tuyệt đối:

"C:\\data\\list.txt"          // Windows
"/home/user/data/list.txt"    // Linux/Mac

Đường dẫn tương đối:

"data.txt"                    // Cùng thư mục với file .exe
"input\\data.txt"             // Thư mục con input
"..\\data.txt"                // Thư mục cha

Thư Viện Xử Lý Tập Tin

#include <fstream>            // File stream

Các lớp chính:

  • ifstream: Input file stream (đọc)
  • ofstream: Output file stream (ghi)
  • fstream: File stream (đọc/ghi)

Mở và Đóng Tập Tin

Mở Tập Tin Để Đọc

#include <fstream>
using namespace std;

ifstream inFile("data.txt");

// Hoặc
ifstream inFile;
inFile.open("data.txt");

// Kiểm tra mở thành công
if (!inFile) {
    cout << "Khong mo duoc file!" << endl;
    return 1;
}

Mở Tập Tin Để Ghi

ofstream outFile("output.txt");

// Hoặc
ofstream outFile;
outFile.open("output.txt");

// Kiểm tra
if (!outFile) {
    cout << "Khong mo duoc file!" << endl;
    return 1;
}

Chế Độ Mở Tập Tin

Chế độÝ nghĩa
ios::inMở để đọc
ios::outMở để ghi (xóa nội dung cũ)
ios::appGhi tiếp vào cuối file
ios::binaryChế độ nhị phân
ios::truncXóa nội dung cũ
// Ghi tiếp vào cuối file
ofstream outFile("data.txt", ios::app);

// Mở file nhị phân để đọc
ifstream inFile("data.dat", ios::binary);

// Kết hợp nhiều chế độ
fstream file("data.txt", ios::in | ios::out);

Đóng Tập Tin

inFile.close();
outFile.close();

Ghi Dữ Liệu Vào Tập Tin

Giống như cout:

#include <fstream>
using namespace std;

int main() {
    ofstream outFile("test.txt");
    
    if (!outFile) {
        cout << "Loi mo file!" << endl;
        return 1;
    }
    
    int x = 10;
    float y = 123.23;
    
    outFile << x << "\t" << y << endl;
    outFile << "HelloCplusplus." << endl;
    
    outFile.close();
    return 0;
}

Nội dung file test.txt:

10	123.23
HelloCplusplus.

Ghi Tiếp Vào Cuối File

ofstream outFile("test.txt", ios::app);

if (!outFile) {
    cout << "Loi mo file!" << endl;
    return 1;
}

outFile << "Them dong moi" << endl;
outFile.close();

Đọc Dữ Liệu Từ Tập Tin

Đọc Theo Từng Từ/Số

Giống như cin:

#include <fstream>
using namespace std;

int main() {
    ifstream inFile("test.txt");
    
    if (!inFile) {
        cout << "Loi mo file!" << endl;
        return 1;
    }
    
    int x;
    float y;
    string str;
    
    inFile >> x >> y >> str;
    
    cout << "x = " << x << endl;
    cout << "y = " << y << endl;
    cout << "str = " << str << endl;
    
    inFile.close();
    return 0;
}

Đọc Theo Từng Ký Tự

char ch;
ifstream inFile("test.txt");

while (inFile.get(ch)) {
    cout << ch;
}

inFile.close();

Đọc Theo Từng Dòng

#include <fstream>
#include <string>
using namespace std;

int main() {
    ifstream inFile("test.txt");
    
    if (!inFile) {
        cout << "Loi mo file!" << endl;
        return 1;
    }
    
    string line;
    while (getline(inFile, line)) {
        cout << line << endl;
    }
    
    inFile.close();
    return 0;
}

Kiểm Tra Cuối File

ifstream inFile("test.txt");

while (!inFile.eof()) {
    string line;
    getline(inFile, line);
    
    if (!inFile.eof()) {  // Tránh đọc dòng cuối 2 lần
        cout << line << endl;
    }
}

Ví Dụ Thực Tế

Ví Dụ 1: Lưu và Đọc Điểm Sinh Viên

#include <fstream>
#include <iostream>
using namespace std;

struct SINHVIEN {
    char mssv[10];
    char hoten[50];
    float diemTB;
};

// Ghi danh sách sinh viên
void ghiFile(SINHVIEN ds[], int n) {
    ofstream outFile("sinhvien.txt");
    
    if (!outFile) {
        cout << "Loi mo file!" << endl;
        return;
    }
    
    outFile << n << endl;
    for (int i = 0; i < n; i++) {
        outFile << ds[i].mssv << endl;
        outFile << ds[i].hoten << endl;
        outFile << ds[i].diemTB << endl;
    }
    
    outFile.close();
    cout << "Da ghi file thanh cong!" << endl;
}

// Đọc danh sách sinh viên
int docFile(SINHVIEN ds[]) {
    ifstream inFile("sinhvien.txt");
    
    if (!inFile) {
        cout << "Loi mo file!" << endl;
        return 0;
    }
    
    int n;
    inFile >> n;
    inFile.ignore();  // Bỏ qua ký tự xuống dòng
    
    for (int i = 0; i < n; i++) {
        inFile.getline(ds[i].mssv, 10);
        inFile.getline(ds[i].hoten, 50);
        inFile >> ds[i].diemTB;
        inFile.ignore();
    }
    
    inFile.close();
    return n;
}

Ví Dụ 2: Tính Trung Bình Từ File

Cho file trungbinh.txt có nội dung:

46	56	12
12	34	56
45	78	90

Tính trung bình mỗi cột, ghi kết quả vào cuối file:

#include <fstream>
#include <sstream>
#include <string>
using namespace std;

int main() {
    ifstream inFile("trungbinh.txt");
    
    if (!inFile) {
        cout << "Loi mo file!" << endl;
        return 1;
    }
    
    float s1 = 0, s2 = 0, s3 = 0;
    int count = 0;
    
    string line;
    while (getline(inFile, line)) {
        int col1, col2, col3;
        istringstream iss(line);
        
        if (iss >> col1 >> col2 >> col3) {
            s1 += col1;
            s2 += col2;
            s3 += col3;
            count++;
        }
    }
    inFile.close();
    
    // Tính trung bình
    s1 /= count;
    s2 /= count;
    s3 /= count;
    
    // Làm tròn 2 chữ số
    s1 = (int)(s1 * 100) / 100.0;
    s2 = (int)(s2 * 100) / 100.0;
    s3 = (int)(s3 * 100) / 100.0;
    
    // Ghi vào cuối file
    ofstream outFile("trungbinh.txt", ios::app);
    outFile << s1 << "\t" << s2 << "\t" << s3 << endl;
    outFile.close();
    
    cout << "Hoan thanh!" << endl;
    return 0;
}

Ví Dụ 3: Sao Chép File

#include <fstream>
using namespace std;

int main() {
    ifstream inFile("nguon.txt");
    ofstream outFile("dich.txt");
    
    if (!inFile || !outFile) {
        cout << "Loi mo file!" << endl;
        return 1;
    }
    
    char ch;
    while (inFile.get(ch)) {
        outFile.put(ch);
    }
    
    inFile.close();
    outFile.close();
    
    cout << "Sao chep thanh cong!" << endl;
    return 0;
}

Xử Lý File Nhị Phân

Ghi File Nhị Phân

#include <fstream>
using namespace std;

struct SINHVIEN {
    char mssv[10];
    char hoten[50];
    float diemTB;
};

int main() {
    ofstream outFile("sinhvien.dat", ios::binary);
    
    if (!outFile) {
        cout << "Loi mo file!" << endl;
        return 1;
    }
    
    SINHVIEN sv = {"2151063", "Nguyen Van A", 8.5};
    
    outFile.write((char*)&sv, sizeof(SINHVIEN));
    
    outFile.close();
    return 0;
}

Đọc File Nhị Phân

ifstream inFile("sinhvien.dat", ios::binary);

if (!inFile) {
    cout << "Loi mo file!" << endl;
    return 1;
}

SINHVIEN sv;
inFile.read((char*)&sv, sizeof(SINHVIEN));

cout << "MSSV: " << sv.mssv << endl;
cout << "Ho ten: " << sv.hoten << endl;
cout << "Diem TB: " << sv.diemTB << endl;

inFile.close();

Bài Tập Thực Hành

Bài Tập File


Tổng Kết