BÀI GIẢNG HỌC PHẦN

KỸ THUẬT LẬP TRÌNH

CHƯƠNG 4: CÁC KIỂU DỮ LIỆU CÓ CẤU TRÚC

Nội dung

2

4.1. Kiểu mảng 4.2. Kiểu xâu ký tự 4.3. Kiểu cấu trúc

4.1. Kiểu mảng

3

• Khái niệm mảng • Khai báo và sử dụng mảng • Các thao tác cơ bản trên mảng • Một số chương trình với mảng

Khái niệm mảng

• Là một kiểu dữ liệu có cấu trúc, bao gồm một tập hữu hạn các phần tử có cùng kiểu dữ liệu, có tên chung, được lưu trữ kế tiếp nhau trong bộ nhớ

• Kích thước của mảng được xác định ngay khi khai

• Các phần tử trong mảng có tên chung là tên của mảng và được phân biệt với nhau thông qua chỉ số của chúng

• Mảng thường được dùng để lưu một dãy giá trị như: dãy số nguyên, dãy số thực, dãy kí tự, ma trận, …

4

báo và là cố định

Khai báo và sử dụng mảng (1)

kiểu_dữ_liệu tên_mảng[N1][N2]…[Nn]

• Khai báo mảng:

trong đó:

- kiểu_dữ_liệu: là kiểu dữ liệu của các phần tử mảng - tên_mảng: tên biến mảng - N1, N2, …, Nn: kích thước các chiều của mảng  Số phần tử của mảng: N1 * N2 * … * Nn • Ví dụ:

5

- int a[10], b[20]; - float matran[10][20];

Khai báo và sử dụng mảng (2)

thước các chiều của mảng Ví dụ:

• Có thể sử dụng chỉ thị #define để xác định kích

6

#define N1 20 #define N2 30 int a[N1]; float b[N1][N2];

Khai báo và sử dụng mảng (3)

• Kết hợp khởi tạo giá trị trong khai báo mảng: - Khởi tạo giá trị cho mọi phần tử của mảng

Ví dụ: int a[3] = {4,5,9};

float x[2][3] = {{2,1.5,3}, {1,3,4.25}}; - Khởi tạo giá trị cho một số phần tử đầu mảng

Ví dụ: int b[5] = {2,6,1};

bằng với số lượng giá trị khởi tạo Ví dụ: int c[] = {15,20,36,54};

7

- Khi không khai báo kích thước, kích thước mảng

Khai báo và sử dụng mảng (4)

#define tên_mảng {giá_trị_1, …, giá_trị_n} Không truy cập được vào các phần tử của mảng - Cách 2:

• Khai báo hằng kiểu mảng: - Cách 1:

const kiểu_dữ_liệu tên_mảng[N1] … [Nn] = {giá_trị_1, …, giá_trị_n}

Ví dụ:

8

const int hangmang1[4] = {10,20,25,40}; const float hangmang2[] = {4.5,9.25}; const float hangmang3[5] = {1.5,2,5};

Khai báo và sử dụng mảng (5)

• Lưu trữ dữ liệu kiểu mảng: - Các phần tử của mảng được lưu trữ tại các ô nhớ kế

N1 * N2 * … * Nn * sizeof(kiểu_dữ_liệu) - Các phần tử trong mảng được đánh chỉ số, phần tử đầu tiên có chỉ số là 0, phần tử thứ i có chỉ số là i-1 - Biến mảng lưu trữ địa chỉ của ô nhớ đầu tiên trong

vùng nhớ được cấp phát

9

tiếp nhau trong bộ nhớ - Kích thước bộ nhớ lưu trữ:

Khai báo và sử dụng mảng (6)

• Sử dụng mảng: - Cú pháp truy cập vào các phần tử mảng:

tên_mảng[chỉ_số_1][chỉ_số_2] … [chỉ_số_n]

int a[4];

- Ví dụ:

10

 Các truy cập hợp lệ: a[0], a[1], a[2], a[3]  Các truy cập không hợp lệ: a[-2], a[-1], a[4], a[5],

Khai báo và sử dụng mảng (7)

• Sử dụng mảng: (tiếp) - Lấy địa chỉ của phần tử mảng (áp dụng cho mảng 1

chiều):

Ví dụ: &a[1]

&tên_mảng[chỉ_số];

