Dữ liệu kiểu mảng
• Khái niệm
– Là một kiểu dữ liệu có cấu trúc do người lập
trình định nghĩa.
– Biểu diễn một dãy các biến có cùng kiểu. Ví dụ:
dãy các số nguyên, dãy các ký tự
– Kích thước được xác định ngay khi khai báo
và không bao giờ thay đổi.
– NNLT C luôn chỉ định một khối nhớ liên tục
cho một biến kiểu mảng.
7Khai báo biến mảng (tường
minh)
• Tường minh
– , , : số lượng phần tử của mỗi chiều.
• Lưu ý
– Phải xác định cụ thể (hằng) khi khai
báo.
– Mảng nhiều chiều: =
N1*N2* *Nn
– Bộ nhớ sử dụng = *sizeof(
80 trang |
Chia sẻ: thanhle95 | Lượt xem: 506 | Lượt tải: 1
Bạn đang xem trước 20 trang tài liệu Bài giảng Lập trình cơ bản: Ngôn ngữ lập trình C - Đỗ Thị Mai Hường, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Lập trình cơ bản:
Ngôn ngữ lập trình C
Đỗ Thị Mai Hường
Bộ môn Hệ thống thông tin
Khoa Công nghệ thông tin
Mảng, con trỏ và xâu ký
tự
Tài liệu tham khảo
Chương 7 - Phần 13
• Kỹ thuật lập trình C: cơ sở và nâng cao, Phạm
Văn Ất, Nhà xuất bản KHKT – Chương 6
• The C programming language 2nd Edition,
Brian Kernighan and Dennis Ritchie, Prentice
Hall Software Series – Chương 4
• The C programming language 2nd Edition,
Brian Kernighan and Dennis Ritchie, Prentice
Hall Software Series – Chương 5
Nội dung
Chương 7 - Phần 1
• Mảng một chiều
• Mảng hai chiều
• Con trỏ và phép toán trên con trỏ
– Khai báo con trỏ
– Phép gán con trỏ
– Truy xuất giá trị qua con trỏ
• Con trỏ và mảng
• Cấp phát vùng nhớ cho con trỏ
• Xâu ký tự
– Khái niệm
– Khởi tạo
– Các thao tác trên xâu ký tự
4
PHẦN 1. MẢNG MỘT CHIỀU VÀ
NHIỀU CHIỀU
5
Mảng một chiều
• Khái niệm
• Khai báo
• Truy xuất dữ liệu
6
Dữ liệu kiểu mảng
• Khái niệm
– Là một kiểu dữ liệu có cấu trúc do người lập
trình định nghĩa.
– Biểu diễn một dãy các biến có cùng kiểu. Ví dụ:
dãy các số nguyên, dãy các ký tự
– Kích thước được xác định ngay khi khai báo
và không bao giờ thay đổi.
– NNLT C luôn chỉ định một khối nhớ liên tục
cho một biến kiểu mảng.
7
Khai báo biến mảng (tường
minh)
• Tường minh
– , , : số lượng phần tử của mỗi chiều.
• Lưu ý
– Phải xác định cụ thể (hằng) khi khai
báo.
– Mảng nhiều chiều: =
N1*N2**Nn
– Bộ nhớ sử dụng = *sizeof(<kiểu cơ
[];
[][][];
8
01
2
Khai báo biến mảng (tường
minh)
• Ví dụint Mang1Chieu[10];
0 1 2 3 4 7 85 6 9
Mang1Chieu
int Mang2Chieu[3][4];
0 1 2 3 4 7 85 6 9
Mang2Chieu
10 11
9
Khai báo biến mảng (không
tường minh)
• Cú pháp
– Không tường minh (thông qua khai báo kiểu)
• Ví dụ
Chương 7 - Phần 1
typedef [];
typedef [][];
;
typedef int Mang1Chieu[10];
typedef int Mang2Chieu[3][4];
Mang1Chieu m1, m2, m3;
Mang2Chieu m4, m5;
10
Số phần tử của mảng
• Phải xác định cụ thể số phần tử ngay lúc
khai báo, không được sử dụng biến hoặc
hằng thường
• Nên sử dụng chỉ thị tiền xử lý #define để
int n1 = 10; int a[n1];
const int n2 = 20; int b[n2];
#define n1 10
#define n2 20
int a[n1]; // int a[10];
int b[n1][n2]; // int b[10][20];
11
Khởi tạo giá trị cho mảng lúc
khai báo
• Gồm các cách sau
– Khởi tạo giá trị cho mọi phần tử của mảng
– Khởi tạo giá trị cho một số phần tử đầu mảng
int a[4] = {2912, 1706, 1506, 1904};
2912 1706 1506 1904
0 1 2 3
a
int a[4] = {2912, 1706};
2912 1706 0 0
0 1 2 3
a
12
Khởi tạo giá trị cho mảng lúc
khai báo
• Gồm các cách sau
– Khởi tạo giá trị 0 cho mọi phần tử của mảng
– Tự động xác định số lượng phần tử
int a[4] = {0};
0 0 0 0
0 1 2 3
a
int a[] = {2912, 1706, 1506, 1904};
2912 1706 1506 1904
0 1 2 3
a
13
Truy xuất đến một phần tử
• Thông qua chỉ số
• Ví dụ
– Cho mảng như sau
– Các truy xuất
• Hợp lệ: a[0], a[1], a[2], a[3]
• Không hợp lệ: a[-1], a[4], a[5],
=> Cho kết thường không như mong muốn!
[][][]
int a[4];
0 1 2 3
14
Gán dữ liệu kiểu mảng
• Không được sử dụng phép gán thông
thường mà phải gán trực tiếp giữa các phần
tử tương ứng
• Ví dụ
Chương 7 - Phần 1
#define MAX 3
typedef int MangSo[MAX];
MangSo a = {1, 2, 3}, b;
b = a; // Sai
for (int i = 0; i < 3; i++) b[i] = a[i];
15
Ví dụ
• Nhập mảng có n phần tử kiểu nguyên, in ra các phần
tử của mảng
16
#include
#define MAX 1000
void main()
{
int ary[MAX];
int i,n;
printf(“Nhap n = ”);
scanf(“%d”,&n);
for(i=0; i<n; i++) {
printf(“\n Enter value: %d : ”, i+1);
scanf(“%d”,&ary[i]);
}
for(i=1; i<10; i++) printf(“a[%d]=%d\n“,i,
ary[i])
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
Ví dụ
• Nhập 2 mảng có n phần tử kiểu nguyên, tính và in ra
mảng tổng
17
#include
#define MAX 1000
void main()
{
int ary1[MAX], ary2[MAX], sum[MAX];
int i,n;
printf(“Nhap n = ”);scanf(“%d”,&n);
for(i=0; i<n; i++) {
printf(“\n Enter value: %d : ”, i+1);
scanf(“%d”,&ary1[i]);
}
for(i=0; i<n; i++) {
printf(“\n Enter value: %d : ”, i+1);
scanf(“%d”,&ary2[i]);
}
for(i=1; i<10; i++)sum[i]=ary1[i]+ary2[i];
for(i=1; i<10; i++) printf(“a[%d]=%d\n“,i, sum[i])
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
Mảng hai chiều
• Khái niệm
• Khai báo
• Truy xuất dữ liệu
18
Ma Trận
0
m-1
0 1 n-1
Am,n
0
n-1
An
0 n-1
19
Ma Trận
0
n-1
An
0 n-1
0
n-1
0 n-1
0
n-1
0 n-1
dòng = cột dòng > cột dòng < cột
0
n-1
An
0 n-1
0
n-1
0 n-1
0
n-1
0 n-1
dòng + cột = n-1 dòng + cột > n-1 dòng + cột < n-1
20
Khai báo biến mảng 2 chiều
• Cú pháp
– Tường minh
– Không tường minh (thông qua kiểu)
[][];
typedef [][];
;
, ;
21
Khai báo biến mảng 2 chiều
• Ví dụ
– Tường minh
– Không tường minh (thông qua kiểu)
int a[10][20], b[10][20];
int c[5][10];
int d[10][20];
typedef int MaTran10x20[10][20];
typedef int MaTran5x10[5][10];
MaTran10x20 a, b;
MaTran11x11 c;
MaTran10x20 d;
22
Truy xuất đến một phần tử
• Thông qua chỉ số
• Ví dụ
– Cho mảng 2 chiều như sau
– Các truy xuất
• Hợp lệ: a[0][0], a[0][1], , a[2][2], a[2][3]
• Không hợp lệ: a[-1][0], a[2][4], a[3][3]
[][]
int a[3][4];
0
1
2
0 1 2 3
23
Gán dữ liệu kiểu mảng
• Không được sử dụng phép gán thông thường
mà phải gán trực tiếp giữa các phần tử
• Ví dụ
int a[5][10], b[5][10];
b = a; // Sai
int i, j;
for (i = 0; i < 5; i++)
for (j = 0; j < 10; j++)
b[i][j] = a[i][j];
24
Ví dụ
• Nhập mảng có n dòng, m cột các phần tử kiểu nguyên, in các phần tử
của mảng ra màn hình
25
#include
#define MAX 1000
void main()
{
int a[MAX][MAX];
int i,n,m;
//Nhap mang
printf(“Nhap n = ”);scanf(“%d”,&n);
printf(“Nhap m = ”);scanf(“%d”,&m);
for (i=0; i<m; i++)
for (j=0; j<n; j++)
{
printf(“Nhap a[%d][%d]: ”, i, j);
scanf(“%d”, &a[i][j]);
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
Ví dụ
• Nhập mảng có n dòng, m cột các phần tử kiểu nguyên, in các phần tử của
mảng ra màn hình (tiếp)
26
// In cac phan tu cua mang
for (i=0; i<m; i++)
{
for (j=0; j<n; j++)printf(“%d ”, a[i][j]);
printf(“\n”);}
}
16.
17.
18.
19.
20.
21.
Ví dụ
• Nhập 2 mảng A, B có n dòng, m cột các phần tử kiểu nguyên, tính và in
các phần tử của mảng C = A + B
27
#include
#define MAX 1000
void main(){
int a[MAX][MAX], b[MAX][MAX], c[MAX][MAX];int i,n,m;
//Nhap 2 mang
printf(“Nhap n = ”);scanf(“%d”,&n);
printf(“Nhap m = ”);scanf(“%d”,&m);
for (i=0; i<m; i++)
for (j=0; j<n; j++){
printf(“Nhap a[%d][%d]: ”, i, j);
scanf(“%d”, &a[i][j]); }
for (i=0; i<m; i++)
for (j=0; j<n; j++){
printf(“Nhap a[%d][%d]: ”, i, j);
scanf(“%d”, &b[i][j]); }
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
Ví dụ
• Nhập 2 mảng A, B có n, m cột các phần tử kiểu nguyên, tính và in các phần
tử của mảng C = A + B (tiếp)
28
// Tinh cac phan tu cua mang C
for (i=0; i<m; i++)
for (j=0; j<n; j++)
c[i][j] = a[i][j] + b[i][j];
// In cac phan tu cua mang C
for (i=0; i<m; i++)
{
for (j=0; j<n; j++)printf(“%d ”, c[i][j]);
printf(“\n”);}
}
16.
17.
18.
19.
20.
21.
22.
23.
24.
Bài tập thảo luận trên lớp
1. Nhập mảng có n phần tử kiểu nguyên, tìm phần tử
lớn nhất, nhỏ nhất của mảng.
2. Nhập mảng có n dòng, m cột phần tử kiểu nguyên,
tìm phần tử lớn nhất, nhỏ nhất của mảng
3. Nhập mảng có n phần tử kiểu nguyên, nhập giá trị x,
tìm xem x có trong mảng không, xác định vị trí xuất
hiện đầu tiên.
4. Nhập mảng có n dòng, m cột phần tử kiểu nguyên,
nhập giá trị x, tìm xem x có xuất hiện trong mảng
không, xác định các vị trí xuất hiện
29
Bài tập
5. Nhập 2 vector có n phần tử kiểu nguyên, kiểm tra 2
vector đó có vuông góc với nhau không?
6. Sắp xếp mảng một chiều bằng các phương pháp
• Phương pháp chọn
• Phương pháp nổi bọt
7. Nhập vào một dãy, tìm phần tử lớn nhất, nhỏ nhất và
các vị trí có phần tử đạt giá trị lớn nhất, nhỏ nhất.
8. Tính và in ra tích hai ma trận vuông cấp n x n.
9. Nhập ma trận A (n x m) và kiểm tra xem có hai cột
đứng cạnh nhau có tổng bằng nhau hay không?30
Bài tập
10.Nhập 2 mảng A(n,m), B(m,n) phần tử kiểu số
thực, tính và in mảng C=A*B
11.Nhập 2 mảng A(n,m), B(m,n) phần tử kiểu số
thực, kiểm tra A có là chuyển vị của B hay
không
12.Nhập A(n,n) với n không giới hạn trước, kiểm
tra A có là ma trận đơn vị không?
13.Xây dụng ma trận A(n,m), sao cho các phần
tử có giá trị theo dạng xoắn ốc (n, m không
31
PHẦN 2. CON TRỎ VÀ CÁC
PHÉP TOÁN
32
Khai báo biến con trỏ.
33
• Cú pháp: *
• Ý nghĩa: Khai báo một biến có tên là Tên con trỏ dùng để chứa địa chỉ của
các biến có kiểu Kiểu dữ liệu.
• Ví dụ 1: Khai báo 2 biến a,b có kiểu int và 2 biến pa, pb là 2 biến con trỏ kiểu
int.
int a, b, *pa, *pb;
• Ví dụ 2: Khai báo biến f kiểu float và biến pf là con trỏ float
float f, *pf;
• Chú ý: Nếu chưa muốn khai báo kiểu dữ liệu mà con trỏ ptr đang chỉ đến, ta
sử dụng:
void *ptr;
Sau đó, nếu ta muốn con trỏ ptr chỉ đến kiểu dữ liệu gì cũng được.
Tác dụng của khai báo này là chỉ dành ra 2 bytes trong bộ nhớ để cấp phát
cho biến con trỏ ptr.
Các thao tác trên con trỏ .
34
Gán địa chỉ của biến cho biến con trỏ:
• Toán tử & dùng để định vị con trỏ đến địa chỉ của một biến
đang làm việc.
• Cú pháp: =&
• Giải thích: Ta gán địa chỉ của biến Tên biến cho con trỏ
Tên biến con trỏ.
Ví dụ: Gán địa chỉ của biến a cho con trỏ pa, gán địa chỉ của
biến b cho con trỏ pb.
pa=&a; pb=&b;
• Lúc này, hình ảnh của các biến trong bộ nhớ được mô tả
Các thao tác trên con trỏ (t)
35
Lưu ý:
• Khi gán địa chỉ của biến tĩnh cho con trỏ cần phải lưu ý
kiểu dữ liệu của chúng.
• Ví dụ sau đây không đúng do không tương thích kiểu:
int Bien_Nguyen;
float *Con_Tro_Thuc;
...
Con_Tro_Thuc=&Bien_Nguyen;
• Phép gán ở đây là sai vì Con_Tro_Thuc là một con trỏ
kiểu float (nó chỉ có thể chứa được địa chỉ của biến kiểu
float); trong khi đó, Bien_Nguyen có kiểu int
Các thao tác trên con trỏ .
36
Nội dung của ô nhớ con trỏ chỉ tới.
• Để truy cập đến nội dung của ô nhớ mà con trỏ chỉ tới, ta sử dụng cú pháp:
*
• Với cách truy cập này thì * có thể coi là một biến có kiểu được mô tả
trong phần khai báo biến con trỏ.
Ví dụ: Ví dụ sau đây cho phép khai báo, gán địa chỉ cũng như lấy nội dung vùng nhớ của
biến con trỏ: int x=100; int *ptr;
ptr=&x;
int y= *ptr;
• Lưu ý: Khi gán địa chỉ của một biến cho một biến con trỏ, mọi sự thay đổi trên nội dung ô
nhớ con trỏ chỉ tới sẽ làm giá trị của biến thay đổi theo (thực chất nội dung ô nhớ và biến
chỉ là một)
Ví dụ
về
con
trỏ
37
#include “stdio.h”
#include “conio.h”
void main()
{
int a,b,*pa,*pb;
a=2;
b=8;
printf("\nGia tri cua bien a=%d \nGia tri cua
bien b=%d ",a,b);
pa=&a;
pb=&b;
printf("\nNoi dung cua o nho con tro pa tro
toi=%d",*pa);
printf("\nNoi dung cua o nho con tro pb tro
toi=%d ",*pb);
*pa=20; /* thay doi gia tri cua *pa*/
*pb=25; /* thay doi gia tri cua *pb*/
printf("\nGia tri moi cua bien a=%d \nGia tri moi
cua bien b=%d “ ,a,b);
/* a, b thay doi theo*/
getch();
}
Con trỏ và mảng một chiều
38
• Trong C có mối quan hệ chặt chẽ giữa con trỏ và mảng: các phần tử của
mảng có thể được xác định nhờ chỉ số hoặc thông qua con trỏ.
Phép toán lấy địa chỉ:
• Giả sử ta có khai báo: double b[20];
phép toán: &b[9] sẽ cho địa chỉ của phần tử b[9].
Tên mảng là một hằng địa chỉ:
• Khi chúng ta khai báo: float a[10]; máy sẽ bố trí bố trí cho mảng a mười
khoảng nhớ liên tiếp.
• Mỗi khoảng nhớ là 4 byte.
• Như vậy, nếu biết địa chỉ của một phần tử nào đó của mảng a, thì ta có thể
dễ dàng suy ra địa chỉ của các phần tử khác của mảng.
• Trong C ta có: a &a[0]
a+i &a[i]
*(a+i) a[i]
Con trỏ trỏ tới các phần tử của mảng một
chiều:
39
• Khi con trỏ pa trỏ tới phần tử a[k] của mảng a thì:
pa+i trỏ tới phần tử thứ i sau a[k], có nghĩa là nó trỏ tới a[k+i].
pa-i trỏ tới phần tử thứ i trước a[k], có nghĩa là nó trỏ tới a[k-i].
*(pa+i) tương đương với pa[i].
• Ví dụ: sau hai câu lệnh:
float a[20],*pa;
pa=&a; // pa=&a[0];
• Khi đó, 4 cách viết sau có tác dụng như nhau và cùng truy cập đến
phần tử thứ i của mảng a:
a[i] *(a+i) pa[i] *(pa+i)
Ví dụ:
Vào số
liệu cho các
phần tử của
một mảng
và tính tổng
các phần tử
của chúng.
40
Cách 1:
#include "stdio.h"
void main()
{
float a[4],tong;
int i;
for (i=0;i<4;++i)
{
printf("\n a[%d]=",i);
scanf("%f",(a+i));
}
tong=0;
for (i=0;i<4;++i)
tong+=a[i];
Ví dụ:
Vào số
liệu cho các
phần tử của
một mảng
và tính tổng
các phần tử
của chúng.
41
Cách 2:
#include "stdio.h"
void main()
{
float a[4],tong, *troa;
int i;
troa=a;
for (i=0;i<4;++i)
{
printf("\n a[%d]=",i);
scanf("%f",troa+i);
}
tong=0;
for (i=0;i<4;++i)
tong+=a[i];
printf("\n Tong cac phan tu mang la:%8.2f
",tong);
}
Ví dụ:
Vào số
liệu cho các
phần tử của
một mảng
và tính tổng
các phần tử
của chúng.
42
Cách 3:
#include "stdio.h"
void main()
{
float a[4],tong,*troa;
int i;
troa=a;
for (i=0;i<4;++i)
{
printf("\n a[%d]=",i);
scanf("%f",troa+i);
}
tong=0;
for (i=0;i<4;++i)
tong+=*(troa+i);
printf("\n Tong cac phan tu mang la:%8.2f
",tong);
}
Con trỏ và mảng nhiều chiều
43
Phép cộng địa chỉ trong mảng hai chiều:
• Giả sử ta có mảng hai chiều a[2][3] có 6 phần tử ứng với sáu địa chỉ liên tiếp trong bộ
nhớ được xếp theo thứ tự sau:
• Tên mảng a biểu thị địa chỉ đầu tiên của mảng.
• Phép cộng địa chỉ : C coi mảng hai chiều là mảng (một chiều) của mảng, như vậy khai
báo
float a[2][3]; thì a là mảng mà mỗi phần tử của nó là một dãy 3 số thực (một hàng
của mảng).
• Vì vậy: a trỏ phần tử thứ nhất của mảng: phần tử a[0][0]
a+1 trỏ phần tử đầu hàng thứ hai của mảng: phần tử a[0][1],
...
a+i trỏ phần tử đầu hàng thứ i của mảng: phần tử a[0][i].
Phần
tử a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]Địa
chỉ 1 2 3 4 5 6
Con trỏ và mảng nhiều chiều (t)
44
Con trỏ và mảng hai chiều:Để lần lượt duyệt trên các phần tử của mảng
hai chiều ta có thể dùng con trỏ như minh hoạ ở ví dụ sau:
float *pa,a[2][3];
pa=(float*)a;
Khi đó:
– pa trỏ tới a[0][0]
– pa+1 trỏ tới a[0][1]
– pa+2 trỏ tới a[0][2]
– pa+3 trỏ tới a[1][0]
– pa+4 trỏ tới a[1][1]
– pa+5 trỏ tới a[1][2]
Ví dụ:
Dùn
g con trỏ
để vào số
liệu cho
mảng hai
chiều
45
Cách 1:
#include "stdio.h"
void main()
{
float a[2][3],*pa;
int i;
pa=(float*)a;
for (i=0;i<6;++i)
scanf("%f",pa+i);
}
Cách 2:
#include "stdio.h"
void main()
{
float a[2][3],*pa;
int i;
for (i=0;i<6;++i)
scanf("%f",(float*)a+i);
Kiểu con trỏ, kiểu địa chỉ, các phép toán trên con
trỏ
46
Kiểu con trỏ và kiểu địa chỉ:
• Con trỏ dùng để lưu địa chỉ của biến. Mỗi kiểu địa chỉ của biến cần có kiểu con trỏ
tương ứng. Phép gán địa chỉ cho con trỏ chỉ có thể thực hiện được khi kiểu địa chỉ phù
hợp với kiểu con trỏ.
• Theo khai báo:
float a[20][30], *pa,*pn[30],(*pm)[30];
• Ta có:
• a là mảng 2 chiều, có 600 phần tử kiểu float. a là địa chỉ kiểu float[30].
• pa là con trỏ float.
• pn là mảng 30 con trỏ kiểu float.
• pm là con trỏ kiểu float [30].
• Như vậy, phép gán:
pa=a; là không hợp lệ (tuy nhiên sẽ có quá trình chuyển kiểu tự động).
• Nhưng phép gán:
pm=a; là hợp lệ.
Ví dụ
về sử
dụng
con
trỏ
47
#include "conio.h"
#include "stdio.h"
void main()
{
float a[2][3]={{1.0,2.0,3.0},{4.0,5.0,6.0}}, *p,
(*pm3)[3];
float *b;
int i;
p=(float *)a;
pm3=a;
for (i=0;i<6;i++)
printf("%f", *(p+i));
printf("\n");
b=(float*)pm3;
for (i=0;i<3;i++)
printf("%f", b[i]);
printf("\n");
b=(float*)(pm3+1);
for (i=0;i<3;i++)
printf("%f", b[i]);
getch();
}
Các phép toán trên con trỏ
48
Có 4 phép toán liên quan đến con trỏ và đại chỉ là:
Phép gán.
Phép tăng giảm địa chỉ.
Phép truy cập bộ nhớ.
Phép so sánh.
• Phép gán: Phép gán chỉ thực hiện với các con trỏ cùng kiểu. Muốn gán các
con trỏ khác kiểu phải dùng phép ép kiểu.
int x;
char *pc;
pc=(char*)(&x);
• Phép tăng giảm địa chỉ: Sử dụng phép toán này để di chuyển giữa các ô
nhớ:
• Ví dụ: Các câu lệnh:
float x[30],*px;
px=&x[10];
• Cho con trỏ px là con trỏ float trỏ tới phần tử x[10]. Ta có:
px+i trỏ tới phần tử x[10+i]
px-i trỏ tới phần tử x[10-i]
Các phép toán trên con trỏ (t)
49
• Phép truy cập bộ nhớ:
Con trỏ float trỏ tới địa chỉ dài 4 byte, con trỏ int trỏ tới địa chỉ dài 2 byte, con trỏ char
trỏ tới địa chỉ dài 1 byte.
• Ví dụ: khai báo:
float *pf;
int *pi;
char *pc;
• Khi đó ta có các nhận xét sau:
Nếu trỏ pf trỏ đến byte thứ 100 thì *pf biểu thị vùng nhớ 4 byte liên tiếp từ byte 100
đến 103.
Nếu trỏ pi trỏ đến byte thứ 100 thì *pi biểu thị vùng nhớ 2 byte liên tiếp từ byte 100
đến 101.
Nếu trỏ pc trỏ đến byte thứ 100 thì *pc biểu thị vùng nhớ 1 byte chính là byte 100.
• Phép so sánh: Cho phép so sánh các con trỏ cùng kiểu.
• Ví dụ nếu p1 và p2 là các con trỏ cùng kiểu thì nếu:
p1<p2 nếu địa chỉ p1 trỏ tới thấp hơn địa chỉ p2 trỏ tới.
p1==p2 nếu địa chỉ p1 trỏ tới cũng là địa chỉ p2 trỏ tới.
p1>p2 nếu địa chỉ p1 trỏ tới cao hơn địa chỉ p2 trỏ tới.
Ví dụ
50
Ví dụ 1:
Đoạn chương trình tính tổng các số thực dùng phép so sánh con trỏ:
float a[100],*p,*pcuoi,tong=0.0;
pcuoi=(float*)(a+99); /* Địa chỉ cuối dãy*/
for (p=(float*)a;p<=pcuoi;++p)
tong+=*p;
Ví dụ 2:
Dùng con trỏ char để tách các byte của một biến nguyên, ta làm như sau:
Giả sử ta có biến nguyên n được khai báo như sau:
unsigned int n=0xABCD; /* Số nguyên hệ 16*/
char *pc;
pc=(char*)(&n);
Khi đó:
*pc=0xAB (byte thứ nhất của n)
*(pc+1)=0xCD (byte thứ hai của n)
Con trỏ kiểu void
51
• Cú pháp: void *tên_con_trỏ;
• Đây là con trỏ đặc biệt, con trỏ không kiểu, nó có thể nhận địa chỉ
kiểu bất kỳ.
• Ví dụ: Câu lệnh sau là hợp lệ:
void *pa;
float a[20][30];
pa=a;
• Con trỏ void thường dùng làm tham số hình thức để nhận bất kỳ địa
chỉ kiểu nào từ tham số thực. Trong thân hàm phải dùng phép chuyển
đổi kiểu để chuyển sang dạng địa chỉ cần xử lý.
• Chú ý: Các phép toán tăng giảm địa chỉ, so sánh và truy cập bộ
nhớ không dùng được trên con trỏ void.
Mảng con trỏ
52
• Mảng con trỏ: là một mảng mà mỗi phần tử của nó là một con trỏ.
• Cú pháp:
*[N];
• Khi gặp khai báo trên, máy sẽ cấp phát N khoảng nhớ liên tiếp cho N phần
tử của mảng.
• Ví dụ: double *pa[100];
Khai báo một mảng con trỏ kiểu double gồm 100 phần tử. Mỗi phần tử
pa[i] có thể dùng để lưu trữ một địa chỉ kiểu double.
• Chú ý :
• Bản thân các mảng con trỏ không dùng để lưu trữ số liệu.
• Trước khi sử dụng một mảng con trỏ ta cần gán cho mỗi phần tử của nó
một địa chỉ.
Cấp phát bộ nhớ cho biến con trỏ
53
• Trước khi sử dụng biến con trỏ, ta phải cấp phát vùng nhớ cho biến con trỏ này
quản lý địa chỉ. Việc cấp phát được thực hiện nhờ các hàm malloc(), calloc(),
realloc() trong thư viện alloc.h, hoặc stdlib.h.
• Cú pháp các hàm:
void *malloc(size_t size); Cấp phát vùng nhớ có kích thước là size byte.
void *calloc(size_t nitems, size_t size); Cấp phát vùng nhớ có kích thước
là nitems*size byte.
void *realloc( void *memblock, size_t size ); Cấp phát lại ô nhớ cho con
trỏ memblock size byte.
• Ví dụ: Giả sử ta có khai báo:
int a, *pa, *pb;
pa = (int*)malloc(sizeof(int)); /* Cấp phát vùng nhớ có kích thước bằng với
kích thước của một số nguyên */
pb= (int*)calloc(10, sizeof(int)); /* Cấp phát vùng nhớ có thể chứa được 10
số nguyên*/
pa= (int*) realloc(pa, 10*sizeof(int)); /* Cấp phát lại vùng nhớ có thể chứa
được 10 số nguyên*/
Giải phóng vùng nhớ do biến con trỏ quản
lý
54
• Một vùng nhớ đã cấp phát cho biến con trỏ, khi không cần
sử dụng nữa, ta sẽ thu hồi lại vùng nhớ này nhờ hàm
free().
• Cú pháp: void free(void *block)
• Ý nghĩa: Giải phóng vùng nhớ được quản lý bởi con trỏ
block.
• Ví dụ: Ở ví dụ trên, sau khi thực hiện xong, ta giải phóng
vùng nhớ cho 2 biến con trỏ pa & pb:
free(pa);
free(pb);
Ví dụ
Cấp phát động mảng 1 chiều, nhập và in mảng sử