Professional Documents
Culture Documents
Mặt khác, trong kiểm tra kiểu tĩnh, kiểm tra được
thực hiện trong quá trình biên dịch. Trong lược đồ
này, các kiểm tra được thực hiện và báo cáo cho
người lập trình trước khi chương trình được gửi đến
người dùng.
Khi kiểm tra là hoàn toàn tĩnh, hơn nữa, việc duy
trì rõ ràng thông tin kiểu tại thời điểm thực thi là
vô ích vì tính đúng đắn được đảm bảo tĩnh cho mọi
trình tự thực thi. Do đó, việc thực thi hiệu quả hơn,
do không cần kiểm tra trong thời gian chạy. Rõ ràng
là có một cái giá phải trả. Ngay từ đầu, việc thiết kế
một ngôn ngữ định kiểu tĩnh phức tạp hơn so với
một ngôn ngữ động, đặc biệt nếu, cùng với việc
kiểm tra tĩnh, sự an toàn kiểu được đảm bảo cũng
được mong muốn. Ở vị trí thứ hai, quá trình biên
dịch mất nhiều thời gian hơn và phức tạp hơn, một
cái giá mà người ta sẵn sàng trả, vì quá trình biên
dịch chỉ diễn ra một vài lần (liên quan đến số lần
chương trình sẽ được thực thi) và trên hết là do loại
kiểm tra rút ngắn giai đoạn thử nghiệm và gỡ lỗi.
Điều này ít rõ ràng hơn những cái khác nhưng có
liên quan mật thiết với bản chất của kiểm tra kiểu
tĩnh. Các kiểu tĩnh có thể chỉ ra là có lỗi, trong thực
tế, các chương trình không gây ra lỗi kiểu thời gian
chạy.
int x;
if (0==1) x = "pippo";
else x = 3+4;
-> 2 nhánh có kiểu cho x khác nhau.
kiểm tra tĩnh -> báo lỗi.
Nhánh đầu tiên của điều kiện gán biến số nguyên, x,
cho một giá trị không tương thích với kiểu của nó
nhưng việc thực thi đoạn không gây ra lỗi vì điều
kiện không bao giờ được thỏa mãn. Tuy nhiên, mọi
trình kiểm tra kiểu tĩnh sẽ báo hiệu rằng đoạn này
không chính xác vì kiểu của hai nhánh điều kiện
không giống nhau. Kiểm tra tĩnh do đó thận trọng
hơn kiểm tra động
Ví dụ: 1..10 là kiểu của các số nguyên từ 1 đến 10
(bao gồm). Biểu thức của interval type phải được
kiểm tra động để đảm bảo rằng giá trị của nó được
chứa đúng trong khoảng.
Kiểm tra index.
Nói một cách tổng quát hơn, nếu một ngôn ngữ có
mảng muốn kiểm tra xem chỉ mục của mảng có nằm
giữa các giới hạn của mảng đó hay không, thì phải
thực hiện kiểm tra trong thời gian chạy.
8.3 Scalar Types
Scalar types (hoặc đơn giản) là những kiểu mà giá
trị của nó không phải là tổng hợp của các giá trị
khác.
Để dễ hiểu thì viết như này:
type newtype = expression;
Type errors are undecidable
int x;
P;
x = "pippo";
Chỉ khi P kết thúc, trong trường hợp đó, nó sẽ cố
gắng thực hiện phép gán:
x = "pippo"
rõ ràng vi phạm hệ thống kiểu. Mặt khác, nếu P
không kết thúc, nó sẽ không tạo ra lỗi bởi vì quyền
kiểm soát vẫn luôn bên trong P mà không bao giờ
đạt đến nhiệm vụ quan trọng. Do đó, phân đoạn tạo
ra lỗi kiểu khi và chỉ khi P kết thúc
Điều này giới thiệu tên của kiểu, kiểu mới, có cấu
trúc được đưa ra bằng biểu thức. Trong C, chúng
tôi sẽ viết, với cùng một ý nghĩa:
8.3.1 Booleans
8.3.2 Characters
Kiểu ký tự bao gồm:
• Giá trị: một bộ mã ký tự, cố định khi ngôn ngữ
được xác định; phổ biến nhất trong số này là ASCII
và UNICODE.
• Hoạt động: phụ thuộc nhiều vào ngôn ngữ; chúng
ta luôn tìm thấy sự bình đẳng, sự so sánh và một số
cách để chuyển từ một ký tự sang ký tự kế nhiệm
(theo mã hóa cố định) và / hoặc sang ký tự tiền
nhiệm của nó.
Giá trị có thể được lưu trữ, thể hiện và biểu thị.
Biểu diễn trong cửa hàng sẽ bao gồm 1 byte đơn
(ASCII) hoặc 2 byte (UNICODE).
8.3.3 Integers
Loại số nguyên bao gồm:
• Giá trị: Một tập hợp con hữu hạn của các số
nguyên, thường được cố định khi ngôn ngữ được
xác định (nhưng có những trường hợp nó được xác
định bởi máy trừu tượng có thể là nguyên nhân của
một số vấn đề về tính di động). Do các vấn đề về
biểu diễn, khoảng [−2^t, 2^t - 1] thường được sử
dụng.
• Phép toán: so sánh và lựa chọn thích hợp các
toán tử số học chính (cộng, trừ „nhân, chia số
nguyên, phần dư sau khi chia, lũy thừa, v.v.).
Các giá trị có thể được lưu trữ, thể hiện và biểu thị.
Biểu diễn trong bộ nhớ bao gồm một số byte chẵn
(thường là 2, 4 hoặc 8), ở dạng bù 2. (Một số ngôn
ngữ bao gồm hỗ trợ cho các số nguyên có độ dài tùy
ý.)
8.3.4 Reals
Cái gọi là kiểu thực (hoặc số dấu phẩy động) bao
gồm:
• Giá trị: một tập hợp con thích hợp của các số hữu
tỉ, thường được cố định khi ngôn ngữ được định
nghĩa (nhưng có những trường hợp nó được cố định
bởi một cỗ máy trừu tượng cụ thể, một vấn đề ảnh
hưởng sâu sắc đến tính di động); cấu trúc (kích
thước, độ chi tiết, v.v.) của một tập hợp con như
vậy phụ thuộc vào cách biểu diễn được chấp nhận.
• Phép toán: so sánh và lựa chọn thích hợp các
phép toán số chính (cộng, trừ, nhân, chia, lũy thừa,
căn bậc hai, v.v.).
Các giá trị có thể được lưu trữ, thể hiện và biểu thị.
Biểu diễn bộ nhớ bao gồm 4, 8 và 10 byte, ở định
dạng dấu phẩy động như được chỉ định bởi tiêu
chuẩn IEEE 754 (cho các ngôn ngữ và kiến trúc từ
năm 1985).
8.3.5 Fixed Point
• Giá trị: một tập hợp con thích hợp của các số hữu
tỉ, thường được cố định khi ngôn ngữ được xác
định; cấu trúc (kích thước, độ chi tiết, v.v.) của
một tập hợp con như vậy phụ thuộc vào cách biểu
diễn được chấp nhận.
• Phép toán: so sánh và lựa chọn thích hợp các
phép toán số chính (cộng, trừ, nhân, chia, lũy thừa,
chiết xuất căn bậc hai, v.v.).
Các giá trị có thể được lưu trữ, thể hiện và biểu thị.
Biểu diễn trong bộ nhớ bao gồm 4 hoặc 8 byte. Các
giá trị được biểu diễn trong phần bù 2, với một số
bit cố định được dành riêng cho phần thập phân. Số
thực ở điểm cố định cho phép biểu diễn nhỏ gọn
trong một khoảng rộng với few precision places.
8.3.6 Complex
Cái gọi là loại phức hợp bao gồm:
• Giá trị: một tập hợp con thích hợp của các số
phức, thường được cố định bởi định nghĩa của ngôn
ngữ; cấu trúc (kích thước, độ chi tiết, v.v.) của tập
hợp con này phụ thuộc vào cách biểu diễn được
chấp nhận.
• Phép toán: so sánh và lựa chọn thích hợp các
phép toán số chính (tổng, trừ, nhân, chia, lũy thừa,
lấy căn bậc hai, v.v.).
Các giá trị có thể được lưu trữ, thể hiện và biểu thị.
Biểu diễn bao gồm một cặp giá trị dấu phẩy động
8.3.7 Void
Trong một số ngôn ngữ, tồn tại một kiểu nguyên
thủy mà ngữ nghĩa của nó là kiểu có một giá trị duy
nhất. Đôi khi nó được ký hiệu là void (ngay cả khi,
về mặt ngữ nghĩa, tốt hơn là nên gọi nó là đơn vị, vì
nó không phải là tập hợp rỗng mà là một đơn vị):
• Giá trị: chỉ một, có thể được viết là ().
• Hoạt động: không có.
Mục đích của một loại loại này là gì? Nó được sử
dụng để biểu thị loại hoạt động sửa đổi trạng thái
nhưng không trả về giá trị. Ví dụ, trong một số ngôn
ngữ, (nhưng không phải trong C hoặc Java), các
phép gán có kiểu void.
8.3.8 Enumerations
Ngoài các kiểu được xác định trước, chẳng hạn
như những kiểu đã giới thiệu ở trên, chúng tôi cũng
tìm thấy trong một số ngôn ngữ những cách khác
nhau để xác định kiểu mới. Enumerations and
intervals là kiểu vô hướng được xác định bởi người
dùng.
An enumeration type bao gồm một tập hợp các
hằng số cố định, mỗi hằng số được đặc trưng bởi tên
riêng của nó. Bằng ngôn ngữ giả của chúng tôi,
chúng tôi có thể viết định nghĩa sau
Hôm nay học -> nắm đc cơ chế gọi chương trình con
và data types
=====================================
Kiểu con trỏ
Values are memory addresses and apecial value
nil.
Sử dụng
1 truy cập gián tiếp thông qua địa chỉ
2 cách để quản lý bộ nhớ động
• biến heap-động thường không có tên liên
quan (biến ẩn danh)
• chỉ có thể truy cập thông qua một con trỏ
hoặc một tham chiếu
Con trỏ không phải là:
• các kiểu cấu trúc (mặc dù thường được định
nghĩa bằng toán tử kiểu)
• kiểu scalar (giá trị không phải là dữ liệu, mà
là tham chiếu đến các biến)
• typed
• có thể trỏ đến bất kỳ đâu (như trong hợp ngữ)
• cực kỳ linh hoạt -> do đó cần thận trọng hơn
• các phép toán: * - dereference, & - address của
một biến
int list[10]
int *ptr;
ptr = list;
*(ptr+1) = list[1]
*(ptr+index) = list[index]
ptr[index] = list[index]
con trỏ trỏ đến các hàm
• được sử dụng để chuyển các hàm dưới dạng tham
số
con trỏ kiểu void *
• có thể trỏ đến các giá trị thuộc bất kỳ loại nào
(generic pointers)
• không thể được tham chiếu (vì vậy người kiểm tra
loại sẽ không phàn nàn)
• sử dụng: tham số / kết quả của các hàm hoạt
động trên bộ nhớ (ví dụ: malloc)
=====================================
Một biến của kiểu tham chiếu đề cập đến một đối
tượng hoặc một giá trị trong bộ nhớ, không phải địa
chỉ bộ nhớ.
không có điểm để làm số học (không có các
phép toán số học trên nó)
• C ++
• con trỏ không đổi, luôn được tham chiếu
ngầm
• sử dụng: truyền tham số - giao tiếp hai chiều
(lợi thế hơn con trỏ: không cần tham khảo)
• Java
• không hằng số, có thể trỏ đến bất kỳ phiên
bản nào của cùng một lớp
• được sử dụng để tham chiếu các cá thể lớp
• không có explicit deallocation (no dangling
references)
pointers vs references
Việc đưa (con trỏ) của họ vào các ngôn ngữ cấp cao
là một bước lùi mà chúng ta có thể không bao giờ
khôi phục được.
khai báo một con trỏ có thể thay đổi thành một số
nguyên không đổi. Không thể thay đổi giá trị số
nguyên thông qua con trỏ này, nhưng con trỏ có thể
được thay đổi để trỏ đến một số nguyên không đổi
khác. (trỏ được đến số nguyên không đổi khác)
2.int * const ptr;
khai báo một con trỏ hằng cho dữ liệu số nguyên có
thể thay đổi. Giá trị số nguyên có thể được thay đổi
thông qua con trỏ này, nhưng con trỏ có thể không
được thay đổi để trỏ đến một số nguyên không đổi
khác.
Ngăn cấm thay đổi địa chỉ mà ptr chứa hoặc giá trị
mà nó trỏ tới.
Con trỏ thường được đặt thành 0 để báo hiệu rằng
chúng hiện không hợp lệ.
1 int * myFunc () {
2 int phantom = 4;
3 return & phantom ;
4}
int y;
int &x = y; //Makes x a reference to , or alias of , y
Thay đổi x và thay đổi y là như nhau.
Tham chiếu chỉ là các con trỏ được tham chiếu mỗi
khi chúng được sử dụng. Cũng giống như con trỏ,
bạn có thể chuyển chúng xung quanh, trả lại chúng,
đặt các tham chiếu khác cho chúng, v.v.
Sự khác biệt duy nhất giữa sử dụng con trỏ và sử
dụng tham chiếu là:
• Các references là loại được tham chiếu trước -
bạn không bỏ qua chúng một cách rõ ràng.
• Bạn không thể thay đổi vị trí mà một tham chiếu
trỏ đến, trong khi bạn có thể thay đổi vị trí mà một
con trỏ trỏ đến. Do đó, các tham chiếu phải luôn
được khởi tạo khi chúng được khai báo.
• Khi viết giá trị mà bạn muốn tham chiếu đến, bạn
không đặt dấu & trước giá trị đó để lấy địa chỉ của
nó, ngược lại bạn cần thực hiện việc này đối với con
trỏ. (tham khảo địa chỉ con trỏ &var)
Còn đối với tham chiếu không cần