- Tên mảng biểu thị địa chỉ đầu của mảng, tức là:

11

a = &a[0]

Các thao tác cơ bản trên mảng (1)

• Phép gán mảng: - Không được thực hiện phép gán thông thường cho

một biến mảng

- Chỉ có thể thực hiện phép gán giá trị cho các phần

tử của mảng

- Ví dụ:

int a[3] = {4,5,9},b[3],i;

 Phép gán không hợp lệ: b = a;  Phép gán hợp lệ:

for(i=0;i<3;i++)

12

b[i] = a[i];

Các thao tác cơ bản trên mảng (2)

Sử dụng hàm scanf để nhập dữ liệu cho từng phần tử của mảng Ví dụ: int a[10];

• Nhập dữ liệu cho mảng từ bàn phím:

 Nhập dữ liệu cho phần tử a[1]: scanf("%d",&a[1]);  Nhập dữ liệu cho tất cả các phần tử của mảng: sử

dụng cấu trúc lặp for

printf("a[%d] = ",i); scanf("%d",&a[i]);

int i; for(i=0;i<10;i++) {

13

}

Các thao tác cơ bản trên mảng (3)

• Nhập dữ liệu cho mảng từ bàn phím (tiếp):

Lưu ý: Phép lấy địa chỉ không áp dụng cho các phần tử của mảng nhiều chiều (trừ trường hợp mảng 2 chiều gồm các số nguyên)  cần sử dụng biến trung gian khi nhập dữ liệu cho mảng nhiều chiều Ví dụ: float a[10][20];

Nhập dữ liệu cho các phần tử của mảng:

int i,j; float x; for(i=0;i<10;i++)

for(j=0;j<20;j++)

{

printf("a[%d][%d] = ",i,j); scanf("%f",&x); a[i][j] = x;

}

14

Các thao tác cơ bản trên mảng (4)

Sử dụng hàm printf để xuất dữ liệu từng phần tử của mảng ra màn hình Ví dụ: int a[10];

• Xuất dữ liệu của mảng ra màn hình:

printf("a[1] = %d",a[1]);

 Xuất dữ liệu của phần tử a[1]:

 Xuất dữ liệu của tất cả các phần tử mảng sử dụng

cấu trúc lặp for:

printf("a[%d] = %d\n",i,a[i]);

15

int i; for(i=0;i<10;i++) { }

Ví dụ (1)

#include int main(void) { int a[50]; int n,i; lap: printf("Nhap so phan tu n = "); scanf("%d",&n); if (n<=0 || n>50)

{ printf("So phan tu khong hop le, hay nhap lai!\n"); goto lap; }

16

• Chương trình nhập/xuất dữ liệu cho mảng:

Ví dụ (2)

