Số tín chỉ: 3 Giảng viên: Ths. Cù Nguyên Giáp Bộ môn: Tin học
7/2010 Cơ sở lập trình 1
Chương 3 – Mảng, xâu và con trỏ 3.1 Mảng và xâu 3.2 Con trỏ
7/2010 Cơ sở lập trình 2
3.1 Mảng và xâu 3.1.1 Mảng 3.1.2 Xâu
7/2010 Cơ sở lập trình 3
3.1.1 Mảng • Giới thiệu: – Bài toán dãy số với n biến đơn việc khai báo và quản lý biến phức tạp Biến mảng – Mảng: là một dãy các phần tử có cùng kiểu dữ liệu được đặt liên tiếp trong bộ nhớ và có thể truy xuất đến từng phần tử thông qua chỉ số mảng. – Các yếu tố của mảng: • Kiểu mảng • Tên mảng • Số phần tử/kích thước của mảng 7/2010 Cơ sở lập trình 4 3.1.1 Mảng • Khai báo mảng <type> <name> [size1][size2]…[sizeN]; • Trong đó: – type là các kiểu dữ liệu cơ sở/ cấu trúc – name: là tên được đặt cho mảng – Số lượng thành phần [ ] được đặt sau tên mảng sẽ qui định so chiều của mảng đó. – Size1, size2,.., sizeN là các số nguyên qui định kích cỡ của mảng hoặc số phần tử của mảng.
7/2010 Cơ sở lập trình 5
3.1.1 Mảng • Ví dụ: – int a[10]; – float arr[2][3]; • Chú ý: – Mảng chỉ thực sự làm việc tốt với mảng tối đa 3 chiều – Trường size1,size2..sizeN phải là một giá trị hằng khi khai báo một mảng (vì mảng là một khối nhớ tĩnh có kích cỡ xác định) • Ví dụ khai báo sai: – int n,m; – int a[n][m];
7/2010 Cơ sở lập trình 6
3.1.1 Mảng • Khởi tạo mảng: – Biến địa phương: không được khởi tạo – Biến toàn cục: tất cả các phần tử được khởi tạo bằng 0/NULL – Có thể gán giá trị khởi tạo cho các phần tử của mảng • Với mảng 1chiều: <type> <name> [size]={v1,v2,v3,..,vk}; //( k<=N) Hoặc <type> <name> [ ]={v1,v2,v3,..,vk}; • Ví dụ: char a[5] = { 0, 1, 4, 3, 2 }; char a[] = { 0, 1, 4, 3, 2 }; 7/2010 Cơ sở lập trình 7 3.1.1 Mảng • Với mảng 2chiều: <type> <name> [size1][size2] ={ {a11,a12,a13,...}, {a21,a22,a23,... }, … }; • Khi đó: a11,a12,a23, … là giá trị khởi đầu của hàng đầu tiên của mảng. • a21,a22,a23, … là giá trị khởi đầu của hàng thứ hai của mảng. • …. 7/2010 Cơ sở lập trình 8 3.1.1 Mảng • Ví dụ: int a[][4]={ {0},{1,3,5}, {2,4,6,8} } a[0][0] =0 a[1][0]=1, a[1][1]=3, a[1][2]=5, và a[2][0]=2, a[2][1]=4, a[2][2]=6, a[2][3]=8
7/2010 Cơ sở lập trình 9
3.1.1 Mảng • Cách khởi tạo khác: <type> <name> [size1][size2] ={v1,v2,..,vK }; trong đó (k <= size1*size2) – Khi đó m giá trị đầu tiên của k gán cho dòng 1, m giá trị tiếp theo gán cho dòng 2, …., cho đến hết k giá trị, còn lại gán là 0, NULL hoặc giá trị bất kì tuỳ theo kiểu dữ liệu và mảng là biến toàn cục hay biến cục bộ. • Ví dụ: int a[3][2]={1,2,3,4,5,6};
7/2010 Cơ sở lập trình 10
3.1.1 Mảng • Truy xuất phần tử của mảng • Cấu trúc: name[chỉ_số] – Mỗi phần tử của mảng đóng vai trò như một biến đơn • Lưu ý: – Trong ngôn ngữ C các phần tử của mảng được đánh số bắt đầu từ 0 vấn đề vượt quá giới hạn của mảng. – Tên của mảng tương ứng với địa chỉ phần tử đầu tiên của nó. • Ví dụ: int a[5]; a[2]=8;
7/2010 Cơ sở lập trình 11
3.1.1 Mảng • Đối với mảng 2 chiều: – Thường sử dụng define để định nghĩa trước kích thước của mảng – Cách truy xuất cũng tương tự như mảng 1 chiều. Phần tử đầu tiên có chỉ số 0, a[0][0], phần tử a[i][j] là phần tử ở dòng i cột j. • Lưu ý: – Truy xuất tới một phần tử vượt ra ngoài phạm vi khai báo của mảng: không bị báo lỗi nhưng giá trị của phần tử đó sẽ không kiểm soát được.
7/2010 Cơ sở lập trình 12
3.1.1 Mảng • Làm việc với mảng: dùng câu lệnh for để duyệt các phần tử của mảng với biến điều khiển chạy theo chỉ số của mảng. • Ví dụ: Làm việc với dãy số – Nhập dãy số – In dãy số – Đếm số phần tử chia hết cho 5. – Tính tổng các phần tử chẵn. – Sắp xếp dãy 3.1.2 Xâu (string) • Giới thiệu: – Xâu được xây dựng như một mảng các kí tự kể cả khoảng trắng. – Một hằng xâu có nội dung được đặt trong cặp dấu “ ” và kết thúc xâu là kí tự ‘\0’ hay còn gọi là kí tự NULL • Lưu ý: – Kí tự ‘a’ khác với xâu “a”. Vì xâu “a” bao gồm hai kí tự là ‘a’ và ‘\0’
7/2010 Cơ sở lập trình 14
3.1.2 Xâu • Khai báo xâu: – Dùng mảng kí tự: char a[size]; – Dùng con trỏ kí tự: char *str; • Khởi tạo xâu: – Dùng mảng hoặc con trỏ • Ví dụ: – char a[15] = “hello world”; • hoặc – char *str = (char*) malloc (15*sizeof (char)); – char *str=”hello world”;
7/2010 Cơ sở lập trình 15
3.1.2 Xâu • Nhập dữ liệu cho xâu: • Cách 1: khởi tạo giá trị mặc định: char *str=”hello world”; • Cách 2: Dùng hàm scanf: char *s = (char*) malloc (9*sizeof (char)); for( i =0; i< 9; i++) scanf(“%c”, s+i); • Lưu ý: cơ chế làm việc của vùng nhớ đệm có thể nhận được kết quả không theo mong muốn cần làm sạch vùng đệm
7/2010 Cơ sở lập trình 16
3.1.2 Xâu char *s = (char*) malloc (9*sizeof (char)); for( i =0; i< 9; i++) { fflush(stdin) ; scanf(“%c”, s+i); } • Nhận xét: • Dài dòng và phức tạp hạn chế sử dụng hàm scanf để nhập dữ liệu cho xâu
7/2010 Cơ sở lập trình 17
3.1.2 Xâu • Cách 3: dùng hàm gets • Cú pháp: char * gets(char *str); • Hoạt động: – cho phép nhập vào một dãy kí tự cho đến khi gặp kí hiệu ‘\n’(Enter). – Kí tự ‘\n’ sẽ không được đặt vào chuỗi str, trình biên dịch sẽ thêm vào cuối str là kí tự ‘\0’ (ký tự kết thúc chuỗi) - làm sạch vùng đệm trước khi gọi hàm: fflush(stdin);
7/2010 Cơ sở lập trình 18
3.1.2 Xâu • Xuất xâu ra màn hình: • Dùng hàm printf hoặc puts int puts(const char *); • Sự khác nhau: puts in xong thì con trỏ tự động nhảy xuống dòng • Ví dụ: char hello[] = "Hello World"; printf("\nChuoi in ra = %s", hello); puts(hello);
7/2010 Cơ sở lập trình 19
3.1.2 Xâu • Ví dụ: Viết chương trình tìm độ dài của xâu.
7/2010 Cơ sở lập trình 20
3.1.2 Xâu • Một số hàm xử lý xâu: – strlen – strchr – strstr – toupper – tolower • Yêu cầu: viết chương trình để thể hiện các hàm trên
7/2010 Cơ sở lập trình 21
3.2 Con trỏ 3.2.1 Khái quát về con trỏ 3.2.2 Các phép toán trên con trỏ 3.2.3 Con trỏ và mảng
7/2010 Cơ sở lập trình 22
3.2.1 Khái quát về con trỏ • Ý nghĩa: lưu trữ địa chỉ của vùng nhớ, có khả năng tham chiếu trực tiếp đến giá trị mà chúng trỏ tới. • Khai báo: <Type> *<name>; • Trong đó: – Trong đó Type là kiểu dữ liệu của vùng nhớ được trỏ tới không phải là kiểu của bản thân con trỏ Mỗi kiểu địa chỉ có một kiểu con trỏ tương ứng. – Sau khi khai báo nhưng chưa được chỉ định trỏ vào đâu thì con trỏ mang giá trị NULL
7/2010 Cơ sở lập trình 23
3.2.1 Khái quát về con trỏ • Ví dụ: int *number; char *character; float *greatnumber; • Lưu ý: – Đều là con trỏ và đều chiếm một lượng bộ nhớ như nhau ((kích thước của một biến con trỏ tùy thuộc vào hệ điều hành) – Nhưng dữ liệu mà chúng trỏ tới không chiếm lượng bộ nhớ như nhau
7/2010 Cơ sở lập trình 24
3.2.1 Khái quát về con trỏ • Sử dụng con trỏ: cần phân biệt giá trị và địa chỉ của con trỏ • Ví dụ: Int *p; p chứa địa chỉ các vùng nhớ *p chứa giá trị trong các vùng nhớ mà nó trỏ đến • Nhập giá trị cho các biến trỏ: –C1: sử dụng phép gán hoặc khởi tạo ban đầu –C2: sử dụng các hàm cấp phát động
7/2010 Cơ sở lập trình 25
3.2.1 Khái quát về con trỏ • Khởi tạo con trỏ: để chỉ định rõ ràng con trỏ sẽ trỏ tới biến nào khi khai báo • Ví dụ: int cat; int *tommy = &cat; Tương đương với: int cat; int *tommy; tommy = &cat;
7/2010 Cơ sở lập trình 26
3.2.1 Khái quát về con trỏ • Cấp phát bộ nhớ cho con trỏ • Thư viện: alloc.h • Hàm cấp phát: malloc/ calloc • Cú pháp: <name> = <(type*)>malloc(size);// dùng cho các kiểu dl cơ sở <name> = <(type*)>calloc(n,sizeof (type));//dùng cho kiểu dl người dùng định nghĩa
7/2010 Cơ sở lập trình 27
3.2.1 Khái quát về con trỏ • Ví dụ: so sánh int *ptr; ptr = (int *) malloc(sizeof(int)); int x = 5; *ptr = x; Và int *ptr; int x = 5; ptr = &x; • Lưu ý: khi gán con trỏ chúng ta phải luôn luôn gán địa chỉ mà nó trỏ tới chứ không phải là giá trị mà nó trỏ tới.
7/2010 Cơ sở lập trình 28
3.2.2 Các phép toán trên con trỏ • Phép so sánh • Phép cộng con trỏ với số nguyên • Phép trừ hai con trỏ
7/2010 Cơ sở lập trình 29
3.2.2 Các phép toán trên con trỏ • Phép so sánh: là phép so sánh địa chỉ mà con trỏ lưu giữ • Phép cộng con trỏ với số nguyên – Ptr là con trỏ kiểu T, k là số nguyên thì (Ptr+k) là con trỏ kiểu T. – Ptr trỏ tới phần tử t thì (Ptr+k) trỏ tới phần tử cách t một khoảng là k.
7/2010 Cơ sở lập trình 30
3.2.3 Con trỏ và mảng • Giống nhau: Tên của một mảng tương đương với địa chỉ phần tử đầu tiên của nó. Một con trỏ tương đương với địa chỉ của phần tử đầu tiên mà nó trỏ tới. • Ví dụ: int numbers [20], *p; p = numbers; //hợp lệ numbers = p; //không hợp lệ • p và numbers là tương đương và chúng có cùng thuộc tính, nhưng có thể gán một giá trị khác cho con trỏ p trong khi numbers luôn trỏ đến phần tử đầu tiên trong số 20 phần tử mà nó được định nghĩa. Vì p - là một biến con trỏ bình thường, còn numbers là một con trỏ hằng
7/2010 Cơ sở lập trình 31
3.2.3 Con trỏ và mảng • Mối liên hệ – Các phần tử của mảng có thể được truy xuất thông qua chỉ số hoặc con trỏ • Ví dụ: a[5] = 0; *(a+5) = 0; – Mảng a[n] thì a là một con trỏ trỏ tới địa chỉ của phần tử đầu tiên a[0]. Hay (a+i) &a[i], *(a+i) a[i]