Bài giảng Kỹ thuật lập trình - Chương 3: Vài kiến thức nâng cao về C, C++ - Vũ Đức Vượng

Chương III  
Vài kiến thức nâng cao về C,  
C++  
3.1 Mảng  
• Là một dãy hữu hạn các phần tử liên tiếp có  
cùng kiểu và tên  
• Có thể là 1 hay nhiều chiều, C không giới  
hạn số chiều của mảng  
• Khai bá o theo syntax sau :  
DataType ArrayName [size];  
Or DataType ArrayName  
[Size1][Size2]...[Sizen];  
• Khởi tạo giá trị cho mảng theo 2 cách  
– C1.Khi khai bá o :  
float y[5]={3.2,1.2,4.5,6.0,3.6}  
int m[6][2] = {{1,1},{1,2},{2,1},{2,2},{3,1},{3,2}};  
char s1[6] ={‘H’,’a’,’n’,’o’,’i’,’\0’}; hoac  
char s1[6] = “Hanoi”;  
char s1[] =“Dai hoc Bach Khoa Hanoi”; L=24  
int m[][] ={{1,2,3},{4,5,6}};  
– C2. Khai báo rồi gán giá trị cho từng phần tử  
của mảng.  
Ví dụ : int m[4];  
m[0] = 1; m[1] = 2; m[2] = 3; m[3] = 4;  
3.2 Con trỏ  
• Khái niệm : Giá trị các biến được lưu trữ trong bộ  
nhớ MT, có thể truy cập tới các giá trị đó qua tên  
biến, đồng thời cũng có thể qua địa chỉ của chúng  
trong bộ nhớ.  
• Con trỏ thực chất là 1 biến mà nội dung của nó là  
địa chỉ của 1 đối tượng khác ( Biến, hàm, nhưng  
không phải 1 hằng số).  
• Có nhiều kiểu biến với các kích thước khác nhau,  
nên có nhiều kiểu con trỏ. Con trỏ int để trỏ tới biến  
hay hàm kiểu int.  
• Việc sử dụng con trỏ cho phép ta truy nhập tới 1 đối  
tượng gián tiếp qua địa chỉ của nó.  
• Trong C, con trỏ là một công cụ rất mạnh, linh hoạt  
• Khai báo con trỏ :  
• Syntax : dataType * PointerName;  
Chỉ rằng đây là con trỏ  
• Sau khi khai báo, ta được con trỏ NULL, vì nó chưa  
trỏ tới 1 đối tượng nào.  
• Để sử dụng con trỏ, ta dùng toán tử lấy địa chỉ &  
PointerName = & VarName  
Ví dụ :  
int a;  
int *p;  
a=10;  
p= &a;  
• Để lấy nội dung biến do con trỏ trỏ tới, ta dùng toán  
tử lấy nội dung *  
• * PointerName  
Ví dụ :  
int i,j, *p;  
i= 5;  
j= *p;  
100  
102  
104  
i
p= & i;  
*p= j+2;  
j
p
100 5 i  
100 5 i  
Gá n i=5  
gá n p = & i  
102  
104  
j
102  
j
p
104 100 p  
100 5 i  
100 7 i  
*p = j+2  
gá n J = *p  
102 5 j  
104 100 p  
102 5 j  
104 100 p  
Chú ý  
• Một con trỏ chỉ có thể trỏ tới 1 đối tượng cùng  
kiểu  
• Toán tử 1 ngôi * và & có độ ưu tiên cao hơn các  
toán tử số học  
• Ta có thể viết *p cho moi nơi có đối tượng mà nó  
trỏ tới xuất hiện  
int x = 5, *p; p = & x; =>  
x=x+10; ~ *p = *p+10;  
• Ta cũng có thể gán nọi dung 2 con trỏ cho nhau  
: khi đó cả hai con trỏ cùng trỏ tới 1 đối tượng  
int x=10, *p, *q;  
p = &x; q = p;  
=> p và q cùng trỏ tới x  
Các phép toán trên con trỏ  
• Một biến trỏ có thể cộng hoặc trừ với 1 số nguyên n  
để cho kết quả là 1 con trỏ cùng kiểu, là địa chỉ mới  
trỏ tới 1 đối tượng khác nằm cách đối tượng đang  
bị trỏ n phần tử  
• Phép trừ giữa 2 con trỏ cho ta khoảng cách ( số  
phần tử ) giữa 2 con trỏ  
• Không có phép cộng, nhân, chia 2 con trỏ  
• Có thể dùng các phép gán, so sánh các con trỏ,  
nhưng cần chú ý đến sự tương thích về kiểu.  
Ví dụ : char *pchar; short *pshort; long *plong;  
sau khi xác lập địa chỉ cho các con trỏ, nếu :  
pchar ++; pshort ++; plong ++; và các địa chỉ ban  
đầu tương ứng của 3 con trỏ là 100, 200 và 300, thì  
kết quả ta có các giá trị tương ứng là : 101, 202 và  
304 tương ứng  
• Nếu viết tiếp :  
plong += 5; => plong = 324  
pchar -=10; => pchar = 91  
pshort +=5; => pshort = 212  
• Chú ý : ++ và – có độ ưu tiên cao hơn * => *p++  
~ *(p++) tức là tăng địa chỉ mà nó trỏ tới chứ  
không phải tăng giá trị mà nó chứa.  
• *p++ = *q++ sẽ tương đương :  
Vì cả 2 phép tăng đều  
*p = *q;  
p=p+1;  
q=q+1;  
diễn ra sau khiphép  
gán được thực hiện  
=> Cần dùng dấu () để tránh nhầm lẫn  
Con trỏ void*  
• Là con trỏ không định kiểu (void *).Nó có thể trỏ tới bất kì  
một loại biến nào. Thực chất một con trỏ void chỉ chứa một địa  
chỉ bộ nhớ mà không biết rằng tại địa chỉ đó có đối tượng kiểu  
dữ liệu gì. => không thể truy cập nội dung của một đối tượng  
thông qua con trỏ void. Để truy cập được đối tượng thì trước  
hết phải ép kiểu con trỏ void về con trỏ có định kiểu của kiểu  
đối tượng  
float x;  
int y;  
void *p; // khai báo con trỏ void  
p = &x; // p chứa địa chỉ số thực x  
*p = 2.5; // báo lỗi vì p là con trỏ void  
/* cần phải ép kiểu con trỏ void trước khi truy cập  
đối tượng qua con trỏ */  
*((float*)p) = 2.5;  
// x = 2.5  
p = &y;  
// p chứa địa chỉ số nguyên y  
*((int*)p) = 2;  
// y = 2  
Con trỏ và mảng  
• Giả sử ta có : int a[30]; thì & a[0] là địa chỉ phần tử đầu  
tiên của mảng đó, đồng thời là địa chỉ của mảng.  
• Trong C, tên của mảng chính là 1 hằng địa chỉ = địa chỉ  
của ftử đầu tiên của mảng  
a = &a[0];  
a+i = &a[i];  
• Tuy vậy cần chú ý rằng a là 1 hằng => khong thể dùng  
nó trong câu lệnh gán hay toán tử tăng, giảm như a++;  
Xét con trỏ : int *pa;  
pa = & a[0];  
=> pa trỏ vào ftử thứ nhất của mảng và :  
pa +1 sẽ trỏ vào phần tử thứ 2 của mảng  
*(pa+i) sẽ là nội dung của a[i]  
Con trỏ xâu  
• Ta có : char tinhthanh[30] =“Da lat”;  
• Tương đương : char *tinhthanh;  
• tinhthanh=“Da lat”;  
• Hoặc : char *tinhthanh =“Da lat”;  
• Ngoài ra các thao tác trên xâu cũng tương tự  
như trên mảng  
• *(tinhthanh+3) = “l”  
• Chú ý : với xâu thường thì không thể gán trực  
tiếp như dòng thứ 3  
Mảng các con trỏ  
Con trỏ cũng là một loại dữ liệu nên ta có thể tạo một  
mảng các phần tử là con trỏ theo dạng thức.  
<kiểu> *<mảng con trỏ>[<số phần tử>];  
• vd : char *ds[10];  
ds là 1 mảng gồm 10 ftử, mỗi ftử là 1 con trỏ kiểu  
char, đcj dùng để lưu trữ đc của 10 xâu ký tự nào đó  
• Cũng có thẻ khởi tạo trực tiếp các giá trị khi khai báo  
• char * ma[10] = {“mot”,”hai”,”ba”...};  
• Chú ý : cần phân biệt mảng con trỏ và mảng nhiều  
chiều. Mảng nhiều chiều là mảng thực sự được khai  
báo và có đủ vùng nhớ dành sẵn cho các ftử. Mảng  
con trỏ chỉ dành không gian nhớ cho các biến trỏ (  
chứa địa chỉ). Khi khởi tạo hay gán giá trị : cần thêm  
bộ nhớ cho các ftử sử dụng => tốn nhiều hơn  
• Một ưu điểm khác của mảng trỏ là ta có  
thể hoán chuyển các đối tượng ( mảng  
con, cấu trúc..) được trỏ bởi con trỏ này  
bằng cách hoán chuyển các con trỏ  
• Ưu điểm tiếp theo là việc truyền tham số  
trong hàm  
• Ví dụ : Vào ds lớp theo họ và tên, sau đó  
sắp xếp để in ra theo thứ tự ABC.  
#include <stdio.h>  
#include <string.h>  
#define MAXHS 50  
#define MAXLEN 30  
int main () {  
int i, j, count = 0; char ds[MAXHS][MAXLEN];  
char *ptr[MAXHS], *tmp;  
while ( count < MAXHS) {  
printf(“ Vao hoc sinh thu : %d “,count+1);  
gets(ds[count]);  
if (strlen(ds[count] == 0) break;  
ptr[count] = ds +count;  
++count;  
}
for ( i=0;i<count-1;i++)  
for ( j =i+1;j < count; j++)  
if (strcmp(ptr[i],ptr[j])>0) {  
tmp=ptr[i]; ptr[i] = ptr[j]; ptr[j] = tmp;  
}
for (i=0;i<count; i++)  
printf(“\n %d : %s”, i+1,ptr[i]);  
}
Con trỏ trỏ tới con trỏ  
• Bản thân con trỏ cũng là 1 biến, vì vậy nó cũng có địa  
chỉ và có thể dùng 1 con trỏ khác để trỏ tới địa chỉ đó.  
• <Kiểu DL> **<Tên biến trỏ>;  
• Ví dụ : int x = 12;  
int *ptr = &x;  
int **ptr_to_ptr = &ptr;  
• Có thể mô tả 1 mảng 2 chiều qua con trỏ của con trỏ  
theo công thức :  
ArrName[i][k] = *(*(ArrName+i)+k)  
Với ArrName+i là địa chỉ của phần tử thứ i của mảng  
*(ArrName+i) cho nội dung ftử trên  
*(ArrName+i)+k là địa chỉ phần tử [i][k]  
• Ví dụ : in ra 1 ma tran vuông và công mỗi ftử của MT với 10  
#include <stdio.h>  
#define hang 3  
#define cot 3  
int main() {  
int mt[hang][cot] = {{7,8,9},{10,13,15},{2,7,8}};  
int i,j;  
for (i=o;i<hang;i++) {  
for (j=0;j<cot;j++) printf(“ %d ”,mt[i][j]);  
printf(“\n”);  
}
for (i=0; i<hang;i++) {  
for (j=0;j<cot;j++) {  
*(*(mt+i)+j)=*(*(mt+i)+j) +10;  
printf(“ %d “, *(*(mt+i)+j); }  
printf(“\n”); }  
}
3.3 Bộ nhớ động – Dynamic memory  
• Cho đến lúc này ta chỉ dùng bộ nhớ tĩnh : tức là khai  
báo mảng, biến và các đối tượng # 1 cách tường  
minh trước khi thực hiện ct.  
• Trong thực tế nhiều khi ta không thể xđịnh trước  
được kích thước bộ nhớ cần thiết để làm việc, và  
phải trả giá bằng việc khai báo dự trữ quá lớn  
• Nhiều đối tượng có kích thước thay đổi linh hoạt  
• Việc dùng bộ nhớ động cho phép xác định bộ nhớ  
cần thiết trong quá trình thực hiện của CT, đồng thời  
giải phóng chúng khi không còn cần đến để dùng bộ  
nhớ cho việc khác  
• Trong C ta dùng cá c hàm malloc, calloc, realloc và  
free để xin cấp phát, tái cấp phát và giải phóng bộ  
nhớ. Trong C++ ta chỉ dung new và delete  
Xin cấp phát bộ nhớ : new va delete  
• Để xin cấp phát bộ nhớ ta dùng :  
<biên trỏ> = new <kiểu dữ liệu>;  
hoặc <biến trỏ> = new <kiểu dữ liệu>[Số ftử];  
dòng trên xin cấp phát một vùng nhớ cho một biến  
đơn, còn dòng dưới : cho một mảng các phần tử có  
cùng kiểu với kiểu dữ liệu.  
• Bộ nhớ động được quản lý bởi hệ điều hành, và  
với môi trương đa nhiệm (multitask interface) thì bộ  
nhớ này sẽ được chia sẻ giữa hàng loạt các ứng  
dụng, vì vậy có thể không đủ bộ nhớ. Khi đó toán  
tử new sẽ trả về con trỏ NULL.  
• ví dụ : int *pds;  
pds = new int [200];  
if (pds == NULL) { // thông báo lỗi và xử lý  
Giải phóng bộ nhớ  
• delete ptr;  
• delete [] ptr;  
// xóa 1 biến đơn  
// xóa 1 biến mảng  
• ví dụ : #include <iotream>  
int main() {  
int i,n; long total=100,x,*l;  
cout << “Vao so ptu “;  
l = new long [n];  
cin >> n;  
if (l==NULL) exit(1);  
for (i=0;i<n;i++){  
cout <<“\n Vao so thu “<< i+1 <<“ :”; cin >> l[i] }  
Cout << “Danh sach cac so : \n”  
for (i=0;i<n;i++) cout << l[i] << “,”;  
delete []l;  
return 0;  
}
Tải về để xem bản đầy đủ
ppt 40 trang Thùy Anh 20040 Free
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Kỹ thuật lập trình - Chương 3: Vài kiến thức nâng cao về C, C++ - Vũ Đức Vượng", để 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:

  • pptbai_giang_ky_thuat_lap_trinh_chuong_3_vai_kien_thuc_nang_cao.ppt