for(i=0;i

• Chương trình nhập/xuất dữ liệu cho mảng: (tiếp)

{

}

printf("a[%d] = ",i); scanf("%d",&a[i]);

printf("Day so vua nhap la:\n"); for(i=0;i

return 0; }

17

printf("%d ",a[i]);

Một số chương trình với mảng

dãy số nguyên a1, a2 , …, an

• Viết chương trình tính tổng, trung bình cộng cho

• Viết chương trình tìm giá trị lớn nhất, nhỏ nhất của

dãy số nguyên a1, a2 , …, an

• Viết chương trình tìm phần tử có giá trị là x trong dãy số nguyên a1, a2 , …, an (số nguyên x nhập từ bàn phím)

• Viết chương trình sắp xếp dãy số nguyên a1, a2 , …,

• Viết chương trình nhập vào ma trận số nguyên (aij)mxn, in lại các giá trị vừa nhập theo dạng ma trận, tính tổng các phần tử trong ma trận và thông báo kết quả ra màn hình

18

an theo chiều tăng/giảm dần

4.2. Kiểu xâu ký tự

19

• Khái niệm xâu ký tự • Khai báo và sử dụng xâu ký tự • Các hàm xử lý ký tự • Các hàm xử lý xâu ký tự • Một số chương trình với xâu ký tự

Khái niệm xâu ký tự

• Là một kiểu dữ liệu có cấu trúc, bao gồm một dãy các ký tự liên tiếp, kết thúc bởi ký tự '\0' (null – mã ASCII là 0)

• Mỗi ký tự được lưu trong 1byte • Độ dài xâu là số ký tự có trong xâu • Xâu rỗng là xâu không có ký tự nào • So sánh:

20

- Xâu ký tự & mảng ký tự - 'A' và "A"

Khai báo và sử dụng xâu ký tự (1)

• Khai báo xâu ký tự:

trong đó:

char tên_xâu[n]

n = số ký tự tối đa + 1

Ví dụ: - char hoten[30]; - char s1[50], s2[100];

• Khởi tạo giá trị trong khai báo xâu ký tự:

Xét các ví dụ:

21

char s[4] = {'A', 'B', 'C', '\0'}; char hoten[30] = "Luu Thanh Duyen";

Khai báo và sử dụng xâu ký tự (2)

#define tên_xâu giá_trị

• Khai báo hằng xâu ký tự: - Cách 1:

- Cách 2:

const char tên_xâu[n] = giá_trị;

Ví dụ:

22

#define s1 "ABC" const char s2[10] = "ABCDE";

Khai báo và sử dụng xâu ký tự (3)

• Lưu trữ dữ liệu kiểu xâu ký tự: - Các ký tự được lưu trữ tại các byte nhớ kế tiếp nhau

trong bộ nhớ

- Kích thước bộ nhớ lưu trữ: Số ký tự tối đa + 1

23

- Tương tự như dữ liệu kiểu mảng, mỗi ký tự trong xâu được đánh chỉ số, ký tự đầu tiên có chỉ số là 0, ký tự thứ i có chỉ số là i-1

Khai báo và sử dụng xâu ký tự (4)

• Sử dụng xâu ký tự: - Cú pháp truy cập vào các ký tự trong xâu:

tên_xâu[chỉ_số_ký_tự]

char s[15] = "Tran Ngoc Anh"; s[0]  'T' s[3]  'n' s[5]  'N'

24

- Ví dụ:

Khai báo và sử dụng xâu ký tự (5)

• Vào/ra xâu ký tự: - Khai báo tệp tiêu đề:

#include - Nhập xâu ký tự từ bàn phím:

gets(tên_xâu); scanf("%s",&tên_xâu); - Xuất xâu ký tự ra màn hình:

puts(tên_xâu); printf("%s",tên_xâu);

- Không sử dụng hàm scanf() để nhập xâu chứa ký tự cách - Trước khi dùng gets() cần dùng hàm fflush(stdin)

25

• Lưu ý: - Không dùng phép gán giá trị cho xâu

Các hàm xử lý ký tự (1)

#include

• Khai báo tệp tiêu đề:

hoa Ví dụ: toupper('a')  'A'

• Các hàm: - int toupper(int ch): chuyển ký tự thường thành ký tự

- int tolower(int ch): chuyển ký tự hoa thành ký tự

26

thường Ví dụ: tolower('A')  'a'

Các hàm xử lý ký tự (2)

- int isalpha(int ch): kiểm tra xem ký tự có phải là chữ

- int islower(int ch): kiểm tra xem ký tự có phải là

cái hay không

chữ cái in thường hay không

- int isupper(int ch): kiểm tra xem ký tự có phải là

- int isdigit(int ch): kiểm tra xem ký tự có phải là chữ

chữ cái in hoa hay không

số hay không

- int iscntrl(int ch): kiểm tra xem ký tự có phải là ký

- int isspace(int ch): kiểm tra xem ký tự có phải là ký

tự điều khiển hay không (mã ASCII  [0,31])

tự dấu cách (mã ASCII là 32) hay không

27

• Các ký tự đặc biệt: xuống dòng ('\n' - 10), đầu dòng ('\r' - 13), tab ngang ('\t' - 9), tab dọc ('\v' – 11), …

Các hàm xử lý xâu ký tự (1)

• Khai báo tệp tiêu đề: #include • Các hàm: - int strlen(char[] tên_xâu): trả về độ dài của xâu - char[] strcpy(char[] xâu_đích, char[] xâu_nguồn):

sao chép toàn bộ xâu_nguồn sang xâu_đích Để sao chép tối đa n ký tự từ xâu_nguồn sang xâu_đích, sử dụng hàm: char[] strncpy(char[] xâu_đích, char[] xâu_nguồn, int n) - char[] strcat(char[] xâu_đích, char[] xâu_nguồn):

ghép nối xâu_nguồn vào ngay sau xâu_đích

28

- char[] strrev(char[] xâu_nguồn) đảo ngược các ký tự trong xâu_nguồn, ký tự đầu về cuối, cuối về đầu

Các hàm xử lý xâu ký tự (2)

• Các hàm: (tiếp) - int strcmp(char[] xâu_1, char[] xâu_2): so sánh 2 xâu (có phân biệt ký tự in hoa – in thường), hàm trả về giá trị:

= 0 nếu 2 xâu bằng nhau < 0 nếu xâu_1 < xâu_2 > 0 nếu xâu_1 > xâu_2

Nếu muốn so sánh 2 xâu, không phân biệt ký tự in hoa – in thường thì sử dụng hàm:

- char[] strupr(char[] tên_xâu): trả về xâu chữ in hoa

int stricmp(char[] xâu_1, char[] xâu_2)

tương ứng

- char[] strlwr(char[] tên_xâu): trả về xâu chữ in

29

thường tương ứng

Các hàm xử lý xâu ký tự (3)

#include

• Khai báo tệp tiêu đề:

• Các hàm: - int atoi(char[] xâu_ký_tự): chuyển một xâu ký tự

- int atol(char[] xâu_ký_tự): chuyển một xâu ký tự

thành số nguyên tương ứng kiểu int

thành số nguyên tương ứng kiểu long

- float atof(char[] xâu_ký_tự): chuyển một xâu ký tự

• Lưu ý: khi việc chuyển đổi không thành công, cả 3

thành số thực tương ứng

30

hàm trên trả về giá trị 0

Ví dụ (1)

Chương trình làm việc với ký tự/xâu ký tự: #include #include #include int main(void) { char c, s[30]; printf("Nhap ky tu c: ");scanf("%c",&c); if(isalpha(c))

{ if(isupper(c)) printf("Ky tu vua nhap la ky tu in hoa '%c', ky tu in thuong tuong ung la '%c'!",c,tolower(c)); else printf("Ky tu vua nhap la ky tu in thuong '%c', ky tu in hoa tuong ung la '%c'!",c,toupper(c)); }

31

Ví dụ (2)

Chương trình làm việc với ký tự/xâu ký tự (tiếp): else if(isdigit(c))

printf("Ky tu vua nhap la ky tu so '%c'!",c);

fflush(stdin); printf("\nNhap xau ky tu s: ");gets(s); printf("Do dai thuc cua xau la %d",strlen(s)); printf("\nXau chu in hoa tuong ung la: %s",strupr(s)); printf("\nXau chu in thuong tuong ung la: %s",strlwr(s)); printf("\nXau dao tuong ung la: %s",strrev(s)); return 0; }

