Xem mẫu

  1. BÀI GIẢNG HỌC PHẦN KỸ THUẬT LẬP TRÌNH CHƯƠNG 6: CON TRỎ
  2. Nội dung 6.1. Con trỏ và cú pháp khai báo 6.2. Các phép toán trên biến con trỏ 6.3. Con trỏ và hàm 6.4. Con trỏ và dữ liệu kiểu mảng, xâu ký tự, cấu trúc 6.5. Cấp phát bộ nhớ động 2
  3. 6.1. Con trỏ và cú pháp khai báo • Khái niệm • Cú pháp khai báo • Con trỏ kiểu void 3
  4. Khái niệm • Kiểu con trỏ là kiểu dữ liệu dùng để chứa địa chỉ • Biến con trỏ (gọi tắt là con trỏ) dùng để chứa địa chỉ của một đối tượng (biến hoặc hàm) • Con trỏ thường được dùng trong các trường hợp: - Trả về nhiều giá trị từ hàm (thông qua cơ chế truyền tham số theo địa chỉ trong hàm) - Truyền mảng và xâu ký tự giữa các hàm - Tạo các cấu trúc dữ liệu phức tạp (danh sách liên kết, cây nhị phân, …) 4
  5. Cú pháp khai báo (1) • Cú pháp: kiểu_dữ_liệu *tên_con_trỏ; Ví dụ: int x,y,*px,*py; x, y là các biến nguyên px, py là các con trỏ kiểu int (cấp phát các vùng nhớ có tên là px, py dùng để lưu địa chỉ của các đối tượng kiểu int) *px là nội dung của px (giá trị của đối tượng mà px lưu địa chỉ) *py là nội dung của py (giá trị của đối tượng mà py lưu địa chỉ) 5
  6. Cú pháp khai báo (2) Khi sử dụng các lệnh: px = &x; //gán địa chỉ của biến x cho con trỏ px py = &y; //gán địa chỉ của biến y cho con trỏ py  ta nói: px trỏ tới x và py trỏ tới y  *px tương đương với x, *py tương đương với y 6
  7. Ví dụ (1) • Khai báo: int x = 4,y = 5,*px,*py; Địa chỉ Nội Địa chỉ Nội Biến Biến vùng nhớ dung vùng nhớ dung x 1201 px 2010 4 1202 2011 1203 2012 1204 2013 y 1205 py 2014 5 1206 2015 1207 2016 7
  8. Ví dụ (2) • Thực hiện các lệnh gán: px = &x; py = &y; Địa chỉ Nội Địa chỉ Nội Biến Biến vùng nhớ dung vùng nhớ dung x 1201 px 2010 4 1201 1202 2011 1203 2012 1204 2013 y 1205 py 2014 5 1205 1206 2015 1207 2016 8
  9. Ví dụ (3) • Thực hiện các lệnh gán: *px += 10; *py += 10; Địa chỉ Nội Địa chỉ Nội Biến Biến vùng nhớ dung vùng nhớ dung x 1201 px 2010 14 1201 1202 2011 1203 2012 1204 2013 y 1205 py 2014 15 1205 1206 2015 1207 2016 9
  10. Con trỏ kiểu void • Là dạng con trỏ đặc biệt (con trỏ không kiểu), có thể nhận bất kỳ địa chỉ kiểu nào • Cú pháp khai báo: void *tên_con_trỏ; Ví dụ: void *p; float a[20][30]; p=a; • Con trỏ void thường được dùng làm tham số để nhận bất kỳ địa chỉ kiểu nào từ tham số thực. Khi đó, trong thân hàm phải sử dụng phép ép kiểu để chuyển sang dạng địa chỉ cần xử lý 10
  11. 6.2. Các phép toán trên biến con trỏ (1) Có 4 phép toán cơ bản: Phép gán, phép tăng/giảm địa chỉ, phép truy nhập bộ nhớ, phép so sánh • Phép gán giá trị: - Các con trỏ phải cùng kiểu, muốn gán các con trỏ khác kiểu nên dùng phép ép kiểu Ví dụ: int x; char *p; p = (char*)(&x); 11
  12. 6.2. Các phép toán trên biến con trỏ (2) • Phép tăng/giảm địa chỉ: - Ví dụ 1: float x[30],*p; p = &x[10];//p trỏ tới x[10] Giá trị kiểu float lưu trong 4 byte  các phép tăng/giảm địa chỉ được thực hiện trên 4 byte p+i trỏ tới x[10+i], p-i trỏ tới x[10-i] - Ví dụ 2: float y[20][30]; y là một mảng gồm các dòng có 30 phần tử thực Kiểu địa chỉ của y là 30*4 = 120 byte y trỏ tới đầu dòng thứ nhất y[0][0] y+1 trỏ tới đầu dòng thứ hai y[1][0] … 12
  13. 6.2. Các phép toán trên biến con trỏ (3) • Nguyên tắc truy nhập bộ nhớ: - Con trỏ float truy nhập tới 4 byte, con trỏ int truy nhập tới 2 byte, con trỏ char truy nhập tới 1 byte - Ví dụ: float *pf; int *pi; char *pc; Giả sử pf trỏ tới byte 10001 thì *pf biểu thị vùng nhớ 4 byte từ 10001 đến 10004 Giả sử pi trỏ tới byte 10001 thì *pi biểu thị vùng nhớ 2 byte từ 10001 đến 10002 Giả sử pc trỏ tới byte 10001 thì *pc biểu thị vùng nhớ 1 byte 10001 13
  14. 6.2. Các phép toán trên biến con trỏ (4) • Phép so sánh: - Áp dụng với các con trỏ cùng kiểu - Ví dụ: float *p1,*p2; Khi đó: + p1p2 nếu địa chỉ p1 trỏ tới là cao hơn địa chỉ p2 trỏ tới • Lưu ý: Các phép tăng/giảm địa chỉ, truy nhập bộ nhớ và phép so sánh không dùng trên con trỏ void 14
  15. 6.3. Con trỏ và hàm (1) • Tham số của hàm có thể được chia làm 2 loại: Tham số đầu vào (chứa các giá trị đã biết) và tham số đầu ra (chứa các kết quả mới nhận được) • Thông thường, hàm được dùng để trả về một kết quả thông qua tên hàm. Khi cần trả về nhiều kết quả, cần sử dụng các tham số đầu ra dạng con trỏ • Ví dụ: Hàm trả về nghiệm của phương trình bậc hai ax2 + bx + c = 0  Tham số đầu vào: a, b, c  Tham số đầu ra: x1, x2 Lưu ý: Khi sử dụng các tham số đầu ra là các con trỏ, các tham số thực sự tương ứng trong lời gọi hàm phải là các địa chỉ 15
  16. 6.3. Con trỏ và hàm (2) • Chương trình giải phương trình bậc 2: #include #include int ptb2(float a, float b, float c, float *x1,float *x2); int main(void) { float a,b,c,x1,x2; int kt; printf("Nhap vao cac he so:\n"); printf("a = ");scanf("%f",&a); printf("b = ");scanf("%f",&b); printf("c = ");scanf("%f",&c); 16
  17. 6.3. Con trỏ và hàm (3) • Chương trình giải phương trình bậc 2: (tiếp) kt = ptb2(a,b,c,&x1,&x2); if (kt == -1) printf("Delta < 0, phuong trinh vo nghiem!"); else if (kt == 0) printf("Delta = 0, phuong trinh co nghiem kep: x1 = x2 = %6.2f",x1); else printf("Delta > 0, phuong trinh co 2 nghiem: x1 = %6.2f, x2 = %6.2f",x1,x2); return 0; } 17
  18. 6.3. Con trỏ và hàm (4) • Chương trình giải phương trình bậc 2: (tiếp) int ptb2(float a, float b, float c, float *x1,float *x2) { float delta; delta = b*b-4*a*c; if (delta < 0) return -1; else if (delta == 0) { *x1 = -b/(2*a); return 0; } 18
  19. 6.3. Con trỏ và hàm (5) • Chương trình giải phương trình bậc 2: (tiếp) else { *x1 = (-b+sqrt(delta))/(2*a); *x2 = (-b-sqrt(delta))/(2*a); return 1; } } 19
  20. 6.4. Con trỏ và dữ liệu kiểu mảng, xâu ký tự, cấu trúc • Con trỏ và mảng một chiều • Con trỏ và mảng nhiều chiều • Con trỏ và xâu ký tự • Con trỏ và cấu trúc • Mảng con trỏ 20
nguon tai.lieu . vn