1. Giới thiệu
Lý do xử lý mảng làm đối số trong ngôn ngữ C
Khi lập trình bằng ngôn ngữ C, bạn sẽ thường gặp tình huống muốn sử dụng mảng trong các hàm khác. Ví dụ, các thao tác như xử lý toàn bộ tập dữ liệu hoặc tìm kiếm, sắp xếp một lượng lớn dữ liệu sẽ trở nên hiệu quả hơn nếu xử lý mảng trong các hàm riêng, giúp tăng khả năng tái sử dụng mã nguồn.
Bằng cách truyền mảng làm đối số, mã nguồn sẽ được mô-đun hóa và có thể tách riêng từng chức năng cho mỗi hàm. Điều này không chỉ giúp dễ dàng kiểm thử và gỡ lỗi từng hàm độc lập, mà còn nâng cao hiệu suất khi nhiều lập trình viên làm việc song song.
Bài viết này sẽ giải thích một cách dễ hiểu cho người mới bắt đầu về cách truyền mảng làm đối số trong ngôn ngữ C và các điểm cần lưu ý. Từ mảng một chiều đến mảng đa chiều, chúng tôi sẽ cung cấp kiến thức thực tiễn kèm theo các ví dụ mã nguồn cụ thể.
Đối tượng độc giả
Bài viết này dành cho cả người mới bắt đầu học C và những người đã nắm vững cách sử dụng cơ bản. Thông qua nội dung, bạn sẽ không chỉ học về kiến thức cơ bản của mảng và hàm, mà còn tìm hiểu về quản lý bộ nhớ mảng và con trỏ, từ đó xây dựng chương trình hiệu quả hơn.
2. Kiến thức cơ bản về mảng và con trỏ
Mối quan hệ giữa tên mảng và con trỏ
Trong ngôn ngữ C, mảng chỉ đơn giản là một vùng bộ nhớ liên tiếp, nhưng tên mảng có vai trò đặc biệt là trỏ đến địa chỉ đầu tiên của mảng. Ví dụ, khi khai báo int array[5];
, array
sẽ trỏ tới địa chỉ đầu tiên (&array[0]
) của mảng. Điều này có nghĩa là khi truyền mảng vào hàm, chỉ cần truyền tên mảng là đã truyền địa chỉ đầu tiên của mảng.
Hiểu rõ mối quan hệ này rất quan trọng vì tên mảng và con trỏ trong ngôn ngữ C gắn bó mật thiết, đặc biệt khi bạn học về phép toán con trỏ.
Sự khác biệt giữa tên mảng và con trỏ
Mặc dù mảng và con trỏ có một số điểm chung, nhưng cũng có những khác biệt quan trọng. Chẳng hạn, kích thước mảng là cố định, trong khi con trỏ có thể thay đổi và trỏ đến bất kỳ vùng nhớ nào. Ngoài ra, mảng luôn được lưu liên tiếp trong bộ nhớ, nhưng con trỏ không nhất thiết phải trỏ đến vùng nhớ liên tiếp.
Ví dụ mã dưới đây sẽ giúp bạn hiểu rõ hơn mối quan hệ giữa mảng và con trỏ:
#include <stdio.h>
void printArray(int *arr, int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
}
int main() {
int array[5] = {1, 2, 3, 4, 5};
printArray(array, 5); // Truyền địa chỉ đầu tiên của mảng
return 0;
}
Trong ví dụ này, hàm printArray
nhận địa chỉ đầu tiên của mảng và in ra từng phần tử.
3. Cách truyền mảng một chiều làm đối số
Quy trình cơ bản để truyền địa chỉ đầu tiên của mảng
Trong C, bạn có thể truyền địa chỉ đầu tiên của mảng vào hàm để thao tác các phần tử trong mảng. Cách này không sao chép toàn bộ mảng mà chỉ truyền địa chỉ, giúp tiết kiệm bộ nhớ và tăng hiệu quả.
#include <stdio.h>
void modifyArray(int *arr, int size) {
for (int i = 0; i < size; i++) {
arr[i] *= 2; // Nhân đôi giá trị từng phần tử
}
}
int main() {
int array[5] = {1, 2, 3, 4, 5};
modifyArray(array, 5);
for (int i = 0; i < 5; i++) {
printf("%d ", array[i]);
}
return 0;
}
Ở đây, hàm modifyArray
nhận địa chỉ đầu tiên của mảng array
và thay đổi giá trị các phần tử.
Cách truyền số phần tử của mảng
Khi truyền mảng vào hàm, bạn cần truyền kèm kích thước mảng. Ngôn ngữ C không có cách lấy trực tiếp độ dài mảng bên trong hàm, nên lập trình viên phải truyền thủ công giá trị này. Trong ví dụ trên, biến size
được truyền vào hàm modifyArray
để xác định số phần tử.

