Bài tập về con trỏ C++
Trước khi liệt kê danh sách các bài tập về Con trỏ trong C, mình xin nhắc lại một số khái niệm cơ bản về Con trỏ (Pointer): Show Con trỏ - Pointer trong ngôn ngữ C rất dễ học. Một vài tác vụ trong ngôn ngữ C được thực hiện dễ dàng hơn nhờ con trỏ, và những tác vụ khác trở nên linh hoạt hơn, như trong việc cấp phát bộ nhớ, không thể thực hiện mà không dùng con trỏ. Do đó rất cần thiết phải nắm vững con trỏ khi trở thành một lập trình viên C hoàn thiện. Bây giờ hãy bắt đầu bằng những bước đơn giản nhất. Con trỏ là gì?Một con trỏ - pointer là một biến mà trong đó giá trị của nó là địa chỉ của biến khác. Ví dụ như địa chỉ của vùng nhớ. Giống như các biến và hằng số, bạn phải khai báo con trỏ trước khi bạn có thể sử dụng nó để lưu trữ bất kì địa chỉ của biến nào. Dạng tổng quát của việc khai báo con trỏ như sau:
int contro; / con tro tro toi mot so nguyen / double phithuebao; / con tro tro toi mot so double / float hocphi; / con tro tro toi mot so float / char ho, ten; / con tro tro toi mot ky tu */ ```Kiểu dữ liệu thực sự của giá trị của tất cả các con trỏ, có thể là số nguyên, float, ký tự, hoặc kiểu khác như một số thập lục phân dài - Long hexa biểu diễn một địa chỉ bộ nhớ. Điểm khác nhau duy nhất của các con trỏ của các kiểu dữ liệu khác nhau là kiểu dữ liệu của biến hoặc hằng số mà con trỏ chỉ tới. Trong bài học này, Lập trình không khó sẽ hướng dẫn các bạn cách sử dụng con trỏ trong ngôn ngữ lập trình C. Bài viết này sẽ giúp các bạn hiểu thế nào là con trỏ, các khái niệm cơ bản liên quan đến con trỏ cũng như cách sử dụng con trỏ trong C. Con trỏ là phần kiến thức khá rộng, do đó bài viết này sẽ hướng dẫn về con trỏ cơ bản; Các bài viết tiếp theo sẽ trình bày chi tiết hơn con trỏ khi làm việc với mảng, cấp phát bộ nhớ và quản lý bộ nhớ,… Mình hi vọng loạt bài học về con trỏ trong C này sẽ giúp các bạn tự tin hơn. Con trỏ trong C là một loại biến đặc biệt mà giá trị của nó là địa chỉ của 1 biến khác.NỘI DUNG BÀI VIẾT Địa chỉ của biến trong CĐể hiểu và sử dụng được con trỏ trong C, trước tiên bạn cần hiểu về khái niệm địa chỉ ở trong C. Nếu bạn nào theo dõi khóa học C bá đạo của mình từ đầu thì chắc đã thấy mình nhắc tới khái niệm này rồi. Phần này ta sẽ làm rõ vấn đề này. 1 2 3 4 int number; printf("\nNhap number = "); scanf("%d", &number); printf("\nnumber = %d", number); Bạn hãy nhìn ví dụ trên, tại sao khi dùng hàm Mỗi biến mà bạn khai báo đều có địa chỉ riêng của nó và giá trị mà nó đang lưu trữ. Để xem được địa chỉ của biến, bạn thêm dấu 1 2 3 4 5 6 7 8 9 10 #include int main() { int number = 5; printf("Gia tri cua number = %d", number);
// truy xuất địa chỉ bằng cách thêm & trước tên biến printf("\nDia chi cua number = %d", &number); return 0; } Kết quả khi chạy chương trình: 1 2 Gia tri cua number = 5 Dia chi cua number = 6487580 Chú ý:
Con trỏ trong CCon trỏ là gì? Con trỏ trong C cũng chỉ là là biến, cũng có thể khai báo, khởi tạo và lưu trữ giá trị và có địa chỉ của riêng nó. Nhưng biến con trỏ không lưu giá trị bình thường, nó là biến trỏ tới 1 địa chỉ khác, tức mang giá trị là 1 địa chỉ. Chúng ta cùng thống nhất 1 số khái niệm khi làm việc với con trỏ nhé:
Chính vì con trỏ mang địa chỉ, nó là 1 biến đặc biệt có thêm những quyền năng mà biến bình thường không có. Nhờ việc nó mang địa chỉ, nó có thể trỏ lung tung trong bộ nhớ. Đây là 1 điểm mạnh nếu ta khai thác tốt nhưng nếu quản lý không tốt thì lại là 1 tai hại. Cách khai báo con trỏCon trỏ trong C cũng có thể khai báo giống như biến bình thường, tên biến là một định danh hợp lệ. Cú pháp như sau: 1 <kiểu dữ liệu> * <tên biến> Trong đó:
1 2 3 4 5 int *p_i; // khai báo con trỏ để trỏ tới biến kiểu nguyên int *p, val; // khai báo con trỏ p kiểu int, biến val (không phải con trỏ) kiểu int float *p_f; // khai báo con trỏ để trỏ tới biến kiểu thực char *p_char; // khai báo con trỏ để trỏ tới biến kiểu ký tự void *p_v; // con trỏ kiểu void (không kiểu) Gán giá trị cho con trỏSau khi khai báo con trỏ, bạn cần khởi tạo giá trị cho nó. Nếu con trỏ được sử dụng mà không được khởi tạo, giá trị của nó sẽ là giá trị rác, điều này sẽ làm chương trình của bạn chạy không đúng, thậm chí là nguy hiểm nếu giá trị rác đó chẳng may lại chính là địa chỉ của 1 biến nào đó bạn đang dùng. 1 2 3 int *p, value; value = 5; p = &value; // khởi tạo giá trị cho con trỏ p là địa chỉ của value Hoặc bạn cũng có thể khai báo và khởi tạo đồng thời: 1 2 int value = 5; int *p = &value; // khai báo con trỏ p và khởi tạo giá trị cho con trỏ là địa chỉ của value Lưu ý:
1 2 3 4 5 6 7 8 9 10 11 12 13 #include int main() { int number = 5; float *p_int = &number; }
// Ouput: PS G:\c_cources\day_63> g++ .\Pointer.cpp .\Pointer.cpp: In function 'int main()': .\Pointer.cpp:5:19: error: cannot convert 'int*' to 'float*' in initialization float *p_int = &number; ^
1 2 3 4 5 6 7 8 #include int main() { void *p_int = NULL; printf("Gia tri cua con tro la %d", p_int); } // Output // Gia tri cua con tro la 0 Bản chất của con trỏ trong CBạn sẽ hiểu rõ hơn các quyền năng của con trỏ trong phần này, cũng xem ví dụ dưới đây nào: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include int main() { // Khai báo + khởi tạo biến value = 10 int value = 10;
// Lấy giá trị của biến value printf("\nGia tri cua `value` = %d", value); // Lấy địa chỉ của biến value printf("\nDia tri cua `value` = %d", &value);
printf("\n-------------------\n");
/* Khai báo + khởi tạo biến con trỏ p có giá trị là địa chỉ của biến value */ int *p = &value;
// Lấy giá trị của con trỏ p printf("\nGia tri cua con tro `p` = %d", p); // Lấy địa chỉ của con trỏ p printf("\nDia tri cua con tro `p` = %d", &p); // Lấy giá trị của biến ma con trỏ p đang trỏ tới dùng toán tử * printf("\nGia tri cua bien ma con tro `p` dang tro toi = %d", *p);
printf("\n-------------------\n");
/* Thay đổi giá trị của biến value thông qua con trỏ p Giống như hàm scanf() có thể thay đổi giá trị của biến khi nhận vào địa chỉ, con trỏ khi có địa chỉ của 1 biến hoàn toàn có thể thay đổi giá trị của biến đó theo cách dưới đây: */ // Lấy giá trị của biến value printf("\nGia tri cua `value` = %d", value); // Thay đổi giá trị của biến value thông qua `p` *p = 100; // Lấy giá trị của biến value printf("\nGia tri cua `value` = %d", value); // Lấy giá trị của biến ma con trỏ p đang trỏ tới dùng toán tử * printf("\nGia tri cua bien ma con tro `p` dang tro toi = %d", *p);
printf("\n-------------------\n");
/* Việc lấy giá trị của biến thông qua con trỏ chỉ là 1 cách khác để lấy được giá trị của biến đó.
*/ value = 1000; // Lấy giá trị của biến value printf("\nGia tri cua `value` = %d", value); // Lấy giá trị của biến ma con trỏ p đang trỏ tới dùng toán tử * printf("\nGia tri cua bien ma con tro `p` dang tro toi = %d", *p); } Kết quả chạy: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Gia tri cua `value` = 10 Dia tri cua `value` = 6487580 -------------------
Gia tri cua con tro `p` = 6487580 Dia tri cua con tro `p` = 6487568 Gia tri cua bien ma con tro `p` dang tro toi = 10 -------------------
Gia tri cua `value` = 10 Gia tri cua `value` = 100 Gia tri cua bien ma con tro `p` dang tro toi = 100 -------------------
Gia tri cua `value` = 1000 Gia tri cua bien ma con tro `p` dang tro toi = 1000 Qua ví dụ này, bạn có thể thấy rõ sự đúng đắn của các kết luận sau đây về con trỏ:
Bài học hôm nay chúng ta sẽ chỉ dừng lại ở các kiến thức phía trên, các bài học sau chúng ta sẽ cùng nhau đi tìm hiểu về mối liên hệ giữa con trỏ với mảng và con trỏ với hàm cũng như cách quản lý bộ nhớ khi làm việc với con trỏ trong C. Các lỗi thường gặp khi làm việc với con trỏGiả sử bạn muốn khởi tạo giá trị của con trỏ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int value, *p;
// Sai! p cần địa chỉ cơ, // value không phải là cái địa chỉ đó. p = value;
// Sai! *p là giá trị của biến mà con trỏ đang trỏ tới, // &value là địa chỉ. *p = &value;
// Đúng rồi! p cần 1 địa chỉ, // &value là địa chỉ của biến value. p = &value;
// Đúng! *p là giá trị của biến mà con trỏ đang trỏ tới, và // c cũng là giá trị (không phải địa chỉ). *p = value; Các bạn khi mới học con trỏ sẽ mông lung về dấu 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include int main() { int c = 5; // Dấu * ở đây để chúng ta biết chúng ta đang khai báo con trỏ. // Không phải lấy giá trị của nó nhé int *p = &c; // Khai báo trên tương đương // int *p; // p = &c; // Nếu bạn muốn phân biệt 2 thằng này, khi khai báo có thể viết như sau: // int* p = &c;
// Lấy giá chỉ của biến mà con trỏ đang trỏ tới, chính là giá trị của c printf("%d", *p); // 5 } Tài liệu tham khảoMặc dù mình đã cố gắng trình bày tỉ mỉ, nhưng có thể còn thiếu sót. Dưới đây là 1 số tài liệu bạn nên đọc thêm để hiểu hơn về con trỏ trong C:
Nguyễn Văn Hiếu Sáng lập cộng đồng Lập Trình Không Khó với mong muốn giúp đỡ các bạn trẻ trên con đường trở thành những lập trình viên tương lai. Tất cả những gì tôi viết ra đây chỉ đơn giản là sở thích ghi lại các kiến thức mà tôi tích lũy được. |