Bài giảng Kỹ thuật lập trình - Bài 7: Kiểu con trỏ - Ngô Hữu Dũng
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Kỹ thuật lập trình - Bài 7: Kiểu con trỏ - Ngô Hữu Dũng", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
Tài liệu đính kèm:
- bai_giang_ky_thuat_lap_trinh_bai_7_kieu_con_tro_ngo_huu_dung.pdf
Nội dung text: Bài giảng Kỹ thuật lập trình - Bài 7: Kiểu con trỏ - Ngô Hữu Dũng
- Kỹ thuật lập trình Bài 7 – Kiểu con trỏ TS. Ngô Hữu Dũng 181 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Khái niệm con trỏ (pointer) Con trỏ là biến mà giá trị của nó là địa chỉ bộ nhớ Địa chỉ bộ nhớ? scanf(“%d”,&i); // &i là địa chỉ bộ nhớ của biến i Khai báo type * variable_name; p = 0073FB60 i = 20 Con trỏ lưu địa chỉ bộ nhớ Biến Địa chỉ Giá trị i 20 int i = 20; 0073FB60 p 0073FB54 int * p; // Khai báo con trỏ p 0073FB60 p = &i; // Con trỏ p được gán bằng địa chỉ của biến i Ta nói con trỏ p “trỏ vào” biến i 182 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ và địa chỉ 1. int x; // Biến số nguyên x 2. int *p; // Con trỏ p kiểu số nguyên 3. p = &x; // p trỏ vào x 4. x = 20; 5. printf("%d ", x); // Giá trị của x 6. printf("%d ", *p); // Giá trị của x 7. printf("%p ", &x); // Địa chỉ của x 8. printf("%p ", p); // Địa chỉ của x 9. *p = 40; 10.printf("Gia tri: %d = %d" , *p, x); 11.printf("Dia chi: %p = %p" , p, &x); 183 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Kiểu con trỏ Khai báo: Dùng dấu * int *p; Địa chỉ của biến mà con trỏ trỏ vào p = &x Giá trị của biến mà con trỏ trỏ vào: Dùng dấu * *p = x In ra màn hình địa chỉ: Dùng %p printf(“%p = %p”, p, &x); Chú ý: Phân biệt các dấu * và các dấu & 184 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Sử dụng con trỏ Chưa trỏ vào biến int *p; *p = 40; // Run-time error! Trỏ vào biến int x, *p1, *p2; p1 = &x; // Trỏ p1 vào biến x *p1 = 40; // x = 40; p2 = p1; // gán trực tiếp, tương đương với p2 = &x; *p2 = 50; // x = 50; *p1 = 60; // x = 60; 185 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Ví dụ vận dụng 1. int x, y, *px, *py=NULL; 2. px = &x; // px trỏ vào x 3. py = &y; // py trỏ vào y 4. *px = 10; 5. y = 15; 6. printf("x=%d\ny=%d\n",*px,*py); 7. (*px)++; ++*px; 8. *py += 5; 9. printf("x=%d\ny=%d\n",*px,*py); 10. printf("%d+%d=%d\n",x,y,*px + *py); 11. printf("%d*%d=%d\n",x,y,*px * *py); 12. printf("&x=%p\n&y=%p\n",px,py); 186 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Phạm vi ứng dụng con trỏ Con trỏ có thể trỏ vào bất kỳ dữ liệu nào Một kiểu nguyên thủy: int, float, char NULL (rỗng, không trỏ đến đâu) Một hằng số Một đối số của hàm Một mảng/chuỗi Một cấu trúc struct Một hàm Một con trỏ khác (con trỏ trỏ vào con trỏ) Một ô nhớ cấp phát động Một tập tin 187 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Lợi hại của con trỏ Có thể dùng để thao tác với bất kỳ loại dữ liệu nào Thuật lợi để truy cập đến cấu trúc dữ liệu lớn Sử dụng bộ nhớ linh hoạt Cấp phát / giải phóng bộ nhớ động trong quá trình thực thi Cấp phát bộ nhớ mới: Hàm malloc trong stdlib.h Giải phóng bộ nhớ: Hàm free trong stdlib.h Nếu sử dụng con trỏ không cẩn thận Dễ nhầm lẫn Khó tìm lỗi 188 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Kiểu nguyên thủy 1. // Con trỏ có thể trỏ vào các kiểu nguyên thủy 2. int i = 10, *pi; 3. float f = 1.2e2, *pf; 4. char c = 'r', *pc; 5. pi = &i; 6. pf = &f; 7. pc = &c; 8. printf("%d,%f,%c\n",*pi,*pf,*pc); 9. // Chú ý dùng đúng kiểu dữ liệu 10.pc = &f; // Warning! 11.pf = &i; // Warning! 12.pi = &c; // Warning! 189 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ rỗng - NULL NULL là giá trị đặc biệt của con trỏ Không trỏ đến đâu #define NULL 0 Được định nghĩa trong thư viện stdio.h Sử dụng Khởi tạo con trỏ: int *p = NULL; Kiểm tra một con trỏ if (p != NULL) printf(“%d”,*p); if (p) printf(“%d”, *p); Kết thúc một danh sách dữ liệu 190 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ và hằng số Con trỏ trỏ vào hằng số int m, n; const int *ip = &m; ip = &n; // OK! Có thể thay đổi giá trị con trỏ *ip = 10; // ERROR !!! Không thể thay đổi giá trị của biến Con trỏ là hằng số: Chỉ trỏ vào một biến int m, n; int * const cip = &m; *cip = 10; // OK! Có thể thay đổi giá trị của biến cip = &n; // ERROR !!! Không thể thay đổi giá trị con trỏ 191 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ và đối số của hàm 1. void swap (int *px, int *py) // Tham biến 2. { 3. int temp = *px; // Dùng dấu * 4. *px = *py; // để truy cập giá trị 5. *py = temp; 6. } 7. int a = 5, b = 7; 8. swap(&a, &b); // Truyền địa chỉ của biến 9. printf("a = %d, b = %d", a, b); 192 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ và mảng Con trỏ có thể trỏ vào một mảng #define N 7 pa int a[N], b[N]; int *pa, *pb; a[0] a[1] a[2] a[3] a[4] a[5] a[6] pa = a; pb = &b[0]; pb pc int *pc; b[0] b[1] b[2] b[3] b[4] b[5] b[6] pc = &b[5]; pa = a; // Mặc định trỏ vào phần tử đầu tiên của mảng, a[0] 193 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ và phần tử của mảng Địa chỉ của phần tử thứ i pa + i = &a[i] pa pa+1 Giá trị của phần tử thứ i *(pa + i) = pa[i] = a[i] a[0] a[1] a[2] a[3] a[4] a[5] a[6] Ví dụ 1 2 3 *pa = 1; pb pc-1 pc *(pa+1) = 2; pa[2] = 3; b[0] b[1] b[2] b[3] b[4] b[5] b[6] *pb = 4; 4 5 6 *(pc-1) = 5; *pc = 6; 194 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Ví dụ vận dụng 1. #define N 10 2. int a[N]; 3. int i, *pa, *pt; 4. pa = a; //Trỏ vào mảng, không dùng & 5. for (i=0;i<N;i++) 6. *(pa+i)=i; // *(pa+i)=a[i] 7. for (pt=pa;pt<pa+N;pt++) 8. printf("%d ",*pt); // *pt=a[i] 9. for (i=0;i<N;i++) 10. printf("%d ",pa[i]); // pa[i]=a[i] 195 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Mảng là con trỏ hằng số (constant pointer) Mảng là con trỏ hằng số int a[10]; int *pa; pa = a; // a là con trỏ, pa có thể được gán cho a, không cần &a pa++; // Con trỏ pa có thể thay đổi giá trị a = pa; // ERROR! a là hằng số a++; // ERROR! a là hằng số Mảng a là con trỏ trỏ vào phần tử đầu tiên của mảng và không thể thay đổi giá trị địa chỉ 196 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Truyền đối số là mảng / chuỗi Truyền đối số là một mảng vào một hàm void sapxep(int a[], int n) Địa chỉ của phần tử đầu tiên của mảng được truyền: &a[0] Có thể thay thế bằng con trỏ void sapxep(int *a, int n) Tương tự với chuỗi Tham số function(char s[]){ } và function(char *s){ } là tương đương 197 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Ví dụ truyền đối số là mảng/chuỗi 1. int printArray(int *pa, int n) 2. { 3. int *pt; 4. for (pt=pa;pt<pa+n;pt++) //gán, so sánh, tăng 5. printf("%d ",*pt); 6. } 7. int stringLength(char *s) 8. { 9. if(*s=='\0') 10. return 0; 11. else 12. return 1 + stringLength(++s); 13. } 198 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Các phép toán của con trỏ Phép gán hai con trỏ cùng kiểu dữ liệu int a, *pa, *pb; pa=&a; pb = pa; Cộng/trừ con trỏ với số nguyên Tăng/giảm giá trị của con trỏ theo sizeof() Ví dụ: pb+n tăng giá trị của p lên n x sizeof(int) So sánh giữa hai con trỏ trỏ vào phần tử trong cùng một mảng Các phép toán ==, !=, >, =, <= Phép trừ giữa hai con trỏ trỏ vào phần tử trong cùng một mảng Gán hoặc so sánh với NULL (zero) 199 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Ví dụ về các phép toán của con trỏ 1. int printRevertArray(int *pa, int n) 2. { 3. int *pt = pa+n-1; // Cộng số tự nhiên 4. while (pt>=pa) // Phép so sánh 5. { 6. printf("%d ",*pt); pt ; // Phép toán giảm 7. } 8. } 9. int stringLength(char *s) 10. { 11. char *p = s; // Phép gán 12. while (*p!='\0') 13. p++; // Phép toán tăng 14. return p - s; // Phép trừ của con trỏ 15. } 200 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ và chuỗi Tương tự như với mảng nhưng chuỗi có một đặc điểm khác Chuỗi kết thúc bằng ký tự rỗng, ‘\0’ 1. void stringCopy(char *s, char *t) 2. { 3. while (*t != '\0') 4. { 5. *s = *t; 6. s++; 7. t++; 8. } 9. *s = '\0'; // Ký tự rỗng kết thúc chuỗi 10. } 201 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ và chuỗi (tt) 1. void stringCopy1(char *s, char *t) 2. { 3. while ((*s = *t) != '\0'){ //Gán, so sánh 4. s++; t++; // Tăng 5. } 6. } 7. void stringCopy2(char *s, char *t) 8. { 9. while ((*s++ = *t++) != '\0');//Gán, so sánh, tăng 10. } 11. void stringCopy3(char *s, char *t) 12. { 13. while (*s++ = *t++); // Gán, tăng 14. } 202 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ và cấu trúc 1. struct t_date{ 2. int day; 3. int month; 4. int year; 5. }; 6. struct t_date today; // Biến cấu trúc 7. struct t_date *ptr; // Con trỏ kiểu cấu trúc 8. ptr = &today; // Trỏ vào cấu trúc 9. (*ptr).day = 15; // Truy cập member, cách 1 10. (*ptr).month = 9; // Dùng dấu ‘.’ 11. (*ptr).year = 2016; 12. ptr->day = 15; // Truy cập member, cách 2 13. ptr->month = 9; // Dùng dấu ‘->’ 14. ptr->year = 2016; 203 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ và cấu trúc (2) 1. struct t_student{ 2. int ID; 3. char *ten; 4. struct t_date birth, *pB; 5. }; 6. struct t_student SV, *pSV; 7. pSV = &SV; // Trỏ vào cấu trúc t_stu 8. pSV->ID = 123; // SV.ID 9. pSV->ten = "Nguyen Van A";// SV.ten 10. pSV->birth.day = 16; // SV.birth.day 11. pSV->pB = &SV.birth; // Trỏ vào cấu trúc t_date 12. pSV->pB->month = 4; // SV.birth.month 13. (*pSV->pB).year = 1996; // SV.birth.year 204 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ và cấu trúc (3) 1. ++pSV->ID; // Tăng giá trị ID 2. printf("%d ",pSV->ID); 3. printf("%c",*pSV->ten); //Ký tự đầu tiên ‘N’ 4. printf("%c",pSV->ten[1]); //Ký tự thứ 2 ‘g’ 5. printf("%s ",pSV->ten+2); //Từ ký tự ‘u’->hết 6. printf("%d ",++pSV->pB->day); //Tăng day 7. printf("%d ",(*pSV->pB).month); 8. printf("%d\n",SV.pB->year); 9. while(*pSV->ten!='\0') 10. printf("%c",*pSV->ten++); 11.printf("\n%s",pSV->ten); // !? Chuỗi rỗng !? 12.printf("%s", pSV->ten); // !? 205 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ và hàm Hàm có thể trả về kiểu con trỏ Đối số kiểu con trỏ 1. char * stringCopy4(char *s, char *t) 2. { 3. int count = 0; 4. do 5. count++; 6. while (*s++ = *t++); 7. return s-count; 8. } 206 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ đến con trỏ Con trỏ có thể chứa địa chỉ của con trỏ khác char x; x x x char * y; char z; 'x' 00000078 00AFF75F x = 'x'; 00000078 00AFF75F 00AFF750 y = &x; z = &y; 207 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ và cấp phát động Con trỏ có thể được cấp phát bộ nhớ Trỏ trực tiếp vào bộ nhớ động Không cần thông qua một biến cụ thể 1. #include 2. int n ,*a , i; 3. scanf("%d", &n); 4. a =(int*)malloc(sizeof(int)*n); // Cấp phát bộ nhớ 5. for(i=0;i<n;i++) 6. a[i]=i; 7. for (i=0;i<n;i++) 8. printf("%d\n",a[i]); 9. free(a); // Giải phóng bộ nhớ 208 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Con trỏ và tập tin Con trỏ có thể trỏ vào tập tin 1. #include < 2. FILE * pFile; 3. pFile = fopen ("myfile.txt","w"); 4. if (pFile!=NULL) 5. { 6. fputs ("fopen example",pFile); 7. fclose (pFile); 8. } 9. return 0; 209 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng
- Hết bài 7 Khái niệm con trỏ Khái quát các ứng dụng của con trỏ Kiểu nguyên thủy: int, float, char NULL (rỗng, không trỏ đến đâu) Hằng số Đối số của hàm Mảng/chuỗi Cấu trúc struct Hàm Con trỏ trỏ vào con trỏ Cấp phát động Tập tin 210 Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng