Tải bản đầy đủ (.docx) (19 trang)

Chương 6 các kiểu dữ liệu

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.64 MB, 19 trang )

Chương 6 :các kiểu dữ liệu
I. Kiểu dữ liệu mảng
 Mảng là tập hợp các biến cùng tên, cùng kiểu dữ liệu nhưng khác chỉ số,
xác định đựa vào phần tử đầu tiên
 Một số vấn đề cần quan tâm khi làm việc với kiểu dữ liệu mãng
i. Các kiểu dữ liệu của chỉ mục
ii. Có kiểm tra miền giá trị của chỉ mục không
iii. Có bao nhiêu chiều trong mảng
iv. Khi nào cấp pháp vùng nhớ cho mảng
v. Có thể cấp phát giá trị ban đầu cho mảng
vi. Tách một phần của mãng
1. Các loại mảng
 Mảng tĩnh(Static array) : kích thước của nó được xác định
tại thời điểm biên dịch và được cấp phát tại thời điểm load ứng
dụng vào vùng nhớ data segments,và địa chỉ không đổi trong suốt
thời gian thực thi của chương trình
 Ưu điểm :
o Truy xuất trực tiếp
 Fixed stack-dynamic array :mảng được cấp phát động trên ngăn
xếp nhưng kích thước cố định, đây là loại mảng khai báo cục bộ trong
chương trình con,cấp pháp tại thời điểm thực thi câu lệnh khai báo
o Ưu điểm :sử dụng tối ưu ô nhớ
Stack-dynamic array:mảng có kích thước động, được lưu trữ
trong ngăn xếp:mảng được khai báo trong chương trình con và kích
thước sẽ tự động cấp phát tại thời điểm thực thi chương trình
o ví dụ trong ngô ngử Ada
procedure foo (size: integer) is
M: array (1 size, 1 size) of real
begin

end foo;


o Khi biên dịch hệ thống sẽ cấp cho thủ tục foo một
con trỏ dùng để trỏ đến ô nhớ chứa mảng M
o Khi thực thi hệ thống sẽ cấp pháp cho mảng M một
vùng nhớ còn khả dụng trên ngăn xếp ứng với kích
thước của nó và con trỏ sẽ trỏ tới ô nhớ này
 Fixed heap-dynamic array( Loại mảng có kích thước động được
cấp phát trên vùng nhớ Heap), nhưng sau khi cấp phát thì vị trí của
nó sẽ cố định trên vùng nhớ Heap
o Vùng nhớ sẽ được cấp phát khi có yêu cầu bằng các
lệnh cấp phát như malloc hay alloc trong c và vùng
nhớ được cấp phát trong Head
 Heap-dynamic array: có kích thước động được cấp phát
trên Head nhưng kích thước và địa chỉ của mảng có thể thay
đổi nhiều lần trong chương trình
o Ví dụ
@alpha = ("a" "z");
push(@array, <element>);
pop(@array);
2. Khởi tạo giá trị ban đầu cho mảng
 Trong khi khai báo biến mảng ta có thể khởi tạo giá trị ban đầu cho
mảng(khác với gán giá trị :gán là lệnh gán giá trị cho biến nhưng
không nằm ở dòng khai báo (biến có kiểu static trong chương trình
con lệnh khởi tạo chỉ thực hiện một lần khi chương trình con được gọi
lần đầu tiên, có lệnh gán sẽ được thực hiện khi chương trình con
được gọi)
int list[] = {4, 5, 7, 83}
3. Mảng răng cưa
 Là mảng nhiều chiều (2 chiều) nhưng có số phần tử của các dòng
không bằng nhau
 Hướng tiếp cận là xem mảng hai chiều là một mảng một chiều mà

mỗi phần tử của nó là một mảng một chiều
 Ưu điểm :tiết kiệm không gian vùng nhớ
Ví dụ :
4. Slices:
 là một phần của mảng, và giúp truy xuất vào một thành phần của
mảng như là một đơn vị duy nhất
 chỉ xuất hiện trong các ngôn ngữ có các toán tử thao tác trên mảng
 ví dụ
5. Thực thi mảng
 Ta có thể truy xuất từng phần tử của mảng thông qua chỉ mục của

 Nhưng thực chất để truy xuất đến từng phần tử thông qua chỉ mục,
trình biên dịch phải sử dụng hàm truy xuất (Access function) để tính
toán từ chỉ mục tính ra địa chỉ lưu trữ của phần tử trong bộ nhớ
 Ví dụ
Int a[5];khai báo mảng số nguyên a có 5 phần tử mỗi
phần tử chiếm 4 byte trong bộ nhớ
A[2]=7;
Hàm Access function tính toán địa chỉ như sau :dia chỉ của
con trỏ a+2*sizeof(int)=7
 Mảng một chiều được cấp phát một vùng nhớ liên tục trong bộ
nhớ, hàm Access function là f =địa chỉ mang+Index*sizeof(kiểu dữ
liệu)
 Đối với ngôn ngữ hỗ trợ mảng nhiều chiều :hàm truy xuất sẽ ánh
xạ từ mảng nhiều chiều thành mảng một chiều vì địa chỉ bộ nhớ là
tuyến tính
 Hầu hết các ngôn ngữ lập trình Thứ tự của mảng hai chiều là thứ
tự theo dòng, riêng fortran thứ tự theo cột
 Thứ tự tổ chức lưu trữ của mảng nhiều chiều sẽ ảnh hướng rất qua
trọng đối với tốc độ xử lý của chương trình trong trường hợp truy xuất

mảng có dữ liệu lớn
o CPU làm việc với dữ liệu lưu trữ trên Cache
o Nếu dữ liệu CPU cần xử lý chưa có trên Cache thì hệ
thống sẽ đọc dữ liệu từ ram vào Cache, mỗi lần đọc
có thể 32 bit hay 64 bit hay một giá trị khác tùy vào hệ
thống
o Nếu dữ liệu của mảng lớn thì sẽ có nhiều lần hệ
thống đọc dữ liệu từ ram đưa vào Cache (tốn nhiều
thời gian)
o Nếu trong quá trình xữ lý ta xử lý các phần tử không
liên tục , thì hệ thống phải đọc dữ liệu từ ram đưa
vào Cache nhiều lần
o Để chương trình xử lý nhanh ta phải biết thứ tự tổ
chức lưu trữ của mảng để có những xử lý thích hợp
 Tổ chức lưu trữ dữ liệu mảng của trình biên dịch
Ví dụ để truy xuất đến phần tử ở dòng I, cột j
 Nếu mảng một chiều để truy xuất đến phần tử thứ k trong mảng ta
hàm truy cập sẽ tính địa chỉ của phần tử k theo công thức
address(a[k]) = address(a[lower_bound]) + ((k - lower_bound) *
element_size)
 Nếu mảng 2 chiều theo thứ tự dòng hàm truy xuất tính địa chỉ của
phần tử dòng I cột j theo công thức
address(a[i,j]) = address(a[row_lb, col_lb])+ ((i-row_lb)*N + (j-
col_lb)) * element_size
với N là số cột
 Nếu mảng 2 chiều theo thứ tự cột hàm truy xuất tính địa chỉ của
phần tử dòng I cột j theo công thức
address(a[i,j]) = address(a[row_lb, col_lb])+ ((j-col_lb)*M + (i-
row_lb)) * element_size
với M là số dòng

6. Mảng liên hợp :
 Là một mảng không có thứ tự, các phần tử của mảng được truy
xuất thông qua khóa
 Mỗi phần tử của mảng liên hợp bao gồm hai thành phần
<Key,Value>
 Một số vấn đề gặp phải khi thiết kế mảng liên hợp
o Cách thức tham chiếu đến một phần tử của mảng
o Kích thước của mảng liên hợp là tĩnh hay động
 Trong ngôn ngữ C++ mảng liên hợp được gọi là HashTable
 Kích thước của HashTable là động
o HashTable danhsach=new HashTable();
o danhsach.Add(“apple”,”Trai tao”);
o danhsach[“orange”]=“Trai cam”;
7. Bảng Ghi(Record)
 Là một tập một tập hợp bao gồm nhiều thành phần mỗi
thành phần có tên và kiểu dữ liệu khác nhau
 Các vấn đề cần quan tâm khi thiết kế
o Cách thức tham chiếu đến từng thành phần trong
bảng ghi
o Có thể tham chiếu giản lược không
 Khi khai báo một thành phần(một biến) có 1 kiểu dữ liệu a,
biến này sẽ được cấp phát ô nhớ có địa chỉ phải chia hết
cho kích thước của kiểu dư liệu a(nguyên tắc dóng hàng)
 Ví dụ
Struct NhanVien
{
Char Manv[2];
Int Tuoi;//int chiem 4 byte
Float Hsluong;//float chiếm 4 bite
}

Nhanvien a;
Khi đó hệ thống sẽ cấp cho biến a
Manv
tuoi
HSL
Biến a chiếm 12 byte
Khi khai báo một mảng gồm n phần tử của a thì địa chỉ của
mỗi phần tử a phải chia hết cho 12
Struct NhanVien
{
Int Tuoi;//int chiem 4 byte
Float Hsluong;//float chiếm 4 bite
Char Manv[2];
}
Nhanvien a;
Khi đó hệ thống sẽ cấp cho biến a
Tuổi
HSluong
Tuổi
Biến a chiếm 10 byte
Khi khai báo một mảng gồm n phần tử của a thì địa chỉ của
mỗi phần tử a phải chia hết cho 10
8. Kiểu Union(kiểu hội)
 Cùng một vùng nhớ nhưng có thể chứa nhiều kiểu dữ liệu
khác nhau trong từng thời điểm khác nhau=> tiết kiệm bộ
nhớ => không an toàn nếu không kiểm tra kiểu
 Vấn đề
o Có nên kiểm tra kiểu dữ liệu hay không
o Có cho phép định nghĩa kiễu hội trong bảng ghi
không

 Để kiểm tra kiểu của kiểu hội, khi khai báo phải khai báo bộ
chỉ định kiểu( tag hoặc discriminant)
 bộ chỉ định kiểu sẽ cho biết tại một thời điểm kiễu dữ liệu lưu
trong union là gì
 ví dụ trong pascal khai báo bảng ghi có sử dụng bộ chỉ định
kiểu như sau :
type rec_var =
record tag : Boolean of
true : (blint : integer);
false : (blreal : real);
end;
 vấn đề của ngôn ngữ pascal là kiểm tra kiểu không hiệu quả
 ví dụ
var x: rec_var; y: real;
x.tag := true; { it is an integer }
x.blint := 47; { ok }
x.tag := false; { it is a real }
y := x.blreal; { assigns an integer to real}
 trong ngôn ngữ Ada việc kiểm tra kiểu union chặt chẽ hơn

 Kiểu hội của Ada an toàn hơn Pascal vì
o Mọi bảng ghi biến đổi luôn luôn phải có biệt thức đi
kèm
o Biệt thức phải có giá trị mặc định
o Người sử dụng không thể tạo và sử dụng union
không nhất quán giữa định thức và dữ liệu
 Ví dụ nếu trong pascal ta gán dữ liệu của định
thức là real nhưng truy xuất dữ liệu int, trong
ada thì không thể

 Cách thức truy xuất của Union
9. Kiểu tập hợp(Type)
 Là tập hợp các giá trị được định nghĩa sẳn
 Chỉ có pascal hỗ trợ
 Số giá trị tối đa<100
 Hỗ trợ các toán tử :hội,giao
 Ví dụ

 Thực thi tập hợp
o Dùng một chuỗi bit để biểu diển
o Ví dụ dùng một chuổi có 16 bit để biểu diễn một tập
hợp các ký tự từ [‘a’ ’p’] với qui định
 1 :có
 0 ; không có
 Ví dụ tập hợp [‘a’, ‘c’, ‘h’, ‘o’] được biểu diển
bằng chuổi 101000010000001
 Phép hội :or
 Phép giao :and
10. Kiểu con trỏ :
 Là một loại biến dùng lưu trữ địa chỉ của ô nhớ và có thể nhận giá tri NULL
 Cung cấp một khả năng để truy cập địa chỉ gián tiếp
 Cung cấp con đường để quản lý bộ nhớ động
o Con trỏ có thể được dùng để truy cập đến vị trí chứa dữ liệu
được cung cấp động( thường gọi là Head)
 Các vấn đề của con trỏ :
o Phạm vi và thời gian sống của biến con trỏ
o Thời gian sống của bộ nhớ Head
o Có hạn chế kiểu dữ liệu mà con trỏ mà con trỏ có thể
trỏ đến hay không
o Con trỏ dùng quản lý bộ nhớ động (truy xuất trực tiếp)

hay bộ nhớ truy xuất gián tiếp hay cả hai
o Hỗ trợ kiểu con trỏ hay kiểu tham chiếu hay cả hai
o Ví dụ :a=*p(lấy giá tri của ô nhớ gán cho a(reference))
 Các toán tử trên con trỏ\
o Phép gán : dùng để gán giá trị địa chỉ cho con trỏ
o Dereferencing : truy xuất đến giá trị của ô nhớ mà
con trỏ đang trỏ đến
o Dereferencing : có thể tường minh hay không tường
minh
o Ví dụ : trong ngôn ngữ lập trình c/c++ dùng dấu * đễ
Dereferencing
J= *ptr
 Vấn đề con trỏ
o Contrỏ treo(Dangling pointers)
 Con trỏ trỏ đến một vùng nhớ động của một
biết đã bị xóa
 Ví dụ
int *p1,*p2;
p1= new int();
*p1=5;
p2=p1;
delete(p1);
printf ("%d",*p2);
p2 trỏ đến một vùng nhớ đã bị xóa:con trỏ treo;
 Một ngôn ngữ nếu cho phép khởi tạo và hủy
một con trỏ thì sẽ bị con trỏ treo
o Tạo rac trong bộ nhớ Head
 Ví dụ con trỏ p1 được thiết lập chỉ đến một
vùng nhớ trong bộ nhớ Head
 Gán p1 cho một vùng nhớ khác ,vùng nhớ cũ

sẻ tạo thành rác, không sử dụng được
Int *p1,p2;
P1= new int();
P2= new int();
P1=p2;
 Con trỏ trong ngôn ngử Palca
o Con trỏ cả hai dùng quản lý bộ nhớ động (truy xuất
trực tiếp) hay bộ nhớ truy xuất gián tiếp
o Truy xuất tường minh thông qua ^
o Bị con trỏ treo
 Con trỏ trong ngôn ngữ Ada
o Được phép cấp phát vùng nhớ động và hệ thống tự
động giải phóng khi biến con trỏ tra khỏi phạm vi
nhưng tùy trình biên dich
o Mọi con trỏ đều có giá trị mặc định là null
 Con trỏ trên c,C++
o Rất linh động
o Cho phép thao tác trên con trỏ như thao tác trên số
o Ví dụ
 *p=*p1-*p2
 *p=*p+5
o Cho phép con trỏ có thể trỏ đến kiểu khác thông qua
lệnh ép kiểu
o Được dùng để truyền tham số trong chương trình con
 Con trỏ trong ngôn ngữ Fortran 95
o Bị con trỏ treo
o Truy xuất không tường minh
o Ví dụ
 Kiểu tham chiếu
o Là một trường hợp đặt biệt của kiểu con trỏ

o Java chỉ hỗ trợ kiểu tham chiếu
 Thực thi con trỏ
o Hầu hết các máy tính biểu diễn con trỏ bằng một giá
trị đơn
o Intel microprocessors biểu diễn địa chỉ bằng hai giá trị
segment và offset
o Giải quyết vấn đề con trỏ treo
 Tombstone(bia mộ)
• Thêm một con trỏ trung gian trỏ đến địa
chỉ của biến ,con trỏ này là Tomdstone
• Các con trỏ trỏ đến biến thông qua
Tomdstone
• Khi xóa hết tất cả các con trỏ trỏ đến
Tomdstone, thì con trỏ Tomdstone sẻ
gán= null

hệ máy macintosh dùng kỹ thuật này để giải quyết con trỏ treo
 Dùng khóa và chìa khóa
 khi cấp phát một vùng nhớ cho con trỏ thì trên con trỏ và
vùng nhớ được cấp phát một giá trị gọi là <khóa,địa chỉ>
trên con trỏ,và một giá trị là “ổ khóa” trên ô nhớ
 khi truy xuất ô nhớ, hệ thống sẽ kiểm tra giá trị khóa và ổ
khóa có trùng không, nếu trùng thì cho truy xuất
 khi một con trỏ xóa thì thuộc tính khóa của nó sẽ xóa
11. thu dọn rác trong bộ nhớ
 nhiều ngôn ngữ lập trình cố gắng không tạo rác trên head khi lập trình
nhưng điều này rất khó
 có nhiều ngôn ngữ lập trình xây dựng hệ thống dọn rác tự động
 Kỹ thuật dọn rác “đếm các tham chiếu”
 Khi có một con trỏ trỏ đến một vùng nhớ thì tăng biến đếm

lên một
 Khi xóa một con trỏ trỏ đến vùng nhớ sẽ giảm biến đếm
xuống 1
 Và chỉ cho phép giải phóng vùng nhớ khi biến đếm =0
 không làm việc được nếu tham chiếu vòng tròn
ví dụ



 Kỹ thuật quét và đánh dấu
• Bộ dọn rác sẽ duyệt qua vùng nhớ head và đánh dấu
tất cả các ô nhớ là “useless”
• Bộ dọn rác sẽ dò tìm tất cả các vùng nhớ trên head
đang được trỏ tới bởi các con trỏ ngoài head và gán là
“useful”
• Bộ dọn rác sẽ duyệt lại vùng nhớ head và giải phóng
các ô nhớ là “useless”
• Một số vấn đề của bộ thu dọn rác :
o Phải biết được vị trí bắt đầu và kết thúc của vùng nhớ
đang sử dụng
o Vùng nhớ cấp phát cho con trỏ có thể lớn hơn kích
thước của nó
 Kỹ thuật đảo con trỏ
• Trong bước 2 của kỹ thuật quét và đánh dấu là dò tìm
tất cả những ô nhớ đang được dùng bởi các con trỏ
ngoài head, quá trình dò tìm này là một quá trình đệ
quy
• Trong quá trình xử lý đệ quy có thể phát sinh các biến
và các biến này sẽ lưu lại trong ngăn xếp , quá trình
này tốn ô nhớ của ngăn xếp

• Giả sử bộ đọn rác chỉ được kích hoạt khi bộ nhớ đã
gần đầy => không còn ô nhớ để cấp pháp
• Dùng kỷ thuật đảo con trỏ

 Kỹ thuật dừng và sao chép
• Chia bộ nhớ head thành hai vùng bằng
nhau
• Chỉ sử dụng một vùng
• Khi vùng chứa đã gần đầy, bộ đọn rác
sẽ tìm kiếm những vùng nhớ còn dùng
sẽ chép vùng nhớ đó sang vùng còn lại,
trong khi sao chép tìm những vị trí thích
hợp để chống phân mảnh
• Đưa vùng nhớ này thành vùng nhớ hoạt
động
• Giải phóng vùng nhớ còn lại

×