4. Cách truyền mảng đa chiều làm đối số
Lưu ý khi truyền mảng hai chiều
Khi truyền mảng đa chiều vào hàm trong ngôn ngữ C, bạn có thể bỏ qua số hàng nhưng không thể bỏ qua số cột. Do đó, khi khai báo tham số hàm cho mảng hai chiều, cần chỉ định số cột cụ thể.
#include <stdio.h>
void print2DArray(int arr[][3], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main() {
int array[2][3] = {{1, 2, 3}, {4, 5, 6}};
print2DArray(array, 2);
return 0;
}
Trong ví dụ này, số cột 3
được chỉ định trong khai báo tham số hàm, cho phép hàm truy cập chính xác từng phần tử của mảng.
Sử dụng mảng có độ dài thay đổi (C99 trở lên)
Từ phiên bản C99, bạn có thể sử dụng mảng có độ dài thay đổi (VLA) để khai báo kích thước mảng một cách linh hoạt khi truyền vào hàm.
#include <stdio.h>
void printFlexibleArray(int rows, int cols, int arr[rows][cols]) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main() {
int array[2][3] = {{1, 2, 3}, {4, 5, 6}};
printFlexibleArray(2, 3, array);
return 0;
}
Với VLA, bạn có thể dễ dàng thay đổi số hàng và số cột, giúp việc xử lý mảng đa chiều trong hàm trở nên linh hoạt hơn.
5. Lưu ý khi truyền mảng làm đối số
Tầm quan trọng của việc quản lý kích thước mảng
Nếu xác định sai kích thước mảng khi truyền vào hàm, bạn có thể gây lãng phí bộ nhớ hoặc thậm chí lỗi tràn bộ đệm (buffer overflow). Do đó, cần cẩn thận đặt điều kiện vòng lặp để không vượt quá kích thước mảng.
Mảng động và quản lý bộ nhớ
Khi cấp phát mảng động, hãy sử dụng malloc
hoặc free
để quản lý bộ nhớ trong vùng heap. Đặc biệt, cần giải phóng bộ nhớ sau khi sử dụng để tránh rò rỉ bộ nhớ.
Thay đổi mảng trong hàm và tác động phụ
Khi truyền mảng vào hàm, mọi thay đổi bên trong hàm sẽ ảnh hưởng ra bên ngoài. Nếu không muốn điều này, hãy sao chép mảng trước khi xử lý hoặc thiết kế hàm sao cho không làm thay đổi dữ liệu gốc.
6. Câu hỏi thường gặp (FAQ)
Làm thế nào để lấy kích thước mảng?
Trong C, bạn không thể lấy trực tiếp kích thước mảng bên trong hàm, vì vậy phải truyền kích thước mảng như một tham số. Ví dụ: void myFunction(int *arr, int size)
là cách thường dùng.
Kích thước mảng có thể tính tại nơi khai báo bằng sizeof(array) / sizeof(array[0])
. Ví dụ:
#include <stdio.h>
void printArray(int *arr, int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
}
int main() {
int array[] = {1, 2, 3, 4, 5};
int size = sizeof(array) / sizeof(array[0]); // Tính số phần tử
printArray(array, size);
return 0;
}
Vấn đề khi sử dụng mảng động
Mảng động hữu ích khi muốn tiết kiệm bộ nhớ hoặc chưa biết kích thước trước. Hãy dùng malloc
hoặc calloc
để cấp phát và free
để giải phóng.
#include <stdio.h>
#include <stdlib.h>
void fillArray(int *arr, int size) {
for (int i = 0; i < size; i++) {
arr[i] = i * 2; // Gán giá trị
}
}
int main() {
int size = 5;
int *array = (int *)malloc(size * sizeof(int));
if (array == NULL) {
printf("Cấp phát bộ nhớ thất bại.\n");
return 1;
}
fillArray(array, size);
for (int i = 0; i < size; i++) {
printf("%d ", array[i]);
}
free(array);
return 0;
}
Nên dùng mảng hay con trỏ?
Mảng thích hợp khi dữ liệu có kích thước cố định. Con trỏ phù hợp khi cần quản lý bộ nhớ động hoặc dữ liệu có độ dài thay đổi. Với cấu trúc dữ liệu lớn hoặc yêu cầu linh hoạt, con trỏ là lựa chọn tối ưu.
7. Kết luận
Tóm tắt các điểm chính khi truyền mảng làm đối số
Bài viết đã trình bày cách truyền mảng trong C từ mảng một chiều đến đa chiều, kèm ví dụ thực tế. Truyền mảng giúp mã nguồn mô-đun hóa và tái sử dụng tốt hơn, nhưng với mảng đa chiều và mảng động, cần nắm vững con trỏ và quản lý bộ nhớ.
Quản lý đúng kích thước mảng và luôn giải phóng bộ nhớ khi dùng mảng động sẽ giúp cải thiện hiệu suất và chất lượng chương trình C của bạn.