Bài giảng Tin học đại cương - Phần 8: Con trỏ
1/26/2011
Nội dung
Nhắc lại về tổ chức bộ nhớ của máy tính
Biến con trỏ
Con trỏ và cấu trúc
Con trỏ và hàm
Chương 8
CON TRỎ ‐ POINTER
Con trỏ và cấu trúc
Con trỏ và cấp phát bộ nhớ động
Nhắc lại về tổ chức bộ nhớ máy tính
Trong máy tính, bộ nhớ trong :
chia thành các ô nhớ
Các ô nhớ được đánh địa chỉ khác nhau
Kích thước của mỗi ô nhớ là 1 byte
Địa chỉ ô nhớ
11111111
11111110
11111101
10010101
11010101
10010100
10000101
Nhắc lại tổ chức bộ nhớ của máy tính
00000000
00010101
1
1/26/2011
#include <stdio.h>
#include <stdlib.h> //cho ham system()
Nhắc lại về tổ chức bộ nhớ máy tính
Khi khai báo 1 biến, các ô nhớ sẽ được cấp phát cho biến
đó
int A; // 4 byte
int main()
{
int a, b;
A=5;
double c,d;
a=5; b=7;
c=3.5; d=10.0;
printf("Gia tri a=%d, dia chi %#x\n",a,&a);
printf("Gia tri b=%d, dia chi %#x\n",b,&b);
printf("Gia tri a=%f, dia chi %#x\n",c,&c);
printf("Gia tri a=%f, dia chi %#x\n",d,&d);
system("pause");
Biến A
Biến A được lưu trữ trong 4 ô
bắt đầu tại địa chỉ 10001111
Giá trị của biến A là 5 (4 ô nhớ
chứa giá trị 5)
Lấy địa chỉ ô nhớ (đầu tiên)
cấp phát cho biến: dùng
toán tử &
&A trả về 10001111
10001111
10001110
5
10001101
10001100
10001011
10001010
return 0;
10001001
10001000
}
Biến con trỏ
Biến con trỏ ‐ Pointer Variable: giá trị của biến là một địa
chỉ ô nhớ.
Kích thước 1 biến con trỏ phụ thuộc vào các platform
(môi trường ứng dụng):
Platform 16 bit là 2 byte.
Platform 32 bit là 4 byte.
Platform 64 bit là 8 byte.
Biến con trỏ
Khai báo biến con trỏ
KieuDuLieu *TenBien;
int *pInt;
float *pFloat;
2
1/26/2011
0x23FF74
0x23FF73
0x23FF72
0x23FF71
0x23FF70
0x23FF6F
0x23FF6E
0x23FF6D
0x23FF6C
0x23FF6B
0x23FF6A
0x23FF69
0x23FF68
0x23FF67
0x23FF66
0x23FF65
Biến con trỏ
Kích thước biến con trỏ không phụ thuộc vào kiểu dữ liệu
Truy cập vào giá trị của vùng nhớ đang trỏ bởi con trỏ: dùng toán
tử *
100
int A;
int *pInt;
A=5;
*pInt là giá trị vùng nhớ trỏ bởi con trỏ pInt
0x23FF74
0x23FF74
pInt = &A;
*pInt = 7;
int *p2;
p2 = pInt;
*p2 = 100;
int A=5;
int *pInt;
pInt = &A;
printf("Dia chi A = %#x, Gia tri pInt = %#x Dia chi pInt = %#x\n",
&A, pInt, &pInt);
printf("Gia tri A = %d, gia tri vung nho tro boi pInt = %d\n",A,*pInt);
*pInt = 7;
printf("Gan *pInt = 7\n");
printf("Gia tri A = %d, gia tri vung nho tro boi pInt = %d\n",A,*pInt);
c
'('
Biến con trỏ
Biến con trỏ trong biểu thức
#include <stdio.h>
int main (void)
{
int i1, i2;
int *p1, *p2;
i1 = 5;
p1 = &i1;
i2 = *p1 / 2 + 10;
p2 = p1;
#include <stdio.h>
int main (void)
{
char_pointer
char c = 'Q';
char *char_pointer = &c;
printf ("%c %c\n", c, *char_pointer);
c = '/';
printf ("%c %c\n", c, *char_pointer);
*char_pointer = '(';
printf ("i1 = %i, i2 = %i, *p1 = %i, *p2 = %i\n",
i1, i2, *p1, *p2);
printf ("%c %c\n", c, *char_pointer);
return 0;
return 0;
}
}
3
1/26/2011
Con trỏ hằng và hằng con trỏ
char c = 'X';
Khai báo biến con trỏ thông thường
char *charPtr = &c;
charPtr là hằng con trỏ, nó không thể thay đổi được
giá trị (không thể trỏ vào ô nhớ khác)
Có thể thay đổi giá trị của ô nhớ con trỏ đang trỏ đến
char * const charPtr = &c;
charPtr = &d; // not valid
charPtr là con trỏ hằng (con trỏ tới 1 hằng số)
không thể thay đổi giá trị ô nhớ trỏ tới bởi con trỏ
(có thể cho con trỏ trỏ sang ô nhớ khác)
const char *charPtr = &c;
*charPtr = 'Y'; // not valid
Con trỏ và cấu trúc
const char * const *charPtr = &c;
Hằng con trỏ trỏ tới hằng số: không thay đổi được
cả giá trị con trỏ và giá trị ô nhớ mà nó trỏ đến
todaysDate
.month
11
Con trỏ và cấu trúc
struct date
{
Con trỏ và cấu trúc
Truy cập vào trường biến cấu trúc thông qua con trỏ
(* TênConTrỏ).TênTrường
.date
27
.year
2010
int month;
int day;
int year;
};
TênConTrỏ‐>TênTrường
datePtr
todaysDate
.month
1
1
datePtr = &todaysDate;
datePtr‐>month = 1;
(*datePtr).day = 1;
datePtr‐>year = 2011;
.date
.year
struct date todaysDate ={11,27,2010};
2011
struct date *datePtr;
datePtr = &todaysDate;
datePtr
4
1/26/2011
#include <stdio.h>
struct intPtrs
int main (void)
{
Con trỏ và cấu trúc
Cấu trúc chứa con trỏ
int *p1;
int *p2;
};
{
struct date
{
int month;
#include <stdio.h>
int main (void)
{
pointers
p1
int day;
int year;
struct intPtrs
{
};
p2
struct date today = {11,27,2010}, *datePtr;
datePtr = &today;
int *p1;
int *p2;
100
‐97
printf ("Today's date is %i/%i/%.2i.\n",datePtr‐>month,
};
i1
i2
datePtr‐>day, datePtr‐>year % 100);
struct intPtrs pointers;
int i1 = 100, i2;
pointers.p1 = &i1;
pointers.p2 = &i2;
*pointers.p2 = ‐97;
datePtr‐>month = 1;
(*datePtr).day = 1;
datePtr‐>year = 2011;
printf ("Today's date is %i/%i/%.2i.\n",datePtr‐>month,
printf ("i1 = %i, *pointers.p1 = %i\n", i1, *pointers.p1);
printf ("i2 = %i, *pointers.p2 = %i\n", i2, *pointers.p2);
return 0;
datePtr‐>day, datePtr‐>year % 100);
return 0;
}
}
#include <stdio.h>
Con trỏ và cấu trúc
int main (void)
{
Danh sách liên kết – linked list: một trong những cấu trúc
phức tạp được xây dựng từ quan hệ con trỏ và cấu trúc
struct node
{
int value;
struct entry *pNext;
N1
value
5
};
pNext
struct node
{
struct node N1, N2, N3;
int i;
int value;
struct node *pNext;
N1.value = 5; N2.value = 7; N3.value = ‐100;
7
N2
N3
value
N1.pNext = &n2;
N2.pNext = &n3;
i = N1.pNext‐>value;
printf ("%i ", i);
printf ("%i\n", N2.pNext‐>value);
return 0;
};
pNext
‐100
value
pNext
}
5
1/26/2011
Con trỏ và hàm
Tham số của hàm có thể là con trỏ, và hàm có thể trả về
giá trị kiểu con trỏ
Thay đổi giá trị
vùng nhớ
void Absolute(int *x)
{
if(*x<0)
}
int main()
{
Truyền vào
là địa chỉ
Con trỏ và hàm
int a=‐6;
printf("Before call function Absolute, a = %d\n",a);
printf("After call function Absolute, a = %d\n",a);
}
void Exchange(int x, int y)
{
Con trỏ và hàm
Con trỏ và hàm
//exchange value of x and y, huh ?
int tmp;
tmp=x;
x=y;
y=tmp;
Giá trị các biến
không thay đổi?
Cách truyền tham số của hàm
}
Khi khai báo hàm, các tham số của hàm là tham số hình thức
Khi gọi hàm, ta truyền vào các giá trị, biến, đó là các tham số
thực sự
Một bản copy của các tham số thực sự được gán cho các
tham số hình thức của hàm, do đó mọi thay đổi giá trị trên
các tham số hình thức trong khi thực hiện hàm sẽ bị mất sau
khi hàm thực hiện xong (giá trị tham số thực sự không đổi)
int main()
{
int x=5, y=16;
printf("Before function call: x = %d, y = %d\n",x,y);
Exchange(x,y);
printf("After function call: x = %d, y = %d\n",x,y);
return 0;
}
6
1/26/2011
void Exchange2(int *x, int *y)
{
Con trỏ và hàm
Con trỏ và hàm
//it really exchange value of x and y
int tmp;
tmp=*x;
*x=*y;
*y=tmp;
}
Khi truyền tham số cho hàm là con trỏ
Một bản copy của con trỏ cũng được tạo ra và gán cho tham số
hình thức của hàm.
Cả bản copy và con trỏ thực này đều cùng tham chiếu đến một
vùng nhớ duy nhất, nên mọi thay đổi giá trị vùng nhớ đó (dù
dùng con trỏ nào) là như nhau giống và được lưu lại
int main()
{
Sau khi kết thúc thực hiện hàm, giá trị của con trỏ không đổi,
nhưng giá trị vùng nhớ mà con trỏ trỏ đến có thể được thay
đổi (nếu trong hàm ta thay đổi giá trị này)
int x=5,y=16;
printf("Before function call: x = %d, y = %d\n",x,y);
Exchange2(&x,&y);
printf("After function call: x = %d, y = %d\n",x,y);
Truyền tham số con trỏ khi ta muốn giá trị vùng nhớ được
thay đổi sau khi thực hiện hàm
return 0;
}
struct list
{
Con trỏ và hàm
int data;
struct list *pNext;
};
Hàm trả về con trỏ
struct list * searchList(struct list *pHead, int key)
{
while(pHead!=(struct list*)0) //or NULL
{
if(key==pHead‐>data)
Con trỏ và mảng
return pHead;
else
pHead=pHead‐>pNext;
}
return (struct list*)0; //or NULL
}
7
1/26/2011
Con trỏ và mảng
Con trỏ và mảng
Tên mảng là một con trỏ hằng trỏ vào phần tử đầu tiên
int values[6]={2,5,‐4,7,‐5,12};
của mảng
int *pt;
pt
pt
pt = values; // same as &value[0]
int values[6]={2,5,‐4,7,‐5,12};
values[0]
values[0]
values[1]
values[2]
values[3]
values[4]
values[5]
2
5
7
*(pt)=7; //same as values[0]=7
int *pt;
values[1]
values[2]
values[3]
values[4]
values[5]
5
pt = values; // same as &value[0]
*(pt+3)=25; //same as values[3]=25
‐4
7
2
25
3
12
pt = &values[2];//pt points to
values[2] address
Ta có thể dễ dàng dùng con
trỏ để truy cập vào các phần
tử trong mảng
*pt = 2;//same as values[2]=2;
‐5
12
*(pt+2) = 3;//same as values[4]=3;
Con trỏ và mảng
Con trỏ và mảng
Một số thao tác
Các phép toán quan hệ với con trỏ
int values[6]={2,5,‐4,7,‐5,12};
int *pt;
Có thể sử dụng các toán tử con hệ với kiểu con trỏ
Các phép toán đó sẽ là so sánh các địa chỉ ô nhớ với nhau
Kiểu giá trị trả về là TRUE (khác 0) và FALSE (bằng 0)
pt = values; cho con trỏ trỏ vào phần tử đầu tiên trong mảng
(chứa địa chỉ của phần tử đầu tiên)
pt>= &values[5];
pt==&values[0];
*(pt+i) truy cập tới giá trị phần tử cách phần tử
đang trỏ bởi con trỏ ݅ phần tử
pt=pt+n; //or pt+=n; cho pt trỏ tới địa chỉ của phần tử
cách địa chỉ của phần tử hiện tại ݊ phần tử
pt++;
pt‐‐;
Cho con trỏ dịch chuyển cách 1 phần tử
(tức là sizeof(kieudulieu) ô nhớ)
8
1/26/2011
Con trỏ và mảng
Con trỏ và mảng
Khi truyền vào mảng ta chỉ truyền địa chỉ của phần tử đầu
tiên trong mảng, do đó hàm arraySum có thể viết lại là
int arraySum (int Array[], const int n)
{
int sum = 0, *ptr;
int * const arrayEnd = array + n;
for ( ptr = Array; ptr < arrayEnd; ++ptr )
int arraySum (int *Array, const int n)
{
sum += *ptr;
return sum;
}
int sum = 0;
int * const arrayEnd = Array + n;
for ( ; Array < arrayEnd; ++Array )
sum += *Array;
return sum;
}
int main (void)
{
int values[10] = { 3, 7, ‐9, 3, 6, ‐1, 7, 9, 1, ‐5 };
printf ("The sum is %i\n", arraySum (values, 10));
return 0;
}
Con trỏ và mảng
Con trỏ và mảng
char *days[] = { "Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday" };
void copyString (char to[], char from[])
{
int i;
for ( i = 0; from[i] != '\0'; ++i )
to[i] = from[i];
to[i] = '\0';
}
void copyString (char *to, char *from)
{
for ( ; *from != '\0'; ++from, ++to )
*to = *from;
*to = '\0';
}
9
1/26/2011
Con trỏ và mảng
Sử dụng con trỏ để tìm độ dài của xâu ký tự
int stringLength (const char *string)
{
const char *cptr = string;
while ( *cptr )
++cptr;
return cptr ‐ string;
}
Con trỏ và cấp phát bộ nhớ động
int main (void)
{
printf ("%i ", stringLength ("stringLength test"));
printf ("%i ", stringLength (""));
printf ("%i\n", stringLength ("complete"));
return 0;
}
Con trỏ và cấp phát bộ nhớ động
Con trỏ và cấp phát bộ nhớ động
Cấp phát tĩnh
Trong nhiều trường hợp tại thời điểm lập trình ta chưa
biết trước kích thước bộ nhớ cần dùng để lưu trữ dữ liệu.
Ta có thể:
Khai báo mảng với kích thước tối đa có thể tại thời điểm biên
dịch (cấp phát bộ nhớ tĩnh)
Sử dụng mảng với kích thước biến đổi tại thời điểm chạy (chỉ
có trong C99)
Sử dụng mảng cấp phát bộ nhớ động
Kích thước bộ nhớ cấp phát được xác định ngay tại thời điểm
biên dịch chương trình và không thể thay đổi trong quá trình
chạy chương trình
Việc quản lý và thu hồi bộ nhớ được thực hiện tự động, người
lập trình không cần quan tâm
Sẽ là rất lãng phí bộ nhớ nếu không dùng hết dung lượng được
cấp
Bộ nhớ được lấy từ phần DATA, do đó dung lượng bộ nhớ
được cấp phát tĩnh là có giới hạn
int A[1000];
double B[1000000]; //not enough memory
10
1/26/2011
Con trỏ và cấp phát bộ nhớ động
Con trỏ và cấp phát bộ nhớ động
Cấp phát bộ nhớ động trong C: dùng 2 hàm malloc và calloc
Cấp phát bộ nhớ động
Bộ nhớ được cấp phát tại thời điểm thực hiện chương trình,
nên có thể thay đổi được trong mỗi lần chạy
Việc quản lý và thu hồi bộ nhớ sẽ do người lập trình đảm
nhiệm
(trong thư viện <stdlib.h>)
Hàm trả về địa chỉ của ô nhớ đầu tiên trong vùng nhớ xin cấp phát,
do đó dùng con trỏ để chứa địa chỉ này
Trả về con trỏ NULL nếu cấp phát không thành công
pointer=(dataType*) calloc(sizeofAnElement, noElements);
pointer = (dataType*) malloc(sizeofAnElement * noElements);
Tiết kiệm bộ nhớ hơn so với cấp phát tĩnh (vì chỉ cần cấp phát
đủ dùng)
Bộ nhớ cấp phát được lấy ở phần bộ nhớ rỗi (HEAP) nên dung
lượng bộ nhớ có thể cấp phát lớn hơn so với cấp phát tĩnh
Nếu không thu hồi bộ nhớ sau khi dùng xong thì sẽ dẫn đến rò
rỉ bộ nhớ (memory leak), có thể gây ra hết bộ nhớ
double *pt;
pt = (double *) calloc(sizeof(double),10000);
int *pInt;
pInt = (int*) malloc(sizeof(int)*10000);
Con trỏ và cấp phát bộ nhớ động
Con trỏ và cấp phát bộ nhớ động
#include <stdlib.h>
Hàm calloc
Cần 2 tham số là kích thước 1 phần tử (theo byte) và số lượng
phần tử
Khi cấp phát sẽ tự động đưa giá trị các ô nhớ được cấp phát về 0
#include <stdio.h>
...
int *intPtr;
...
Hàm malloc
Chỉ cần 1 tham số là kích thước bộ nhớ (theo byte)
Không tự đưa giá trị các ô nhớ về 0
intptr = (int *) calloc (sizeof (int), 1000);
if ( intPtr == NULL )
{
fprintf (stderr, "calloc failed\n");
exit (EXIT_FAILURE);
}
Hàm sizeof
Trả về kích thước của 1 kiểu dữ liệu, biến (tính theo byte)
11
1/26/2011
Con trỏ và cấp phát bộ nhớ động
Giải phóng bộ nhớ sau khi đã sử dụng xong: hàm free
Con trỏ và cấp phát bộ nhớ động
struct entry
{
int value;
struct entry *next;
};
free(pointer);
Trong đó pointer là con trỏ chứa địa chỉ đầu của vùng nhớ đã
cấp phát
struct entry *addEntry (struct entry *listPtr)
{
int *intPtr;
...
intptr = (int *) calloc (sizeof (int), 1000);
....
// find the end of the list
while ( listPtr‐>next != NULL )
listPtr = listPtr‐>next;
// get storage for new entry
listPtr‐>next = (struct entry *) malloc (sizeof (struct entry));
// add null to the new end of the list
if ( listPtr‐>next != NULL )
(listPtr‐>next)‐>next = (struct entry *) NULL;
return listPtr‐>next;
free(intptr);
}
12
Bạn đang xem tài liệu "Bài giảng Tin học đại cương - Phần 8: Con trỏ", để 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:
- bai_giang_tin_hoc_dai_cuong_phan_8_con_tro.pdf