32

Một số chương trình với xâu ký tự

• Viết chương trình cho phép người dùng nhập vào một xâu ký tự s, đếm số ký tự 'A' và 'a' có mặt trong s, thay thế các ký tự đó bởi ký tự 'B' rồi đưa xâu mới ra màn hình

33

• Viết chương trình cho phép người dùng nhập vào một xâu ký tự s. Xây dựng xâu s1 gồm toàn các ký tự chữ, s2 gồm toàn các ký tự số có trong s, thông báo kết quả ra màn hình

4.3. Kiểu cấu trúc

34

• Định nghĩa kiểu cấu trúc • Khai báo biến cấu trúc • Truy nhập đến các thành phần của cấu trúc • Mảng cấu trúc • Phép gán cấu trúc

Định nghĩa kiểu cấu trúc

• Cú pháp:

{

struct tên_kiểu_cấu_trúc

khai_báo_các_thành_phần_trong_cấu_trúc;

- Việc khai báo các thành phần tuân theo quy tắc khai báo biến thông thường (tên_kiểu tên_thành_phần;) - Kiểu dữ liệu của thành phần có thể là kiểu bất kỳ (nguyên, thực, ký tự, mảng, xâu hay kiểu cấu trúc) Ví dụ: struct ngaythang

}; Trong đó:

35

{int ngay; int thang; int nam;};

Khai báo biến cấu trúc (1)

struct tên_kiểu_cấu_trúc tên_biến_cấu_trúc;

• Cú pháp:

Ví dụ:

struct ngaythang ngaysinh;

• Khi có nhiều biến cùng kiểu cấu trúc, có thể khai báo gộp với các tên biến viết ngăn cách bởi dấu , Ví dụ:

struct ngaythang ngaydi, ngayden;

