Bài giảng Hệ thống máy tính và ngôn ngữ lập trình - Chương 12: Pointer - Nguyễn Phúc Khải

Khái niệm
 Thao tác trên POINTER
 POINTER và mảng
 Đối số của hàm là pointer - truyền đối số theo
số dạng tham số biến
 Hàm trả về pointer và mảng
 Chuỗi ký tự
 Pointer và việc định vị bộ nhớ động
 Mảng các pointer 
pdf 83 trang xuanthi 29/12/2022 2200
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Hệ thống máy tính và ngôn ngữ lập trình - Chương 12: Pointer - Nguyễn Phúc Khải", để tải tài liệu gốc về máy hãy click vào nút Download ở trên.

File đính kèm:

  • pdfbai_giang_he_thong_may_tinh_va_ngon_ngu_lap_trinh_chuong_12.pdf

Nội dung text: Bài giảng Hệ thống máy tính và ngôn ngữ lập trình - Chương 12: Pointer - Nguyễn Phúc Khải

  1. Các nội dung: . Khái niệm . Thao tác trên POINTER . POINTER và mảng . Đối số của hàm là pointer - truyền đối số theo số dạng tham số biến . Hàm trả về pointer và mảng . Chuỗi ký tự . Pointer và việc định vị bộ nhớ động . Mảng các pointer © TS. Nguyễn Phúc Khải 2
  2. KHÁI NIỆM . Một biến có kiểu pointer có thể lưu được dữ liệu trong nó, là địa chỉ của một đối tượng đang khảo sát. Đối tượng đó có thể là một biến, một chuỗi hoặc một hàm. © TS. Nguyễn Phúc Khải 4
  3. KHÁI NIỆM . Hình ảnh stack thực thi khi điều khiển chương trình đang ở dòng doi_1 = doi_2 ; © TS. Nguyễn Phúc Khải 6
  4. THAO TÁC TRÊN POINTER . Cú pháp để khai báo biến pointer: kiểu *tên_biến_pointer . Với:  kiểu có thể là kiểu bất kỳ, xác định kiểu dữ liệu có thể được ghi vào đối tượng mà con trỏ đang trỏ đến.  tên_biến_pointer là tên của biến con trỏ, một danh hiệu hợp lệ. © TS. Nguyễn Phúc Khải 8
  5. THAO TÁC TRÊN POINTER . Khai báo biến pointer - pointer hằng: . Trong ngôn ngữ C, một toán tử lấy địa chỉ của một biến đang làm việc, toán tử này là một dấu & (ampersand), tạm gọi là toán tử lấy địa chỉ. Cú pháp như sau: & biến . với biến là một biến thuộc kiểu bất kỳ, nhưng không được là biến thanh ghi. © TS. Nguyễn Phúc Khải 10
  6. THAO TÁC TRÊN POINTER . Ví dụ: Xét ví dụ sau: int object; int *pint; object = 5; pint = &object; printf(“%d”,*pint); © TS. Nguyễn Phúc Khải 12
  7. THAO TÁC TRÊN POINTER . Các phép toán trên pointer: . Có thể cộng, trừ một pointer với một số nguyên (int, long, ). Kết quả là một pointer. . Ví dụ : int *pi1, *pi2, n; pi1 = &n; pi2 = pi1 + 3; © TS. Nguyễn Phúc Khải 14
  8. THAO TÁC TRÊN POINTER . KHÔNG thể thực hiện các phép toán nhân, chia, hoặc lấy dư một pointer với một số, vì pointer lưu địa chỉ, nên nếu thực hiện được điều này cũng không có một ý nghĩa nào cả. . Phép trừ giữa hai pointer vẫn là một phép toán hợp lệ, kết quả là một trị thuộc kiểu int biểu thị khoảng cách (số phần tử) giữa hai pointer đó. © TS. Nguyễn Phúc Khải 16
  9. THAO TÁC TRÊN POINTER . Ví dụ: Cho các khai báo sau: int * a1; char * a2; a1 = 0;/* Chương trình dịch sẽ nhắc nhở lệnh này */ a2 = (char *)0; if( a1 != a2) /* Chương trình dịch sẽ nhắc nhở kiểu của đối tượng */ { a1 = (int *) a2; /* Hợp lệ vì đã ép kiểu */ } © TS. Nguyễn Phúc Khải 18
  10. THAO TÁC TRÊN POINTER printf ("Tri cua bien pint la: %p\n", pint); printf ("Tri cua bien pchar la: %p\n", pchar); printf ("Doi tuong pint dang quan ly la %X \n", *pint); printf ("Doi tuong pchar dang quan ly la %X \n", *pchar); pchar++; printf ("Doi tuong pchar dang quan ly la %X \n", *pchar); getch(); } © TS. Nguyễn Phúc Khải 20
  11. THAO TÁC TRÊN POINTER . Các khai báo sau đây cho thấy đối tượng của một pointer là hằng: 1. int i; const int * pint; pint = &i; i = 1; (*pint) ++;  C báo lỗi i++; 2. const char * s = "ABCD" hoặc char const * s = "ABCD"; © TS. Nguyễn Phúc Khải 22
  12. POINTER VÀ MẢNG . Trong C, tên mảng là một hằng pointer tới phần tử có kiểu là kiểu của biến thành phần dưới nó một bậc trong mảng, . VD: tên của mảng một chiều của các int là pointer chỉ tới int, tên của mảng hai chiều của các int là pointer chỉ tới mảng một chiều là hàng các int trong mảng. . Trong trường hợp mảng một chiều, tên mảng chính là địa chỉ của phần tử đầu tiên của mảng. Do đó, ta hoàn toàn có thể truy xuất mảng bằng một pointer © TS. Nguyễn Phúc Khải 24
  13. POINTER VÀ MẢNG Khi đó, (pa + 0) sẽ chỉ đến phần tử a[0], (pa + 1) sẽ chỉ đến phần tử a[1], (pa + i) sẽ chỉ đến phần tử a[i]. Như vậy, các đối tượng *(pa + 0) chính là *(a + 0), cũng là a[0] *(pa + 1) chính là *(a + 1), cũng là a[1] *(pa + i) chính là *(a + i), cũng là a[i] © TS. Nguyễn Phúc Khải 26
  14. POINTER VÀ MẢNG . Phân biệt rõ ràng giữa mảng và pointer:  Một mảng, sau khi được khai báo và định nghĩa, đã được cấp một vùng nhớ trong bộ nhớ trong của máy tính và địa chỉ chính là tên mảng.  Một biến pointer, sau khi được khai báo, thì vùng nhớ được cấp chỉ là bản thân biến pointer, còn trị bên trong nó là một địa chỉ rác.  Ngoài ra, biến pointer có một địa chỉ trong bộ nhớ, còn không thể lấy địa chỉ của tên mảng. © TS. Nguyễn Phúc Khải 28
  15. POINTER VÀ MẢNG . Ví dụ: int *pa; . thì trong bộ nhớ . do đó lệnh gán . *pa = 2; là không có ý nghĩa, dù C không báo lỗi trong trường hợp này. © TS. Nguyễn Phúc Khải 30
  16. POINTER VÀ MẢNG . Ví du: #define MAX_ROW 20 #define MAX_COL 30 int array[MAX_ROW][MAX_COL]; int row, col; int (*parr) [MAX_ROW][MAX_COL]; /* biến con trỏ mảng hai chiều */ parr = &array; © TS. Nguyễn Phúc Khải 32
  17. POINTER VÀ MẢNG . Khi đó, ta có: array[row][col] ≡ *(array[row] + col), tức array[row][col] ≡ *( int (*) array + row*MAX_COL + col) array[row][col] ≡ *(*(array + row) + col) array[row][col] ≡ *(*( (int (*)[MAX_COL]) parr + row) + col) © TS. Nguyễn Phúc Khải 34
  18. ĐỐI SỐ CỦA HÀM LÀ POINTER - TRUYỀN ĐỐI SỐ THEO SỐ DẠNG THAM SỐ BIẾN #include #include void Swap (int *doi_1, int *doi_2); main () { int a = 3, b = 4; clrscr(); printf ("Tri cua hai bien a va b la: %d %d\n", a, b); Swap (&a, &b); printf ("Sau khi goi ham Swap, tri cua a va b la: %d %d\n", a, b); getch(); } © TS. Nguyễn Phúc Khải 36
  19. ĐỐI SỐ CỦA HÀM LÀ POINTER - TRUYỀN ĐỐI SỐ THEO SỐ DẠNG THAM SỐ BIẾN © TS. Nguyễn Phúc Khải 38
  20. Bài tập . Viết hàm nhập 3 số từ bàn phím và hàm nhập mảng 1 chiều. Trong hàm main(), gọi 2 hàm trên để nhập dữ liệu và xuất giá trị ra màn hình. © TS. Nguyễn Phúc Khải 40
  21. HÀM TRẢ VỀ POINTER VÀ MẢNG . Ví dụ: Có khai báo int *lon_nhat (int a, int b, int c); . thì hàm lon_nhat() trả về một địa chỉ, địa chỉ đó có thể là địa chỉ của một int hoặc địa chỉ của một mảng các int, việc sử dụng địa chỉ theo đối tượng nào là do nơi gọi. © TS. Nguyễn Phúc Khải 42
  22. HÀM TRẢ VỀ POINTER VÀ MẢNG . Ví dụ: Chương trình sử dụng hàm nhập trị mảng #include #include int *nhap_tri(int *num); main() { int *pint, so_phan_tu, i; clrscr(); pint = nhap_tri (&so_phan_tu); printf ("Cac phan tu cua mang la:"); for (i =0; i <so_phan_tu; i++) printf ("%d", pint[i]); getch(); } © TS. Nguyễn Phúc Khải 44
  23. CHUỖI KÝ TỰ . Hàm scanf() cũng cho phép nhập chuỗi qua định dạng nhập %s. Việc nhập chuỗi sẽ kết thúc khi hàm scanf() gặp một trong các ký tự khoảng trắng, ký tự tab hay ký tự xuống hàng đầu tiên mà nó gặp. Đây chính là điểm khác nhau giữa hai hàm nhập chuỗi gets() và scanf(). © TS. Nguyễn Phúc Khải 46
  24. CHUỖI KÝ TỰ . Xuất chuỗi . Để xuất chuỗi, hai hàm thường hay được dùng là puts() và printf(). . Hàm puts: ta chỉ cần cung cấp cho hàm đối số là địa chỉ của chuỗi cần in. Hàm này sẽ đọc từng ký tự của chuỗi và in ra màn hình cho đến khi gặp ký tự NUL thì in ra màn hình thêm một ký tự xuống hàng nữa. Prototype của hàm này như sau: int puts (char * s); © TS. Nguyễn Phúc Khải 48
  25. CHUỖI KÝ TỰ . Gán trị cho chuỗi: . Việc gán trị cho biến chuỗi thực tế là việc chép từng ký tự từ hằng chuỗi hoặc biến chuỗi đã biết sang một biến chuỗi khác. Trong C, thao tác này được thực hiện nhờ hàm strcpy(), hàm này có prototype trong file string.h như sau: char *strcpy(char *dest, const char *src); © TS. Nguyễn Phúc Khải 50
  26. CHUỖI KÝ TỰ . Lấy chiều dài chuỗi . Trong C, để lấy chiều dài chuỗi ta dùng hàm strlen(). Prototype của hàm này trong file string.h: size_t strlen(const char *s); . với size_t là một kiểu nguyên, C quy định đây là unsigned. © TS. Nguyễn Phúc Khải 52
  27. CHUỖI KÝ TỰ . Nối chuỗi: . Để nối hai chuỗi lại, C có hàm chuẩn strcat(). Hàm này nhận hai chuỗi làm đối số, và một bản sao của chuỗi thứ hai sẽ được chép nối vào cuối của chuỗi thứ nhất, để tạo ra chuỗi mới. Chuỗi thứ hai vẫn không có gì thay đổi. Prototype của hàm này trong file string.h: char *strcat(char *dest, const char *src); © TS. Nguyễn Phúc Khải 54
  28. CHUỖI KÝ TỰ . Ví dụ: Xét chương trình ví dụ sau đây #include #include #include main() { clrscr(); printf("%d \n", strcmp("QUAN", “quan”)); printf("%d \n', strcmp("QUAN", "QUAN")); printf("%d \n", strcmp("quan", "QUAN")); printf("%d \n", strcmp("quang”, “quanG")); printf("%d \n", strcmp("quang”, “quan")); getch(); } © TS. Nguyễn Phúc Khải 56
  29. POINTER VÀ VIỆC ĐỊNH VỊ BỘ NHỚ ĐỘNG . C cho phép khai báo các biến động, các biến này khi cần thì xin chỗ, không cần thì giải phóng vùng nhớ cho chương trình sử dụng vào mục đích khác. Các biến động này được cấp phát trong vùng nhớ heap, là vùng đáy bộ nhớ (vùng còn lại sau khi đã nạp các chương trình khác xong), và được quản lý bởi các biến pointer © TS. Nguyễn Phúc Khải 58
  30. POINTER VÀ VIỆC ĐỊNH VỊ BỘ NHỚ ĐỘNG . Nếu biến động được xin, sau khi dùng xong, vùng nhớ của nó không được giải phóng thì nó vẫn chiếm chỗ trong bộ nhớ, mặc dù chương trình đã kết thúc. C đưa ra hàm free() để giải phóng khối bộ nhớ được xin bằng hàm malloc() hoặc calloc(). . Prototype của hàm free() trong file stdlib.h hoặc alloc.h như sau: void free (void * block); © TS. Nguyễn Phúc Khải 60
  31. POINTER VÀ VIỆC ĐỊNH VỊ BỘ NHỚ ĐỘNG . Ví dụ: Cần xin một khối bộ nhớ có 10 phần tử int, ta viết như sau: int *pint; pint = (int *) malloc (10 * sizeof (int)); hoặc pint = (int *) calloc (10, sizeof (int)); © TS. Nguyễn Phúc Khải 62
  32. POINTER VÀ VIỆC ĐỊNH VỊ BỘ NHỚ ĐỘNG clrscr(); printf ("Moi nhap 10 tri vao mang: "); for (i = 0; i <10; i++) scanf ("%d", &pint[i]); for (i = 0; i <10; i++) s += pint[i]; printf ("Tong cac phan tu cua mang la: %d \n", s); getch(); free (pint); } © TS. Nguyễn Phúc Khải 64
  33. MẢNG CÁC POINTER © TS. Nguyễn Phúc Khải 66
  34. MẢNG CÁC POINTER © TS. Nguyễn Phúc Khải 68
  35. POINTER CỦA POINTER . Cú pháp khai báo pointer này như sau: kiểu tên_pointer . Ví dụ: int pint; int*p; int a[4][4]; . thì pint = &p; hoặc pint = (int ) &a; © TS. Nguyễn Phúc Khải 70
  36. POINTER CỦA POINTER #include #define MAX_ROW 3 #define MAX_COL 3 main() { int row, col; int *pint1; int a2d [MAX_ROW][MAX_COL] = { {0, 1, 2}, {10, 11, 12}, {20, 21, 22} }; int pint2; int (*pa2d)[MAX_ROW][MAX_COL]; /* Thu dia chi cua pointer va mang 2 chieu */ © TS. Nguyễn Phúc Khải 72
  37. POINTER CỦA POINTER for (row = 0; row < MAX_ROW; row ++) { for (col = 0; col < MAX_COL; col ++) printf ("%d ", *( *( ( int (*)[MAX_COL] ) pint2 + row)+ col)); printf ("\n"); } getchar();} © TS. Nguyễn Phúc Khải 74
  38. POINTER CỦA POINTER . Ví dụ: int pi; int * pint[4]; int a[3], b[3], c[3], d[3]; pi = pint; pint[0] = a; pint[1] = b; pint[2] = c; pint[3] = d; © TS. Nguyễn Phúc Khải 76
  39. ĐỐI SỐ CỦA HÀM MAIN . C hoàn toàn cho phép việc nhận đối số vào hàm main(), có hai đối số C đã quy định theo thứ tự:  int agrc: đối số cho biết số tham số đã nhập, kể cả tên chương trình.  char *argv[]: mảng các pointer trỏ đến các chuỗi là tham số đi theo sau tên chương trình khi chạy chương trình từ DOS. © TS. Nguyễn Phúc Khải 78
  40. ĐỐI SỐ CỦA HÀM MAIN . Nếu nhập từ bàn phím như sau: C:\>thu_main tin thu 123 . thì chương trình cho xuất liệu là: Cac doi so cua chuong trinh la: Ten chuong trinh la: C:\thu_main.exe Doi so thu 1: tin Doi so thu 2: thu Doi so thu 3: 123 © TS. Nguyễn Phúc Khải 80
  41. Bài tập . Viết chương trình nhập vào một dãy số nguyên (chưa biết có bao nhiêu số nguyên). Loại bỏ các số nguyên bị lặp lại. In ra dãy số mới này. © TS. Nguyễn Phúc Khải 82