slide pointer sepro
DESCRIPTION
Quản lý bộ nhớ hiệu quả bằng Con trỏ trong C/C++TRANSCRIPT
1
CON TRỎ TRONG NGÔN NGỮC/C++
SE-PRO GROUP
Đ I H C CÔNG NGH THÔNG TIN - ĐHQG Tp HCM – SE05Ạ Ọ Ệ
2
NỘI DUNG:
DẪN NHẬP CON TRỎ CƠ BẢN CON TRỎ NÂNG CAOTÀI LIỆU THAM KHẢO
3
DẪN NHẬP#include <stdio.h>int main(){ int i; float f; double *pD; printf("\ni=%d f=%f\n“,i,f); getch(); return 1;}
Memory(RAM)
Win n bit thì RAM tối đa
2n byte
Code data stack
programcode
data
stack*.exe
Con trỏ chỉ là một biến kiểu int lưu địa chỉcủa biến khác
Loader
Process
Compiler
Hệ điềuhành nạp Process lên RAM để thực thiquản lý theođịa chỉ
Lưu trữ tại
Byte 0
Byte 2n
Hệ điều hành n bits
4
Con trỏ cơ bản
Khai báo và cách sử dụng1
Các cách truyền đối số cho hàm2
Con trỏ và cấu trúc4
Con trỏ và mảng một chiều3
5
• Khai báo: Giống như mọi biến khác, biến con trỏ muốn sử dụng cũng cần phải được khai báo
<kiểu dữ liệu> *<tên biến con trỏ>;
int a = 5;int *ptr;ptr = &a;
Ví dụ:
Khai báo con trỏ và cách sử dụng
a
Tên : a Giá tri mà biến lưu trữ : 5 Địa chỉ lưu trong bộ nhớ: 1025(giả định)
ptr
Tên : ptrGiá trị mà biến lưu trữ : 1025 Địa chỉ lưu trong bộ nhớ: 5000(giả định)
6
Khai báo con trỏ và cách sử dụng
• Sử dụng từ khóa typedef
typedef <kiểu dữ liệu> *<tên kiểu con trỏ>;<tên kiểu con trỏ> <tên biến con trỏ>;
typedef int *pInt;int *p1;pInt p2, p3;
• Ví dụ:
• Giảm bối rối khi mới tiếp xúc với con trỏ.• Nhưng dễ nhầm lẫn với biến thường.
Chý ý:
7
Khai báo con trỏ và cách sử dụng
• Con trỏ NULL– Con trỏ NULL là con trỏ không trỏ vào đâu cả.– Khác với con trỏ chưa được khởi tạo.
p2
int n;int *p1 = &n;int *p2; int *p3 = NULL;
// Trỏ đến vùng nhớ kiểu int một cách ngẫu nhiên
*p1 và n đều chỉ nội dung của biến n. p1 và &n đều chỉ địa chỉ của biến n.
NULLChú ý:
8
• Truyền địa chỉ (con trỏ) cho hàm
Cách truyền đối số cho hàm
#include <stdio.h>
void hoanvi(int *x, int *y);
void main(){
int a = 3; b = 6;hoanvi(&a, &b);printf(“a = %d, b = %d”, a, b);
}void hoanvi(int *x, int *y){
int t = *x; *x = *y; *y = t;}
9
Con trỏ và mảng một chiều
• Mảng một chiều
– Tên mảng Array là một hằng con trỏ không thể thay đổi giá trị của hằng này.
– Giá trị của Array là địa chỉ phần tử đầu tiên của mảngArray == &Array[0]
int Array[3];
……
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
Array
10
• Con trỏ đến mảng một chiều
…
Con trỏ và mảng một chiều
int Array[3], *pArray;
pArray = Array; // Cách 1pArray = &Array[0]; // Cách 2
……
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
…
18 19 1A 1B 1C 1D 1E 1F
0B 00 00 00
pArray
Array
11
• Phép cộng (tăng) + n + n * sizeof(<kiểu dữ liệu>) Có thể sử dụng toán tử gộp += hoặc ++
+2
Các phép toán số học trên con trỏ
……
p = Array
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
+1
int Array[3]
Con trỏ và mảng một chiều
12
Con trỏ và mảng một chiều
• Phép trừ (giảm) + n + n * sizeof(<kiểu dữ liệu>) Có thể sử dụng toán tử gộp -= hoặc --
p = &Array[2]
–1
–2
……
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
int Array[3]
13
Con trỏ và mảng một chiều// Nhập mảngvoid main(){ int a[10], n = 10, *pa; pa = a; // hoặc pa = &a[0];
for (int i = 0; i<n; i++) scanf(“%d”, &a[i]); // scanf(“%d”, &pa[i]);
// scanf(“%d”, a + i);// scanf(“%d”, pa + i);// scanf(“%d”, a++);// scanf(“%d”, pa++);
} &a[i] (a + i) (pa + i) &pa[i]
// Xuất mảngvoid main(){ int a[10], n = 10, *pa; pa = a; // hoặc pa = &a[0];
for (int i = 0; i<n; i++)printf(“%d”, a[i]);
// printf(“%d”, pa[i]); // printf(“%d”, *(a + i)); // printf(“%d”, *(pa + i)); // printf(“%d”, *(a++); // printf(“%d”, *(pa++));} a[i] *(a + i) *(pa + i) pa[i]
14
Con trỏ và mảng một chiều
void xuat(int _a[10], int n){
for (int i = 0; i<n; i++)printf(“%d”, *(_a++)); // OK
}void main(){
int a[10], n = 10;
for (int i = 0; i<n; i++)printf(“%d”, *(a++)); // Lỗi
}
Đối số mảng truyền cho hàm
Đối số mảng truyền cho hàm không phải hằng con trỏ.
15
Con trỏ và mảng một chiều• Các phép toán khác– Phép so sánh: So sánh địa chỉ giữa hai con trỏ (thứ
tự ô nhớ) == != > >= < <=
– Không thể thực hiện các phép toán: * / %
16
Con trỏ và cấu trúc
Truy xuất bằng 2 cách
typedef struct{
int tu, mau;} PHANSO;PHANSO ps1, *ps2 = &ps1; // ps2 là con trỏ
ps1.tu = 1; ps1.mau = 2;ps2->tu = 1; ps2->mau = 2; (*ps2).tu = 1; (*ps2).mau = 2;
<tên biến con trỏ cấu trúc>-><tên thành phần>(*<tên biến con trỏ cấu trúc>).<tên thành phần>
Ví dụ
17
Bài tập con trỏ cơ bản
float pay;float *ptr_pay;pay=2313.54;ptr_pay = &pay;
Bài 1: Cho đoạn chương trình sau:
Hãy cho biết giá trị của:a. pay b. *ptr_payc. *payd. &pay
18
Bài tập con trỏ cơ bản
• Bài 2: Tìm lỗi#include <stdio.h> #include <conio.h>
void main() {
int *x, y = 2;
*x = y;*x += y++;
printf("%d %d", *x, y);getch();
}
Con trỏ x chưa được khởi tạo
Bài tập con trỏ cơ bản
• Bài tập 3: Cho biết giá trị xuất ra
Output: ???#include <stdio.h> #include <conio.h>
void main() {
int x=25;int *ptr=&x; int **temp=&ptr;printf(“%d %d %d”,x,*ptr,**temp);
}
Bài tập con trỏ cơ bản• Bài 4: Cho đoạn chương trình: #include <stdio.h> #include <conio.h>void main() {
int *ptr=( int *)1000;ptr=ptr+1;printf(" %u",ptr);
}
Output: ???
Bài tập con trỏ cơ bản• Bài 5: Cho đoạn chương trình: #include <stdio.h> #include <conio.h>
void main() {
double *p=( double *)1000;p=p+3;printf(" %u",p);
}
Output: ???
Bài tập con trỏ cơ bản• Bài 6: Cho đoạn chương trình:
#include <stdio.h> #include <conio.h>void calc(int* a, int b)
{ *a = b; *a += b++;
} int main() { int x=5,y=6; calc (&x,y); printf(“%d %d”, x, y); return 0; }
Output: ???
23
Con trỏ nâng cao
Mảng con trỏ2
Mảng động hai chiều3
Con trỏ hàm4
Cấp phát động1
24
Cấp phát độngĐể cấp phất động chúng ta sử dụng thư viện <stdlib.h>
void *malloc( size );
void *calloc( num, size );
HOẶCSố byte cần cấp phát
Kích thước một khối nhớ
Số lượng khối nhớ cần cấp phát
Cấp phát một khối nhớ size bytes trả về con trỏ void trỏ đếnđầu khối nhớ đó và trả về NULL nếu thất bại.
Cấp phát một khối nhớ num*size bytes trả về con trỏ void trỏđến đầu khối nhớ đó và trả về NULL nếu thất bại.
25
Cấp phát độngVí dụ:
double *dVar;dVar = (double*) malloc ( sizeof(double) );
Tương tự với calloc
Dùng con trỏ dVar để quản lý khối nhớ động kiểu double vừa được malloc cấp phát.
double *dVar;dVar = (double*) calloc ( 1 , sizeof(double) );
Để có được một mảng động với n phần tử trong bộ nhớ ta làm như sau:
double *dVar;dVar = (double*) malloc ( n*sizeof(double) );
double *dVar;dVar = (double*) calloc ( n , sizeof(double) );
HOẶC
26
Mảng con trỏĐặt vấn đề
• Khi cần lưu trữ những dãy dữ liệu lớn kích thước không bằng nhau ví dụ như một dãy tên sinh viên chẳng hạn nếu ta dùng mảng hai chiều để lưu trữ sẽ dẫn đến lãng phí do kích thước mỗi tên phải bằng nhau.
Giải quyết vấn đề•Ta dùng một cách lưu trữ mới đó là mảng con trỏ. Với bài toán lưu trữ và xử lý dãy tên sinh viên ta khai báo mảng sau.
for( int i=0; i<max ; i++) { printf(“Name[%d]= “,i); scanf(“%s”,pChar[i]); }
#define max 100;char *pChar[max];
• Khai báo :
for( int i=0; i<max ; i++) { printf(“Name[%d]=%s“,pChar[i]); }
•Xuất
•Nhập
27
Mảng động hai chiều
DataType **Matrix;int Rows, Columns;
• Khai báo:
• Cấp phát Rows dòng cho ma trận thực chất là mảng một chiều gồm Rows con trỏ kiểu DataType.
• Cấp phát cho mỗi dòng Columns phần tử
Matrix = (DataType**) calloc(Rows , sizeof(DataType*));
for (int i=0 ; i<Rows; i++)Matrix[i]=(DataType *) malloc(Columns*sizeof(DataType));
Khai báo và cấp phát
28
Mảng động hai chiều
Sử dụng:typedef int DataType;void Nhap(DataType **M,int r,int c){ int i,j; for ( i=0 ; i<r ; i++) for ( j=0 ; j<c ; j++) {
printf("Nhap M[%d][%d]= ",i,j);scanf("%d",&M[i][j]);
// Hoặc *(M+i)+j }}
typedef int DataType;void Xuat(DataType **M,int r,int c){ int i,j; for ( i=0 ; i<r ; i++) for ( j=0 ; j<c ; j++) {
printf(“M[%d][%d] = %d ", i,j,M[i][j]); // Hoặc *(*(M+i)+j) }}
29
Mảng động hai chiều
Hủy:
for ( i=0 ; i<Rows ; i++) free (Matrix[i]);
• Giải phóng từng dòng một
free (Matrix);
• Giải phóng Matrix
30
Con trỏ hàmKhái niệm– Hàm cũng đuợc lưu trữ trong bộ nhớ, tức là cũng
có địa chỉ.– Con trỏ hàm là con trỏ trỏ đến vùng nhớ chứa
hàm và có thể gọi hàm thông qua con trỏ đó.
……
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
p int Cong(int, int)
11 00 00 00
31
Con trỏ hàm
Khai báo tường minh<kiểu trả về> (* <tên biến con trỏ>)(ds tham số);
Ví dụ
typedef <kiểu trả về> (* <tên con trỏ hàm>)(ds tham số);<tên con trỏ hàm> <tên biến con trỏ>;
int (*pt1)(int, int); // Tường minh
typedef int (*PhepToan)(int, int);
PhepToan pt2, pt3; // Không tường minh
Khai báo không tường minh – bằng typedef
32
Con trỏ hàm
Gán giá trị cho con trỏ hàm
Hàm được gán phải cùng dạng (ds tham số)Ví dụ
<biến con trỏ hàm> = <tên hàm>; // Dạng ngắn gọn<biến con trỏ hàm> = &<tên hàm>; // Dạng sử dụng địa chỉ
int Cong( int x, int y); // Hàmint Tru( int x, int y); // Hàmint (*tinhtoan)(int x, int y);// Con trỏ hàm
tinhtoan = Cong; // Dạng ngắn gọntinhtoan = &Tru; // Dạng sử dụng địa chỉtinhtoan = NULL; // Không trỏ đến đâu cả
33
C. “ Giải bài tập C bằng con trỏ ”
Câu nói nào sau đây là đúng !
Thu hoạch
A. “ Dùng con trỏ để giải bài A”
B. “ Giải bài tập B sử dụng con trỏ”
34
Tài liệu tham khảo
• Kĩ thuật lập trình – Đặng Bình Phương – Khoa Công nghệ thông tin - ĐH KHTN.
• Everything you need to know about pointers in C- Version 1.3 - Copyright 2005–2010 Peter Hosey.
• Understanding Pointers In C By Yashwant Kanetkar.