cấu trúc Ví dụ:

• Có thể kết hợp khai báo và khởi tạo giá trị cho biến

36

struct ngaythang ngaysinh = {12,10,1988};

Khai báo biến cấu trúc (2)

khai báo biến cấu trúc theo cú pháp:

• Có thể kết hợp việc định nghĩa kiểu cấu trúc với

struct tên_kiểu_cấu_trúc

{

khai_báo_các_thành_phần_trong_cấu_trúc; } tên_biến_cấu_trúc1, tên_biến_cấu_trúc2, …;

Ví dụ:

37

struct ngaythang {int ngay; int thang; int nam;} ngaydi,ngayden;

Khai báo biến cấu trúc (3)

• Có thể sử dụng cú pháp sau để khai báo các biến

cấu trúc: (bỏ tên_kiểu_cấu_trúc) struct {

khai_báo_các_thành_phần_trong_cấu_trúc; } tên_biến_cấu_trúc1, tên_biến_cấu_trúc2, …; Chỉ khai báo các biến cấu trúc, không định nghĩa

kiểu cấu trúc Ví dụ:

{int ngay; int thang; int nam;} ngaydi,ngayden;

38

struct

Lưu ý

• Có thể sử dụng cú pháp typedef để định nghĩa kiểu

cấu trúc:

{

typedef struct

khai_báo_các_thành_phần_trong_cấu_trúc;

} tên_kiểu_cấu_trúc;

Khi đó, cú pháp khai báo biến cấu trúc sẽ là: tên_kiểu_cấu_trúc tên_biến_cấu_trúc;

Ví dụ: typedef struct {int ngay; int thang; int nam;} ngaythang;

39

 Khai báo biến ngaysinh: ngaythang ngaysinh;

Truy nhập đến các thành phần của cấu trúc

tên_cấu_trúc.tên_thành_phần

• Cú pháp:

Ví dụ:

40

printf("%d",ngaysinh.ngay); ngaydi.ngay = ngayden.ngay+2; ngaydi.thang = 12; …

Mảng cấu trúc

một kiểu cấu trúc đã được định nghĩa trước đó Ví dụ:

• Có thể khai báo một mảng gồm các phần tử thuộc

struct ngaythang ngaythuephong[10];

 ngaythuephong là một mảng có 10 phần tử. Mỗi phần tử là một cấu trúc kiểu ngaythang

41

• Lưu ý: Khi nhập dữ liệu cho mỗi phần tử trong mảng cấu trúc, cần nhập dữ liệu cho từng thành phần trong cấu trúc

Phép gán cấu trúc (1)

• Khi các biến cấu trúc và các phần tử của mảng cấu trúc là cùng kiểu với nhau, có thể thực hiện các phép gán giá trị:

- Gán giá trị của biến cấu trúc này cho một biến cấu

- Gán giá trị của biến cấu trúc cho phần tử mảng cấu

trúc khác

trúc và ngược lại

- Gán giá trị của phần tử mảng cấu trúc này cho phần

Mỗi phép gán cấu trúc tương đương với một dãy

tử mảng cấu trúc khác

42

phép gán các thành phần tương ứng cho nhau

Phép gán cấu trúc (2)

Khai báo: struct ngaythang ngay1,ngay2,ngaythue [10],ngaytra[10]; Các phép gán sau đều là hợp lệ:

• Ví dụ:

43

ngay1 = ngay2; ngaythue[0] = ngay1; ngay2 = ngaytra[0]; ngaythue[0] = ngaytra[0];

Ví dụ (1)

• Chương trình nhập/hiển thị danh sách n cán bộ: #include int main(void)

{ struct canbo

char macb[6]; char hoten[30]; char mapb[6];

{

struct canbo dscb[50]; int i,n; printf("Nhap so can bo n = ");scanf("%d",&n);

44

};

Ví dụ (2)

for(i=0;i

printf("Nhap can bo thu %d\n",i); fflush(stdin); printf("Ma can bo: ");gets(dscb[i].macb); printf("Ho ten: ");gets(dscb[i].hoten); printf("Ma pb: ");gets(dscb[i].mapb);

{

}

printf("%-6s %-30s %-6s\n",

printf("Danh sach can bo vua nhap la:\n"); for(i=0;i

dscb[i].macb,dscb[i].hoten,dscb[i].mapb);

return 0;

45

}