You are on page 1of 42

Chương 7.

CON TRỎ
1. Giới thiệu
2. Khai báo và khởi tạo
3. Toán tử
4. Truyền đối số cho hàm
5. Các phép toán học con trỏ
6. Con trỏ với mảng
7. Cấp phát vùng nhớ động
1. Giới thiệu con trỏ
• Con trỏ là kiểu dữ liệu lưu trữ địa chỉ của các vùng dữ
liệu trong bộ nhớ máy tính
• Kiểu con trỏ cho phép:
• Truyền tham số kiểu địa chỉ
• Biểu diễn các kiểu, cấu trúc dữ liệu động
• Lưu trữ dữ liệu trong vùng nhớ heap
• Rất mạnh nhưng khó
• Truyền tham chiếu
• Đặc biệt hay sử dụng trong mảng và chuỗi
2. Khai báo và khởi tạo
• Giá trị của biến con trỏ là địa chỉ ô nhớ
• Con trỏ chứa địa chỉ của biến (chứa giá trị)
• Con trỏ tham chiếu gián tiếp đến giá trị
2. Khai báo và khởi tạo
• Khai báo

int *pi;

long int *p;

float *pf;

char c, d, *pc; /* c và d kiểu char


pc là con trỏ đến char */

double *pd, e, f; /* pd là con trỏ đến double


e and f are double */

char *start, *end;/*khai báo 2 biến con trỏ*/


2. Khai báo và khởi tạo
• Khởi tạo:
Không thể khởi tạo trực tiếp
Chỉ có thể gán giá trị NULL hoặc 0 (không trỏ đến gì)
int *ptr = NULL;
3. Toán tử
• Toán tử “&”
“&”: toán tử lấy địa chỉ của 1 biến
Địa chỉ của tất cả các biến trong chương trình đều đã được
chỉ định từ khi khai báo

char x = 'z';

int main()
{
char y = 'a';
char *p;
p = &y;
p = &x;
return 0;
}
3. Toán tử
• Toán tử “*”
• “*”: toán tử truy xuất giá trị của vùng nhớ được quản lý
bởi con trỏ

