----> Lời khuyên cho các bạn bắt đầu học lập trình: nếu có tham khảo code trên mạng thì các bạn nên xem nó và tự viết lại rồi tìm hiểu xem từng phần trong code ý nó có mục đích là gì chứ đừng nên copy và paste một cách máy móc mà mình chả hiểu được code ý nó viết gì.
Các biến chúng ta đã biết và sửdụng trước đây đều là biến có kích thước và kiểu dữ liệu xác định. Người ta gọi các biến kiểu này là biến tĩnh. Khi khai báo biến tĩnh, một lượng ô nhớ cho các biến này sẽ được cấp phát mà không cần biết trong quá trình thực thi chương trình có sử dụng hết lượng ô nhớ này hay không. Mặt khác, các biến tĩnh dạng này sẽ tồn tại trong suốt thời gian thực thi chương trình dù có những biến mà chương trình chỉ sử dụng 1 lần rồi bỏ.
Một số hạn chế có thể gặp phải khi sử dụng các biến tĩnh:
Để tránh những hạn chế trên, ngôn ngữ C cung cấp cho ta một loại biến đặc biệt gọi là biến động với các đặc điểm sau:
Mỗi biến khi được khai báo đều được cấp phát cho 1 vùng nhớ nhất định ở những nơi (địa chỉ) khác nhau.
Biến con trỏ là biến dùng để lưu trữ địa chỉ của các biến đó.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 | #include <stdio.h> int main() { /* khai bao bien x va bien con tro px */ int x, *px; px = &x; /* &x : tra ve dia chi cua bien x. px = &x : gan dia chi cua bien x cho px hay px tro den x */ x = 42; printf("Vi tri cua bien x la %p \n", &x); printf("Noi dung cua bien x la %d \n", x); printf("Vi tri cua bien x la %p \n", px); printf("Noi dung cua bien x la %d \n", *px); *px = 7826; printf("\n -------- \n\n"); printf("Noi dung cua bien x la %d \n", x); printf("Noi dung cua bien x la %d \n", *px); return 0; } |
Kết quả:
Vi tri cua bien x la 0xbff327e4
Noi dung cua bien x la 42
Vi tri cua bien x la 0xbff327e4
Noi dung cua bien x la 42——–
Noi dung cua bien x la 7826
Noi dung cua bien x la 7826
Qua ví dụ này ta có thể rút ra một số điểm sau:
Với mỗi kiểu dữ liệu ta có tương ứng một biến con trỏ có kiểu đó.
Kiểu * Tên biến con trỏ;
Trong VD trên ta khai báo 1 biến con trỏ px thuộc kiểu int.
Ta dùng toán tử & để lấy địa chỉ của 1 biến và sau đó gán địa chỉ đó cho biến con trỏ.
Tên con trỏ = &biến;
Với con trỏ px bên trên ta có 2 phép tuy xuất là:
Trong VD trên ta có thể thấy sau phép gán px = &x ; thì việc ta viết:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include <stdio.h> int main() { /* khai bao bien x va 2 bien con tro px, qx */ int x, *px, *qx; px = &x; printf("Nhap gia tri cho vung nho px tro toi: "); scanf("%d", px); /* px la con tro nen khong viet scanf("%d", &px); */ qx = px; /* gan gia tri cua px cho qx, qx cun tro toi x*/ printf("Vi tri cua bien x la %p \n", &x); printf("Vi tri cua bien x la %p \n", px); printf("Vi tri cua bien x la %p \n", qx); printf("Noi dung cua bien x la %d \n", x); printf("Noi dung cua bien x la %d \n", *px); printf("Noi dung cua bien x la %d \n", *qx); // tang gia tri cua o nho len, <=> x = x + 7826 *px += 7826; printf("Noi dung cua bien x la %d \n", x); px++; /* cong them mot don vi cho px => px tro toi vung nho tiep theo */ printf("Vi tri px tro toi la %p \n", px); return 0; } |
Kết quả:
Nhap gia tri cho vung nho px tro toi: 42
Vi tri cua bien x la 0xbfba58a0
Vi tri cua bien x la 0xbfba58a0
Vi tri cua bien x la 0xbfba58a0
Noi dung cua bien x la 42
Noi dung cua bien x la 42
Noi dung cua bien x la 42
Noi dung cua bien x la 7868
Vi tri px tro toi la 0xbfba58a4
Trong vd trên ta thấy có một số phép toán trên con trỏ hay gặp sau: (ngoài ra còn nhiều phép toán khác).
Các bạn chú ý:
Trước khi vào phần này ta làm ví dụ nho nhỏ.
1 2 3 4 5 6 7 8 9 | #include <stdio.h> int main() { int *px; *px = 42; printf("Vi tri con tro px la %p \n", px); printf("Gia tri con tro px tro toi la %d \n", *px); return 0; } |
Khi biên dịch thì sẽ không có lỗi (có cảnh báo), khi chạy sẽ không thể chạy được mà chương trình sẽ thoát ra luôn.
Nguyên nhân là khi khai báo biến con trỏ px thì máy mới chỉ cung cấp 2 byte để lưu địa chỉ của biến con trỏ mà chưa cấp phát vùng nhớ để con trỏ px lưu trữ dữ liệu. (tương tự như hợp tác xã cung cấp 2 Kg thóc cho bạn để làm giống nhưng lại không cung cấp cho bạn ruộng đất để bạn reo mạ vậy ).
Lưu ý: Có một số trình dịch sẽ không báo lỗi mà vẫn chạy bình thường nhưng tốt nhất là ta nên cấp phát trước khi sử dụng. Lỗi này sẽ xuất hiện rõ nhất khi bạn sử dụng con trỏ với mảng mà lát nữa ta sẽ đề cập.
Thôi ta đi vào vấn đề chính, làm sao để cấp phát vùng nhớ cho con trỏ.
Để cấp phát vùng nhớ cho con trỏ ta dùng các hàm sau trong thư viện stdlib.h.
Trong đó sizeof(kiểu con trỏ) là kích thước của kiểu; n là số lần của sizeof(kiểu con trỏ) được cấp.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | #include <stdio.h> #include <stdlib.h> int main() { int *px, *qx; px = (int *) malloc(sizeof(int)); qx = (int *) calloc(1, sizeof(int)); printf("Vi tri con tro px la %p \n", px); printf("Gia tri con tro px tro toi la %d \n", *px); printf("Vi tri con tro qx la %p \n", qx); printf("Gia tri con tro qx tro toi la %d \n", *qx); return 0; } |
Ở đây các bạn chú ý: sự khác nhau duy nhất giữa malloc và calloc mà các bạn hiểu đơn giản là với malloc thì khi cấp phát máy sẽ cấp phát cho px 1 ô bất kỳ mà không cần biết ô đó có dữ liệu là gì hay không có dữ liệu (do đó *px có giá trị như trên) còn calloc cũng vậy nhưng khác 1 điểm là sau khi cấp phát thì máy sẽ tự động gán luôn giá trị 0 cho ô nhớ mà biến qx trỏ tới, tức qx có giá trị mặc định là 0.
Khi cấp phát cho biến con trỏ 1 số lượng ô nhớ nào đó mà trong quá trình làm việc ta thiếu và cần cấp phát thêm thì ta sử dụng lệnh realloc:
tên con trỏ = (kiểu con trỏ *) realloc (tên con trỏ, số lượng cần cấp phát * sizeof(kiểu con trỏ));
Trong đó: số lượng cần cấp phát = cũ + mới.
VD: Ban đầu ta cấp phát cho con trỏ px là 10 ô nhớ. Sau đó muốn cấp phát thêm cho nó 5 ô nhớ nữa thì số lượng cấp phát = 15.
Để thu hổi bộ nhớ đã cấp phát ta dùng hàm free(tên con trỏ);
Lưu ý: Chúng ta có thể dùng con trỏ để cấp phát 1 mảng động để không bị giới hạn số phần tử.