Con trỏ trong C: Chìa khóa quản lý bộ nhớ và tối ưu hiệu suất
Khám phá sức mạnh của con trỏ trong C: quản lý bộ nhớ động, truyền tham chiếu, con trỏ hàm và các kỹ thuật tối ưu hiệu suất.
•19 tháng 10, 2025

Tóm tắt kiến thức
Con trỏ là khái niệm mạnh mẽ nhất trong C, cho phép trực tiếp thao tác với bộ nhớ và tạo ra các chương trình hiệu quả. Hiểu rõ con trỏ là chìa khóa để làm chủ C và viết code chuyên nghiệp.
Con trỏ là một trong những khái niệm quan trọng và mạnh mẽ nhất trong ngôn ngữ lập trình C. Con trỏ cho phép trực tiếp truy cập và thao tác với bộ nhớ, giúp tạo ra các chương trình hiệu quả, linh hoạt và tối ưu về mặt hiệu suất.
Quảng cáo giúp chúng tôi duy trì trang web này
Tổng quan về con trỏ
Khái niệm con trỏ
Con trỏ là một biến lưu trữ địa chỉ của biến khác trong bộ nhớ. Con trỏ cho phép truy cập gián tiếp đến dữ liệu thông qua địa chỉ của nó.
Cú pháp khai báo con trỏ
kiểu_dữ_liệu *tên_con_trỏ;Ví dụ thực tế:
#include <stdio.h>
int main() {
int number = 42;
int *ptr = &number; // ptr trỏ đến địa chỉ của number
printf("Gia tri cua number: %d\n", number);
printf("Dia chi cua number: %p\n", &number);
printf("Gia tri cua ptr (dia chi): %p\n", ptr);
printf("Gia tri tai dia chi ptr tro toi: %d\n", *ptr);
return 0;
}Tại sao con trỏ quan trọng?
- Quản lý bộ nhớ: Cấp phát và giải phóng bộ nhớ động
- Hiệu suất cao: Truyền địa chỉ thay vì sao chép dữ liệu
- Linh hoạt: Thao tác với mảng, chuỗi và cấu trúc
- Hàm trả về nhiều giá trị: Thông qua tham chiếu
Toán tử con trỏ
Toán tử & (Address of)
#include <stdio.h>
int main() {
int a = 10;
char ch = 'A';
float f = 3.14;
printf("Dia chi cua a: %p\n", &a);
printf("Dia chi cua ch: %p\n", &ch);
printf("Dia chi cua f: %p\n", &f);
return 0;
}Toán tử * (Dereference)
#include <stdio.h>
int main() {
int value = 100;
int *ptr = &value;
printf("Gia tri cua value: %d\n", value);
printf("Gia tri tai dia chi ptr: %d\n", *ptr);
// Thay đổi giá trị thông qua con trỏ
*ptr = 200;
printf("Gia tri cua value sau khi thay doi: %d\n", value);
return 0;
}Con trỏ và mảng
Mối quan hệ giữa con trỏ và mảng
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr; // ptr trỏ đến phần tử đầu tiên
printf("Mang ban dau: ");
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
printf("Truy cap qua con tro: ");
for (int i = 0; i < 5; i++) {
printf("%d ", *(ptr + i));
}
printf("\n");
printf("Truy cap qua dia chi mang: ");
for (int i = 0; i < 5; i++) {
printf("%d ", *(arr + i));
}
printf("\n");
return 0;
}Con trỏ trỏ đến mảng
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int (*ptr)[5] = &arr; // Con trỏ trỏ đến mảng 5 phần tử
printf("Gia tri cac phan tu qua con tro mang:\n");
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, (*ptr)[i]);
}
return 0;
}Con trỏ với chuỗi
#include <stdio.h>
int main() {
char str[] = "Hello World";
char *ptr = str;
printf("Chuoi ban dau: %s\n", str);
printf("Chuoi qua con tro: %s\n", ptr);
printf("Cac ky tu qua con tro:\n");
while (*ptr != '\0') {
printf("%c ", *ptr);
ptr++;
}
printf("\n");
return 0;
}Con trỏ với struct
#include <stdio.h>
#include <string.h>
typedef struct {
char name[50];
int age;
float salary;
} Employee;
int main() {
Employee emp = {"Nguyen Van A", 30, 15000000};
Employee *ptr = &emp;
// Truy cập thành phần qua con trỏ
printf("Ten: %s\n", ptr->name);
printf("Tuoi: %d\n", ptr->age);
printf("Luong: %.0f\n", ptr->salary);
// Thay đổi giá trị qua con trỏ
ptr->age = 31;
ptr->salary = 16000000;
printf("\nSau khi thay doi:\n");
printf("Tuoi: %d\n", ptr->age);
printf("Luong: %.0f\n", ptr->salary);
return 0;
}Con trỏ hàm (Function Pointer)
Khai báo và sử dụng con trỏ hàm
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int multiply(int a, int b) {
return a * b;
}
int main() {
int (*operation)(int, int); // Con trỏ hàm
int x = 10, y = 5;
// Trỏ đến hàm add
operation = add;
printf("%d + %d = %d\n", x, y, operation(x, y));
// Trỏ đến hàm subtract
operation = subtract;
printf("%d - %d = %d\n", x, y, operation(x, y));
// Trỏ đến hàm multiply
operation = multiply;
printf("%d * %d = %d\n", x, y, operation(x, y));
return 0;
}Con trỏ hàm trong mảng
#include <stdio.h>
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int divide(int a, int b) { return (b != 0) ? a / b : 0; }
int main() {
int (*operations[])(int, int) = {add, subtract, multiply, divide};
char *operationNames[] = {"Cong", "Tru", "Nhan", "Chia"};
int a = 20, b = 4;
for (int i = 0; i < 4; i++) {
printf("%s: %d %c %d = %d\n",
operationNames[i], a,
(i == 0) ? '+' : (i == 1) ? '-' : (i == 2) ? '*' : '/',
b, operations[i](a, b));
}
return 0;
}Cấp phát bộ nhớ động
malloc() - Cấp phát bộ nhớ
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("Nhap kich thuoc mang: ");
scanf("%d", &n);
// Cấp phát bộ nhớ cho mảng
int *arr = (int*)malloc(n * sizeof(int));
if (arr == NULL) {
printf("Khong the cap phat bo nho!\n");
return 1;
}
// Nhập mảng
printf("Nhap %d phan tu:\n", n);
for (int i = 0; i < n; i++) {
printf("arr[%d] = ", i);
scanf("%d", &arr[i]);
}
// Hiển thị mảng
printf("Mang vua nhap: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// Giải phóng bộ nhớ
free(arr);
return 0;
}calloc() - Cấp phát và khởi tạo về 0
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("Nhap kich thuoc mang: ");
scanf("%d", &n);
// Cấp phát và khởi tạo về 0
int *arr = (int*)calloc(n, sizeof(int));
if (arr == NULL) {
printf("Khong the cap phat bo nho!\n");
return 1;
}
printf("Mang sau khi khoi tao (calloc): ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr);
return 0;
}realloc() - Thay đổi kích thước bộ nhớ
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr = (int*)malloc(3 * sizeof(int));
if (arr == NULL) {
printf("Khong the cap phat bo nho!\n");
return 1;
}
// Khởi tạo mảng ban đầu
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
printf("Mang ban dau: ");
for (int i = 0; i < 3; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// Thay đổi kích thước thành 5 phần tử
arr = (int*)realloc(arr, 5 * sizeof(int));
if (arr == NULL) {
printf("Khong the thay doi kich thuoc!\n");
return 1;
}
// Thêm phần tử mới
arr[3] = 4;
arr[4] = 5;
printf("Mang sau khi thay doi kich thuoc: ");
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr);
return 0;
}Con trỏ với mảng động 2D
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows, cols;
printf("Nhap so hang: ");
scanf("%d", &rows);
printf("Nhap so cot: ");
scanf("%d", &cols);
// Cấp phát mảng 2D động
int **matrix = (int**)malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++) {
matrix[i] = (int*)malloc(cols * sizeof(int));
}
// Nhập ma trận
printf("Nhap ma trận %dx%d:\n", rows, cols);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("matrix[%d][%d] = ", i, j);
scanf("%d", &matrix[i][j]);
}
}
// Hiển thị ma trận
printf("Ma trận:\n");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d\t", matrix[i][j]);
}
printf("\n");
}
// Giải phóng bộ nhớ
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
return 0;
}Con trỏ cấp độ cao
Con trỏ đến con trỏ
#include <stdio.h>
int main() {
int value = 100;
int *ptr1 = &value;
int **ptr2 = &ptr1;
printf("Gia tri cua value: %d\n", value);
printf("Gia tri tai ptr1: %d\n", *ptr1);
printf("Gia tri tai ptr2: %d\n", **ptr2);
printf("Dia chi cua value: %p\n", &value);
printf("Dia chi trong ptr1: %p\n", ptr1);
printf("Dia chi trong ptr2: %p\n", *ptr2);
// Thay đổi giá trị qua con trỏ cấp 2
**ptr2 = 200;
printf("Gia tri sau khi thay doi: %d\n", value);
return 0;
}Con trỏ NULL và kiểm tra lỗi
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = NULL;
// Kiểm tra con trỏ NULL
if (ptr == NULL) {
printf("Con tro chua duoc khoi tao!\n");
}
// Cấp phát bộ nhớ
ptr = (int*)malloc(sizeof(int));
if (ptr == NULL) {
printf("Khong the cap phat bo nho!\n");
return 1;
}
*ptr = 42;
printf("Gia tri: %d\n", *ptr);
free(ptr);
ptr = NULL; // Đặt con trỏ về NULL sau khi giải phóng
return 0;
}Ví dụ thực hành
1. Hàm tìm min/max trong mảng bằng con trỏ
#include <stdio.h>
#include <stdlib.h>
void findMinMax(int *arr, int size, int *min, int *max) {
*min = *max = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] < *min) {
*min = arr[i];
}
if (arr[i] > *max) {
*max = arr[i];
}
}
}
int main() {
int n;
printf("Nhap kich thuoc mang: ");
scanf("%d", &n);
int *arr = (int*)malloc(n * sizeof(int));
printf("Nhap %d phan tu:\n", n);
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
int min, max;
findMinMax(arr, n, &min, &max);
printf("So nho nhat: %d\n", min);
printf("So lon nhat: %d\n", max);
free(arr);
return 0;
}2. Tạo mảng động có kích thước theo yêu cầu
#include <stdio.h>
#include <stdlib.h>
int* createDynamicArray(int size) {
int *arr = (int*)calloc(size, sizeof(int));
if (arr == NULL) {
printf("Khong the cap phat bo nho!\n");
return NULL;
}
return arr;
}
void fillArray(int *arr, int size) {
printf("Nhap %d phan tu:\n", size);
for (int i = 0; i < size; i++) {
printf("arr[%d] = ", i);
scanf("%d", &arr[i]);
}
}
void printArray(int *arr, int size) {
printf("Mang: ");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int size;
printf("Nhap kich thuoc mang: ");
scanf("%d", &size);
int *arr = createDynamicArray(size);
if (arr != NULL) {
fillArray(arr, size);
printArray(arr, size);
free(arr);
}
return 0;
}3. Đếm tần suất nguyên âm trong chuỗi
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
void countVowels(char *str, int *vowelCount) {
int len = strlen(str);
// Khởi tạo mảng đếm
for (int i = 0; i < 5; i++) {
vowelCount[i] = 0;
}
for (int i = 0; i < len; i++) {
char ch = tolower(str[i]);
switch (ch) {
case 'a': vowelCount[0]++; break;
case 'e': vowelCount[1]++; break;
case 'i': vowelCount[2]++; break;
case 'o': vowelCount[3]++; break;
case 'u': vowelCount[4]++; break;
}
}
}
int main() {
char str[100];
int vowelCount[5];
char vowels[] = {'a', 'e', 'i', 'o', 'u'};
printf("Nhap chuoi: ");
fgets(str, sizeof(str), stdin);
str[strcspn(str, "\n")] = 0;
countVowels(str, vowelCount);
printf("Tan suat cac nguyen am trong '%s':\n", str);
for (int i = 0; i < 5; i++) {
printf("%c: %d lan\n", vowels[i], vowelCount[i]);
}
return 0;
}Tổng kết
Con trỏ là chìa khóa để làm chủ C và viết code hiệu quả, chuyên nghiệp.
Lưu ý quan trọng về con trỏ
- Memory leak: Luôn giải phóng bộ nhớ đã cấp phát
- Dangling pointer: Không sử dụng con trỏ sau khi giải phóng
- Segmentation fault: Kiểm tra con trỏ NULL trước khi sử dụng
- Buffer overflow: Cẩn thận khi thao tác với mảng qua con trỏ
Best Practices
- Khởi tạo con trỏ với NULL
- Kiểm tra kết quả malloc/calloc trước khi sử dụng
- Sử dụng free() cho mỗi malloc/calloc
- Tránh truy cập bộ nhớ đã được giải phóng
- Sử dụng const với con trỏ khi không thay đổi dữ liệu
Với những kiến thức này, bạn đã sẵn sàng để tạo ra các chương trình C phức tạp, hiệu quả và tiếp tục khám phá các khái niệm nâng cao như cấu trúc dữ liệu và hệ thống!
Last updated on
Edit on GitHubQuản lý bộ nhớ trong C: Hướng dẫn về Stack, Heap, malloc và free
Khám phá sâu về quản lý bộ nhớ trong C: stack, heap, memory leaks, và các kỹ thuật debug memory để viết code an toàn và hiệu quả.
Bộ tiền xử lý C: Làm chủ #define, Macro và biên dịch có điều kiện
Khám phá sức mạnh của C Preprocessor: macro, conditional compilation, và các directive để tạo code linh hoạt và có thể tái sử dụng.