char x = 'z';
int main()
{
char y = 'a';
char *p;
p = &y;
printf("%c\n", *p);
p = &x;
printf("%c\n", *p);
return 0;
}
VD: Cho biết kết quả đoạn CT sau nếu địa chỉ của x
và y lần lượt là 0029F758 và 0029F800
char x = ‘ @';
int main(void)
{
char y = ‘ %';
char *p;
p = &y;
printf("p = %p\n&y = %p\ny = %c
\n*p = %c\n", p, &y, y, *p);
p = &x;
printf("\np = %p\n&x = %p\nx = %c
\n*p = %c\n", p, &x, x, *p);
getch();
}
VD: Cho biết kết quả in ra màn hình nếu địa chỉ của
biến a là 0029F758
int a;
int *aPtr;
a = 7;
aPtr = &a;
printf("The address of a is %p\nThe value of
aPtr is %p", &a, aPtr);
printf("\n\nThe value of a is %d\nThe value of
*aPtr is %d", a, *aPtr);
printf("\n\n\n&*aPtr = %p\n*&aPtr = %p\n",
&*aPtr, *&aPtr);
• Con trỏ NULL
Giá trị đặc biệt để chỉ rằng con trỏ không quản
lý vùng nào. Giá trị này thường được dùng để chỉ một
con trỏ không hợp lệ

#include <stdio.h>
int main()
{
int i = 13;
short *p = NULL;
if (p == NULL)
printf(“Con trỏ không hợp lệ!\n");
else
printf(“Giá trị : %0xi\n", *p);
return 0;
}
3. Toán tử
• Chú ý toán tử gán “ = “
• VD:

int i = 10, j = 14;


int *p = &i;
int *q = &j;

*p = *q;

int i = 10, j = 14;


int *p = &i;
int *q = &j;

p = q;
BT 1. Cho biết kết quả in ra màn hình

inta,b;
int *p;
p = &a;
*p = 5;
p = &b;
*p = 7;
printf(“a = %d\nb = %d\n", a, b);
getch();
4. Truyền đối số cho hàm
• Truyền tham chiếu
• Truyền địa chỉ đối số sử dụng toán tử “&”
• Cho phép thay đổi vị trí thực trong bộ nhớ
• Mảng không sử dụng toán tử “&” vì bản thân mảng đã là
1 con trỏ
• VD:
void double1( int *number )
{
*number = 2 * ( *number );
}

void double2( int number )


{
number = 2 * ( number );
}
#include <stdio.h>
#include <conio.h>
int cubeByValue(int n);
int main(void)
{
int number = 5;
printf("The original value of number is %d",
number);
number = cubeByValue(number);
printf("\nThe new value of number is %d\n",
number);
getch();
}

int cubeByValue(int n)
{ return n * n * n;}
#include <stdio.h>
#include <conio.h>
void cubeByReference(int *nPtr);
int main(void)
{
int number = 5;
printf("The original value of number is %d", number);
cubeByReference(&number);
printf("\nThe new value of number is %d\n", number);
getch();
}

void cubeByReference(int *nPtr)


{
* nPtr = *nPtr * *nPtr * *nPtr;
}
5. Sử dụng const qualifier
• Từ định kiểu (quatifier) có 3 dạng: const, volatile, restrict
const: Biến không được thay đổi
Nếu cố thay đổi sẽ phát sinh lỗi
Sử dụng trong trường hợp biến không cần hiệu chỉnh
• Const pointers
Trỏ tới vị trí không đổi trong bộ nhớ
Phải được định nghĩa khi khởi tạo
int *const myPtr = &x;
• Type int *const – constant pointer
const int *myPtr = &x;
• Modifiable pointer to a const int
const int *const Ptr = &x;
• const pointer to a const int
• Sử dụng const qualifier giúp hạn chế sai sót, giảm thời
gian gỡ lỗi, dễ bảo dưỡng, nâng cấp
• Có một số trình biên dịch không thực thi const qualifier
• Nếu cần truyền 1 biến không nên thay đổi thì nên khai
báo const để chắc chắn nó không bị vô tình thay đổi
• Chỉ một giá trị có thể được thay đổi trong một hàm gọi
khi sử dụng truyền tham trị. Giá trị đó phải được gán từ
giá trị trả lại của hàm. Để sửa đổi nhiều giá trị trong một
hàm gọi, phải sử dụng truyền tham chiếu
• Trước khi sử dụng một hàm cần kiểm tra hàm nguyên
mẫu để xem hàm có làm thay đổi giá trị truyền cho nó
#include <stdio.h>
void f(const int *xPtr);
int main(void)
{
int y;
f(&y);
}
void f(const int *xPtr)
{
*xPtr = 100;/*error:cannot modify a const object*/
}
VD: Sắp xếp Bubble sd truyền tham chiếu

• Viết hàm hoán vị sử dụng truyền tham chiếu


• Viết hàm sắp xếp sử dụng truyền tham chiếu
• Viết chương trình nhập mảng. Gọi hàm sắp xếp, in
ra mảng đã sắp xếp
void swap(int *element1Ptr, int *element2Ptr)
{
int hold = *element1Ptr;
* element1Ptr = *element2Ptr;
* element2Ptr = hold;
}
void bubbleSort(int *array, int size)
{
void swap(int *element1Ptr, int *element2Ptr);
int pass, j;
for (pass = 0; pass < size - 1; pass++)
{
for (j = 0; j < size - 1; j++)
{
if (array[j] > array[j + 1])
{swap(&array[j], &array[j + 1]);}
}
}
}
int main(void)
{
int a[SIZE] = { 2, 6, 4, 8, 10, 12, 89, 68, 45,
37 };
int i;
printf("Data items in original order\n");
for (i = 0; i < SIZE; i++)
{ printf("%4d", a[i]);}
bubbleSort(a, SIZE);
printf("\nData items in ascending order\n");
for (i = 0; i < SIZE; i++)
{printf("%4d", a[i]);}
printf("\n");
getch();
}
6. Toán tử Sizeof
• Sizeof
Trả lại kích thước bộ nhớ được cấp phát cho phần tử ở
trong Sizeof () bằng byte
Đối với mảng: (Kích thước của 1 phần tử)*(số phần tử)
• VD: Nếu sizeof (int) = 4, cho biết kết quả in ra màn hình đoạn
CT sau
int myArray[ 10 ];
printf( "%d", sizeof( myArray ) );
• Sizeof có thể được sử dụng cho
Tên biến
Tên loại dữ liệu
Tên hằng số
VD: In kích thước các kiểu data
#include <stdio.h>
#include <conio.h>
int main(void)
{
char c; short s; int i; long l; float f; double d;
long double ld; int array[20]; int *ptr = array;
printf(" sizeof c = %d\tsizeof(char) = %d"
"\n sizeof s = %d\tsizeof(short) = %d"
"\n sizeof i = %d\tsizeof(int) = %d"
"\n sizeof l = %d\tsizeof(long) = %d"
"\n sizeof f = %d\tsizeof(float) = %d"
"\n sizeof d = %d\tsizeof(double) = %d"
"\n sizeof ld = %d\tsizeof(long double) = %d"
"\n sizeof array = %d"
"\n sizeof ptr = %d\n",
sizeof c, sizeof(char), sizeof s, sizeof(short), sizeof i,
sizeof(int), sizeof l, sizeof(long), sizeof f,
sizeof(float), sizeof d, sizeof(double), sizeof ld,
sizeof(long double), sizeof array, sizeof ptr);
getch();
}
VD:
#include <stdio.h>
#include <conio.h>
size_t getSize(float *ptr); /*dùng để lưu size của
bất kỳ object nào*/
int main(void)
{
float array[20];
printf("The number of bytes in the array is %d"
"\nThe number of bytes returned by getSize is %d\n",
sizeof(array), getSize(array));
getch();
}
size_t getSize(float *ptr)
{return sizeof(ptr);}
7. Phép so sánh và phép toán
con trỏ
• Phép toán
Tăng hoặc giảm
Cộng và trừ số integer
Trừ con trỏ
• Trừ con trỏ (thực hiện trên cùng 1 mảng)
vPtr2 = v[2];
vPtr = v[0];
vPtr2 - vPtr = 2
• So sánh con trỏ (thực hiện trên cùng 1 mảng)
<, >, ==
Con trỏ trỏ tới vùng nhớ có địa chỉ nhỏ hơn là
con trỏ nhỏ hơn
++pa hoặc pa++ Trỏ đến số nguyên kế tiếp đứng sau a

–pa hoặc pa– Trỏ đến số nguyên kế tiếp đứng trước a

pa + i Trỏ đến số nguyên thứ i đứng sau a

pa – i Trỏ đến số nguyên thứ i đứng trước a

++*pa hoặc Sẽ tăng giá trị a thêm 1 nếu pa đang trỏ


(*pa)++ đến a.
Sẽ giảm giá trị a bớt 1 nếu pa đang trỏ
–*pa hoặc (*pa)–
đến a.
Sẽ tác động đến giá trị của số nguyên kế
*pa++
tiếp sau a
8. Mối quan hệ giữa con trỏ và mảng
• Tên mảng giống như một constant pointer
• Khai báo mảng b[5] và con trỏ bPtr
bPtr = &b[0]
• Truy xuất phần tử b[3] bằng cách *(bPtr+3) hoặc
bPtr[3] hoặc *(b+3)
BT: Cho biết kết quả in ra
int main(void)
{
int a[5] = { 5, 10, 15, 30 };
int *p;
p = a;
printf("gia tri = %d\n", *(p+2));
getch();
}
9. Cấp phát vùng nhớ động
• Có thể chỉ định vùng mới cho 1 con trỏ quản lý bằng
các lệnh hàm malloc (memory-alloc), calloc (clear-
alloc)
• Vùng nhớ do lập trình viên chỉ định phải được giải
phóng bằng lệnh free (malloc, calloc)
• Các hàm nằm trong thư viện chuẩn stdlib.h
Malloc( )
• Hàm cấp phát bộ nhớ thời điểm runtime
• Xác định rõ số byte trong đối số
VD: int *p = (int*) malloc(100);
• Hàm trả về địa chỉ byte đầu tiên của vùng nhớ (100 bytes)
được cấp phát (vùng nhớ Heap)
• Nếu cấp phát thất bại trả về NULL
• Để tránh khác biệt về kích thước con trỏ trong hệ thống nên
sử dụng
int *p = (int*) malloc(n*sizeof(int));
Calloc ( )
• Dùng để cấp phát bộ nhớ động
• Vùng nhớ được cấp phát được đưa về 0
• Có 2 đối số, số phần tử mảng và kích thước mỗi phần tử
VD: int *p = (int*) calloc(n,sizeof(int));
• Sau khi cấp phát, phải giải phóng vùng nhớ bằng hàm
free(pointer)
Realloc( )
• Cho phép sử dụng lại vùng nhớ đã được cấp phát
bởi malloc và calloc
• Có 2 đối số: con trỏ trả về bởi malloc và calloc, số
byte được cấp phát lại
• int *p = (int*) realloc(p,n*sizeof(int));
VD
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
int main(void){
int *p, i, n;
printf("Input number of integer you want to storage: ");
scanf("%d", &n);
p = (int*) malloc(n*sizeof(int));
for (i = 0; i<n; i++)
{printf("Enter number %d: ", i + 1);
scanf("%d", &p[i]);} // scanf_s("%d", &*(p+i));
printf("\nArray of integers:\n");
for (i = 0; i<n; i++)
{printf("%d ", p[i]);} // printf("%d ", *(p+i));
free(p);
getch();
}
BT 1
#include<stdio.h>
#include<conio.h>
void main(void)
{
int a[] = { 1, 5, 2, 7, 4, 6, 10 };
int i, *p;
p = a;
*p = 4;
for (i = 0; i < 7; i++)
{
if (*(p + i) % 2 == 0)
{continue;}
else
{printf("%d ", *(p + i));}
}
_getch();
}
BT 2
#include<stdio.h>
#include<conio.h>
void main()
{
int a = 1;
int *p, b = 2;
p = &a;
(*p)++;
p = &b;
*p += 2;
printf("%d", a);
getch();
}
BT 3
#include <stdio.h>
#include <conio.h>
void main(void)
{
int a[5] = { 2, 4, 11, 7, 9 };
int i, j;
int *p = a;
for (i = 0; i < 4; i++)
{
j = i + 1;
*(p + i) = *(p + j);
}
for (i = 0; i < 5; i++)
{printf("%d\t", a[i]);}
getch();
}
#include <stdio.h>
#include <conio.h>
void main(void)
{
int a[4];
int i,tong = 0;
int *p = a;
for (i = 0; i < 4; i++)
{*(p + i) = 1;}
for (i = 0; i < 4; i++)
{tong = tong + *(a + i);}
printf("%.2f\t%d", (float)1/tong, *p);
getch();
}
Viết chương trình C cho phép người
dùng nhập vào mảng n phần tử số
nguyên dùng con trỏ và cấp phát vùng
nhớ động.
In ra tất cả số nguyên âm có trong
mảng. Nếu không có in ra không có số
nguyên âm
Viết hàm kiểm tra tính đối xứng của
mảng vừa nhập
Sắp xếp các giá trị ở vị trí lẻ theo thứ tự
tăng dần
BTVN:
Viết chương trình C cho phép người dùng nhập
vào mảng n phần tử số nguyên dùng con trỏ và
cấp phát vùng nhớ động.
Viết hàm kiểm tra tính đối xứng của mảng vừa
nhập
Sắp xếp các giá trị ở vị trí lẻ theo thứ tự tăng
dần

You might also like