Khảo sát Cấu trúc Mảng
Phần này tập trung vào việc khám phá cách các mảng, cả một chiều và hai chiều, được lưu trữ trong bộ nhớ và cách truy cập các phần tử của chúng trong ngôn ngữ C.
Mảng một chiều
Trong C, các phần tử của mảng một chiều được lưu trữ liên tục trong bộ nhớ. Ví dụ sau minh họa cách mảng số nguyên (int) và mảng ký tự (char) được tổ chức.
#include <stdio.h>
#define KICH_THUOC 4
// Hàm kiểm tra mảng số nguyên một chiều
void kiemTraMangSoNguyen() {
int mangSoNguyen[KICH_THUOC] = {10, 20, 30, 40};
int chiSo;
printf("Kich thuoc mang so nguyen (bytes): %zu\n", sizeof(mangSoNguyen));
printf("Dia chi va gia tri cua tung phan tu:\n");
for (chiSo = 0; chiSo < KICH_THUOC; ++chiSo)
printf("Dia chi %p: %d\n", &mangSoNguyen[chiSo], mangSoNguyen[chiSo]);
printf("Gia tri cua ten mang (dia chi phan tu dau tien): %p\n", (void*)mangSoNguyen);
}
// Hàm kiểm tra mảng ký tự một chiều
void kiemTraMangKyTu() {
char mangKyTu[KICH_THUOC] = {'A', 'B', 'C', 'D'};
int chiSo;
printf("Kich thuoc mang ky tu (bytes): %zu\n", sizeof(mangKyTu));
printf("Dia chi va gia tri cua tung phan tu:\n");
for (chiSo = 0; chiSo < KICH_THUOC; ++chiSo)
printf("Dia chi %p: %c\n", &mangKyTu[chiSo], mangKyTu[chiSo]);
printf("Gia tri cua ten mang (dia chi phan tu dau tien): %p\n", (void*)mangKyTu);
}
int main() {
printf("--- Kiem tra mang so nguyen mot chieu ---\n");
kiemTraMangSoNguyen();
printf("\n--- Kiem tra mang ky tu mot chieu ---\n");
kiemTraMangKyTu();
return 0;
}
Quan sát đầu ra, ta thấy các phần tử trong mảng số nguyên được lưu trữ liên tục, mỗi phần tử chiếm 4 byte (trên hệ thống 32-bit/64-bit phổ biến). Tương tự, các phần tử trong mảng ký tự cũng liên tục, mỗi phần tử chiếm 1 byte. Tên mảng (ví dụ: mangSoNguyen) tự bản thân nó là một con trỏ trỏ đến địa chỉ của phần tử đầu tiên (&mangSoNguyen[0]).
Mảng hai chiều
Mảng hai chiều trong C được lưu trữ theo cơ chế "hàng lớn" (row-major order), nghĩa là tất cả các phần tử của hàng đầu tiên được lưu trữ trước, sau đó đến hàng thứ hai, và cứ thế. Điều này tạo ra một khối bộ nhớ liên tục cho toàn bộ mảng.
#include <stdio.h>
#define SO_HANG 2
#define SO_COT 4
// Hàm kiểm tra mảng số nguyên hai chiều
void kiemTraMangSoNguyenHaiChieu() {
int maTranSo[SO_HANG][SO_COT] = {{11, 12, 13, 14}, {21, 22, 23, 24}};
int hang, cot;
printf("Kich thuoc ma tran so nguyen (bytes): %zu\n", sizeof(maTranSo));
printf("Dia chi va gia tri cua tung phan tu:\n");
for (hang = 0; hang < SO_HANG; ++hang)
for (cot = 0; cot < SO_COT; ++cot)
printf("Dia chi %p: %d\n", &maTranSo[hang][cot], maTranSo[hang][cot]);
printf("\n");
printf("Gia tri cua ten ma tran (dia chi cua hang dau tien): %p\n", (void*)maTranSo);
printf("Gia tri cua maTranSo[0] (dia chi cua phan tu dau tien hang 0): %p\n", (void*)maTranSo[0]);
printf("Gia tri cua maTranSo[1] (dia chi cua phan tu dau tien hang 1): %p\n", (void*)maTranSo[1]);
printf("\n");
}
// Hàm kiểm tra mảng ký tự hai chiều
void kiemTraMangKyTuHaiChieu() {
char maTranKyTu[SO_HANG][SO_COT] = {{'A', 'B', 'C', 'D'}, {'E', 'F', 'G', 'H'}};
int hang, cot;
printf("Kich thuoc ma tran ky tu (bytes): %zu\n", sizeof(maTranKyTu));
printf("Dia chi va gia tri cua tung phan tu:\n");
for (hang = 0; hang < SO_HANG; ++hang)
for (cot = 0; cot < SO_COT; ++cot)
printf("Dia chi %p: %c\n", &maTranKyTu[hang][cot], maTranKyTu[hang][cot]);
printf("\n");
printf("Gia tri cua ten maTranKyTu: %p\n", (void*)maTranKyTu);
printf("Gia tri cua maTranKyTu[0]: %p\n", (void*)maTranKyTu[0]);
printf("Gia tri cua maTranKyTu[1]: %p\n", (void*)maTranKyTu[1]);
}
int main() {
printf("--- Kiem tra ma tran so nguyen hai chieu ---\n");
kiemTraMangSoNguyenHaiChieu();
printf("\n--- Kiem tra ma tran ky tu hai chieu ---\n");
kiemTraMangKyTuHaiChieu();
return 0;
}
Với mảng hai chiều, địa chỉ của tên mảng (ví dụ: maTranSo), địa chỉ của hàng đầu tiên (maTranSo[0]), và địa chỉ của phần tử đầu tiên của hàng đầu tiên (&maTranSo[0][0]) có cùng giá trị số học. Tuy nhiên, chúng có các kiểu dữ liệu khác nhau (con trỏ đến mảng, con trỏ đến phần tử). Sự khác biệt về địa chỉ giữa các hàng liên tiếp (ví dụ: maTranSo[0] và maTranSo[1]) chính là kích thước của một hàng (số phần tử trên một hàng nhân với kích thước của một phần tử).
Thao tác Chuỗi trong C
Các chuỗi trong C được biểu diễn bằng mảng ký tự kết thúc bằng ký tự null ('\0'). Phần này sẽ trình bày cách hoán đổi hai chuỗi và các thao tác phân tích chuỗi.
Hoán đổi chuỗi
Để hoán đổi nội dung của hai chuỗi, chúng ta không thể sử dụng phép gán trực tiếp (ví dụ: str1 = str2;) vì điều này sẽ chỉ thay đổi con trỏ mà không sao chép dữ liệu. Thay vào đó, chúng ta phải sao chép nội dung của chuỗi bằng các hàm như strcpy.
#include <stdio.h>
#include <string.h> // Cần cho strcpy
#define MAX_CHIEU_DAI_CHUOI 80
// Hàm hoán đổi nội dung của hai chuỗi
void thucHienHoanDoiChuoi(char chuoiA[], char chuoiB[]) {
char chuoiTam[MAX_CHIEU_DAI_CHUOI];
// Sao chép chuỗiA vào chuoiTam
strcpy(chuoiTam, chuoiA);
// Sao chép chuỗiB vào chuoiA
strcpy(chuoiA, chuoiB);
// Sao chép chuoiTam (nội dung gốc của chuỗiA) vào chuoiB
strcpy(chuoiB, chuoiTam);
}
// Kiểm tra hoán đổi chuỗi với hai mảng ký tự riêng biệt
void kiemTraHoanDoiBangHaiMang() {
char chuoiThuNhat[MAX_CHIEU_DAI_CHUOI] = "Lap trinh C la thu vi.";
char chuoiThuHai[MAX_CHIEU_DAI_CHUOI] = "Lap trinh C la mot thu thach.";
printf("Truoc khi hoan doi:\n");
puts(chuoiThuNhat);
puts(chuoiThuHai);
thucHienHoanDoiChuoi(chuoiThuNhat, chuoiThuHai);
printf("Sau khi hoan doi:\n");
puts(chuoiThuNhat);
puts(chuoiThuHai);
}
// Kiểm tra hoán đổi chuỗi trong mảng hai chiều (mảng các chuỗi)
void kiemTraHoanDoiTrongMangHaiChieu() {
char tapHopChuoi[2][MAX_CHIEU_DAI_CHUOI] = {"Hoc C that tuyet!",
"Cung co luc nan long :("};
printf("Truoc khi hoan doi:\n");
puts(tapHopChuoi[0]);
puts(tapHopChuoi[1]);
// Gọi hàm hoán đổi, truyền địa chỉ của từng chuỗi con
thucHienHoanDoiChuoi(tapHopChuoi[0], tapHopChuoi[1]);
printf("Sau khi hoan doi:\n");
puts(tapHopChuoi[0]);
puts(tapHopChuoi[1]);
}
int main() {
printf("--- Hoan doi hai chuoi bang hai mang mot chieu ---\n");
kiemTraHoanDoiBangHaiMang();
printf("\n--- Hoan doi hai chuoi bang mang ky tu hai chieu ---\n");
kiemTraHoanDoiTrongMangHaiChieu();
return 0;
}
Khi làm việc với các chuỗi trong mảng hai chiều (hay còn gọi là mảng các chuỗi), mỗi hàng views[X] thực chất là một mảng ký tự một chiều đại diện cho một chuỗi. Do đó, chúng ta có thể truyền views[0] và views[1] trực tiếp vào hàm hoán đổi chuỗi.
Đếm số từ trong chuỗi
Thuật toán đếm số từ thường dựa trên việc quét chuỗi và xác định ranh giới giữa các từ (thường là khoảng trắng).
#include <stdio.h>
#include <string.h> // Để sử dụng strlen
#define KICH_THUOC_DONG 81 // +1 cho ky tu null
// Hàm đếm số từ trong một chuỗi
int demSoTu(char dongVanBan[]) {
int chiSo = 0;
int dangTrongTu = 0; // 0: không trong từ, 1: đang trong từ
int soLuongTu = 0;
while (dongVanBan[chiSo] != '\0') {
if (dongVanBan[chiSo] == ' ' || dongVanBan[chiSo] == '\t' || dongVanBan[chiSo] == '\n') {
dangTrongTu = 0; // Gặp khoảng trắng, không còn trong từ nữa
} else if (dangTrongTu == 0) {
dangTrongTu = 1; // Bắt đầu một từ mới
soLuongTu++;
}
chiSo++;
}
return soLuongTu;
}
int main() {
char dongNguon[KICH_THUOC_DONG];
printf("Nhap van ban (nhan EOF de thoat):\n");
while (fgets(dongNguon, KICH_THUOC_DONG, stdin) != NULL) {
// Xóa ký tự xuống dòng nếu có
dongNguon[strcspn(dongNguon, "\n")] = '\0';
printf("So tu: %d\n\n", demSoTu(dongNguon));
printf("Nhap van ban (nhan EOF de thoat):\n");
}
return 0;
}
Trong ví dụ này, chúng ta sử dụng một cờ dangTrongTu để theo dõi xem chúng ta đang ở trong một từ hay giữa các từ. Mỗi khi chuyển từ trạng thái "không trong từ" sang "trong từ", chúng ta tăng bộ đếm từ lên.
Tìm từ dài nhất trong chuỗi
Để tìm từ dài nhất, chúng ta cần duyệt qua chuỗi, xác định từng từ, đếm độ dài của nó và so sánh với độ dài từ dài nhất đã tìm được.
#include <stdio.h>
#include <string.h> // Cần cho strlen
#define KICH_THUOC_TOI_DA 1000
int main() {
char dongVao[KICH_THUOC_TOI_DA];
int doDaiHienTai; // Độ dài của từ đang xét
int doDaiLonNhat; // Độ dài của từ dài nhất
int viTriBatDauTuDaiNhat; // Vị trí bắt đầu của từ dài nhất
printf("Nhap van ban (nhan EOF de thoat):\n");
while (fgets(dongVao, KICH_THUOC_TOI_DA, stdin) != NULL) {
dongVao[strcspn(dongVao, "\n")] = '\0'; // Xóa ký tự xuống dòng
doDaiHienTai = 0;
doDaiLonNhat = 0;
viTriBatDauTuDaiNhat = 0;
int chiSo = 0;
int batDauTu = 0; // Ghi nhớ vị trí bắt đầu của từ hiện tại
while (1) {
// Bỏ qua các khoảng trắng đầu tiên
while (dongVao[chiSo] == ' ' || dongVao[chiSo] == '\t') {
chiSo++;
}
batDauTu = chiSo; // Đánh dấu vị trí bắt đầu của từ mới
doDaiHienTai = 0;
// Đếm độ dài từ hiện tại
while (dongVao[chiSo] != '\0' && dongVao[chiSo] != ' ' && dongVao[chiSo] != '\t') {
doDaiHienTai++;
chiSo++;
}
// Cập nhật từ dài nhất nếu tìm thấy từ dài hơn
if (doDaiHienTai > doDaiLonNhat) {
doDaiLonNhat = doDaiHienTai;
viTriBatDauTuDaiNhat = batDauTu;
}
// Thoát nếu đã đến cuối chuỗi
if (dongVao[chiSo] == '\0')
break;
}
printf("Tu dai nhat: ");
if (doDaiLonNhat > 0) {
for (int k = 0; k < doDaiLonNhat; ++k)
printf("%c", dongVao[viTriBatDauTuDaiNhat + k]);
} else {
printf("(khong co tu nao)");
}
printf("\n\n");
}
return 0;
}
Chương trình này quét qua chuỗi, sử dụng hai vòng lặp lồng nhau để xác định và đo độ dài từng từ. Biến viTriBatDauTuDaiNhat được dùng để lưu trữ vị trí bắt đầu của từ dài nhất, giúp chúng ta in ra từ đó sau khi quét xong.
Chuyển đổi số thập phân sang hệ cơ số N
Việc chuyển đổi số thập phân sang một hệ cơ số khác (nhị phân, bát phân, thập lục phân) được thực hiện bằng cách chia liên tiếp số đó cho cơ số và ghi lại các số dư theo thứ tự ngược lại.
#include <stdio.h>
#include <string.h> // Cần cho strlen
#define MAX_CHIEU_DAI_KET_QUA 100
// Hàm chuyển đổi số thập phân x sang hệ cơ số n
void chuyenDoiCoSo(int soThapPhan, int coSoDich) {
char bangChuyenDoi[] = "0123456789ABCDEF"; // Bảng ký tự cho các cơ số
char ketQuaDaoNguoc[MAX_CHIEU_DAI_KET_QUA]; // Lưu kết quả theo thứ tự đảo ngược
int chiSo = 0;
int phanNguyen;
int soDu;
if (soThapPhan == 0) {
printf("0\n");
return;
}
// Thực hiện thuật toán chia lấy dư
while (soThapPhan > 0) {
soDu = soThapPhan % coSoDich;
ketQuaDaoNguoc[chiSo++] = bangChuyenDoi[soDu];
soThapPhan = soThapPhan / coSoDich;
}
// In kết quả theo thứ tự đúng (ngược lại của chuỗi đã lưu)
for (int i = chiSo - 1; i >= 0; i--) {
printf("%c", ketQuaDaoNguoc[i]);
}
printf("\n");
}
int main() {
int giaTriThapPhan;
printf("Nhap mot so nguyen thap phan (nhan Ctrl+D/Z de thoat):\n");
while (scanf("%d", &giaTriThapPhan) != EOF) {
printf("Thap phan %d:\n", giaTriThapPhan);
printf(" Nhi phan: ");
chuyenDoiCoSo(giaTriThapPhan, 2);
printf(" Bat phan: ");
chuyenDoiCoSo(giaTriThapPhan, 8);
printf(" Thap luc phan: ");
chuyenDoiCoSo(giaTriThapPhan, 16);
printf("\nNhap mot so nguyen thap phan:\n");
}
return 0;
}
Xử lý Mảng và Sắp xếp
Phần này trình bày các thao tác cơ bản với mảng số nguyên như nhập, xuất, tính trung bình và sắp xếp.
Quản lý điểm số
Chúng ta sẽ xây dựng các hàm để nhập điểm, hiển thị điểm, tính điểm trung bình và sắp xếp điểm theo thứ tự giảm dần sử dụng thuật toán sắp xếp nổi bọt (Bubble Sort).
#include <stdio.h>
#define SO_MON_HOC 5
// Khai báo nguyên mẫu hàm
void nhapDiemSo(int diem[], int soLuong);
void hienThiDiemSo(int diem[], int soLuong);
double tinhDiemTrungBinh(int diem[], int soLuong);
void sapXepNoiBot(int diem[], int soLuong); // Sắp xếp giảm dần
int main() {
int mangDiem[SO_MON_HOC];
double diemTrungBinh;
printf("Nhap %d diem cho cac mon hoc:\n", SO_MON_HOC);
nhapDiemSo(mangDiem, SO_MON_HOC);
printf("\nCac diem so da nhap: \n");
hienThiDiemSo(mangDiem, SO_MON_HOC);
printf("\nThuc hien tinh toan va sap xep...\n");
diemTrungBinh = tinhDiemTrungBinh(mangDiem, SO_MON_HOC);
sapXepNoiBot(mangDiem, SO_MON_HOC); // Sắp xếp tại chỗ
printf("\nCac diem so (cao -> thap):\n");
hienThiDiemSo(mangDiem, SO_MON_HOC);
printf("Diem trung binh cac mon: %.2f\n", diemTrungBinh);
return 0;
}
// Định nghĩa hàm
// Nhập n số nguyên vào mảng x
void nhapDiemSo(int diem[], int soLuong) {
for (int i = 0; i < soLuong; ++i) {
printf("Nhap diem mon %d: ", i + 1);
scanf("%d", &diem[i]);
}
}
// Xuất n phần tử của mảng x
void hienThiDiemSo(int diem[], int soLuong) {
for (int i = 0; i < soLuong; ++i)
printf("%d ", diem[i]);
printf("\n");
}
// Tính điểm trung bình của các phần tử trong mảng x
double tinhDiemTrungBinh(int diem[], int soLuong) {
double tongDiem = 0.0;
for (int i = 0; i < soLuong; ++i)
tongDiem += diem[i];
return tongDiem / soLuong;
}
// Sắp xếp mảng x theo thứ tự giảm dần bằng thuật toán nổi bọt
void sapXepNoiBot(int diem[], int soLuong) {
int i, j, temp;
for (i = 0; i < soLuong - 1; i++) { // Duyệt qua từng lượt
for (j = 0; j < soLuong - 1 - i; j++) { // So sánh các cặp kề nhau
if (diem[j] < diem[j+1]) { // Nếu phần tử hiện tại nhỏ hơn phần tử kế tiếp
temp = diem[j];
diem[j] = diem[j+1];
diem[j+1] = temp;
}
}
}
}
Sắp xếp mảng chuỗi
Sắp xếp mảng chuỗi tương tự như sắp xếp mảng số nguyên, nhưng cần sử dụng các hàm xử lý chuỗi như strcmp để so sánh và strcpy để sao chép chuỗi.
#include <stdio.h>
#include <string.h> // Cần cho strcmp và strcpy
#define SO_LUONG_TEN 5
#define CHIEU_DAI_TEN_TOI_DA 20
// Khai báo nguyên mẫu hàm
void hienThiDanhSach(char danhSach[][CHIEU_DAI_TEN_TOI_DA], int soLuong);
void sapXepTenTheoThuTuABC(char danhSach[][CHIEU_DAI_TEN_TOI_DA], int soLuong);
int main() {
char danhSachNguoi[][CHIEU_DAI_TEN_TOI_DA] = {"An", "Binh", "Cuong", "Dat", "Em"}; // Ví dụ tên tiếng Việt
printf("Danh sach ten ban dau:\n");
hienThiDanhSach(danhSachNguoi, SO_LUONG_TEN);
printf("\nDang sap xep...\n");
sapXepTenTheoThuTuABC(danhSachNguoi, SO_LUONG_TEN); // Gọi hàm sắp xếp
printf("\nDanh sach ten sau khi sap xep theo thu tu bang chu cai:\n");
hienThiDanhSach(danhSachNguoi, SO_LUONG_TEN);
return 0;
}
// Định nghĩa hàm
// Xuất các chuỗi trong mảng str
void hienThiDanhSach(char danhSach[][CHIEU_DAI_TEN_TOI_DA], int soLuong) {
for (int i = 0; i < soLuong; ++i)
printf("%s\n", danhSach[i]);
}
// Sắp xếp mảng chuỗi str theo thứ tự từ điển bằng thuật toán nổi bọt
void sapXepTenTheoThuTuABC(char danhSach[][CHIEU_DAI_TEN_TOI_DA], int soLuong) {
int i, j;
char chuoiTam[CHIEU_DAI_TEN_TOI_DA]; // Mảng tạm để lưu chuỗi khi hoán đổi
for (i = 0; i < soLuong - 1; i++) {
for (j = 0; j < soLuong - 1 - i; j++) {
// So sánh hai chuỗi kề nhau. strcmp trả về > 0 nếu chuỗi đầu lớn hơn (theo thứ tự từ điển)
if (strcmp(danhSach[j], danhSach[j+1]) > 0) {
// Hoán đổi chuỗi bằng cách sao chép nội dung
strcpy(chuoiTam, danhSach[j]);
strcpy(danhSach[j], danhSach[j+1]);
strcpy(danhSach[j+1], chuoiTam);
}
}
}
}
Kiểm tra tính duy nhất của chữ số
Bài toán này yêu cầu kiểm tra xem một chuỗi số có chứa các chữ số lặp lại hay không. Chúng ta có thể sử dụng một mảng tần suất để theo dõi số lần xuất hiện của mỗi chữ số.
#include <stdio.h>
#include <string.h> // Cần cho strlen
#define MAX_CHUOI_SO 105
// Hàm kiểm tra xem một chuỗi số có chứa chữ số lặp lại không
// Trả về 1 nếu có, 0 nếu không
int coSoLapLai(char chuoiSo[]) {
int demChuSo[10] = {0}; // Mảng tần suất cho các chữ số từ 0 đến 9
int chiSo;
for (chiSo = 0; chuoiSo[chiSo] != '\0'; ++chiSo) {
int chuSoHienTai = chuoiSo[chiSo] - '0'; // Chuyển ký tự số thành giá trị số nguyên
demChuSo[chuSoHienTai]++; // Tăng bộ đếm cho chữ số đó
// Nếu số lần xuất hiện của chữ số này lớn hơn 1, nghĩa là nó bị lặp lại
if (demChuSo[chuSoHienTai] > 1)
return 1; // Có chữ số lặp lại
}
return 0; // Không có chữ số nào lặp lại
}
int main() {
char soNguyenDangChuoi[MAX_CHUOI_SO];
printf("Nhap mot chuoi so nguyen (nhan Ctrl+D/Z de thoat):\n");
while (scanf("%s", soNguyenDangChuoi) != EOF) {
if (coSoLapLai(soNguyenDangChuoi))
printf("CO\n"); // Có chữ số lặp lại
else
printf("KHONG\n"); // Không có chữ số lặp lại
}
return 0;
}
Thao tác Ma trận: Xoay hàng
Phần này minh họa cách xoay các phần tử trong mỗi hàng của một ma trận vuông sang phải một vị trí.
#include <stdio.h>
#define KICH_THUOC_MA_TRAN 4 // Ma trận 4x4
#define MAX_HANG_COT 100 // Kích thước tối đa cho mảng để hàm có thể tổng quát hơn
// Hàm hiển thị ma trận
void hienThiMaTran(int maTran[][MAX_HANG_COT], int kichThuoc) {
int hang, cot;
for (hang = 0; hang < kichThuoc; ++hang) {
for (cot = 0; cot < kichThuoc; ++cot)
printf("%4d", maTran[hang][cot]);
printf("\n");
}
}
// Hàm xoay mỗi hàng của ma trận sang phải một vị trí
void xoayMaTranSangPhai(int maTran[][MAX_HANG_COT], int kichThuoc) {
int hang, cot;
for (hang = 0; hang < kichThuoc; ++hang) { // Duyệt qua từng hàng
int giaTriCuoiHang = maTran[hang][kichThuoc - 1]; // Lưu phần tử cuối cùng của hàng hiện tại
// Dịch chuyển tất cả các phần tử trong hàng hiện tại sang phải một vị trí
for (cot = kichThuoc - 1; cot > 0; --cot) {
maTran[hang][cot] = maTran[hang][cot - 1];
}
maTran[hang][0] = giaTriCuoiHang; // Đặt phần tử đã lưu vào vị trí đầu tiên của hàng
}
}
int main() {
int maTranBanDau[KICH_THUOC_MA_TRAN][MAX_HANG_COT] = {{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16}};
printf("Ma tran goc:\n");
hienThiMaTran(maTranBanDau, KICH_THUOC_MA_TRAN);
xoayMaTranSangPhai(maTranBanDau, KICH_THUOC_MA_TRAN);
printf("\nMa tran sau khi xoay:\n");
hienThiMaTran(maTranBanDau, KICH_THUOC_MA_TRAN);
return 0;
}