Giới thiệu : • Các vấn đề cơ bản về ngữ nghĩa của biến Ngôn ngữ mệnh lệnh là sự trừu tượng hóa của kiến trúc von Neumann Biến là sự trừu tượng hóa của các ô nhớ Biến được đặt trương b
Trang 1Chương 5 Names, Binding, Type Checking and Scopes
I. Giới thiệu :
• Các vấn đề cơ bản về ngữ nghĩa của biến
Ngôn ngữ mệnh lệnh là sự trừu tượng hóa của kiến trúc von Neumann
Biến là sự trừu tượng hóa của các ô nhớ
Biến được đặt trương bởi nhiều thuộc tính, nhưng thuộc tính quan trọng nhất là kiểu dữ liệu(data type)
• Để thiết kế một kiểu dữ liệu có một số vấn đề cần quan tâm
Phạm vi và thời gian sống của biến
Kiểm tra kiểu và khởi tạo giá trị ban đầu cho biến
Tương thích kiểu
• Tên
Là một chuỗi dùng để định danh một thực thể trong chương trình
Name :tên biến, nhãn chương trình, tên chương trình con, tên cấu trúc, tên lớp
Các vấn đề khi đặt tên
Chiều dài tối đa :nếu ngắn quá thì không diễn đạt được ý nghĩa của biến
Ví dụ : trong FORTRAN I: maximum 6
FORTRAN 90 and ANSI C: maximum 31
Ada and Java: no limit
–C++: không xác định phù thuộc vào trình biên dịch
Ký tự kết nối(_)
Modula-2 and FORTRAN 77 don't allow
–Others do
Phân biệt hoa, thường
Nếu ngôn ngữ có phân biệt tên hoa và tên thường sẽ ảnh hưởng đến tính dễ đọc của ngôn ngữ
Rất hay gặp lỗi vì nhập văn bản
Ví dụ :c,c++,java phân biệt hoa thường,Ada ,vb basic không phân biệt
Từ đặt biệt :được dùng để làm cho chương trình dễ đọc hơn
Từ khóa (keyword)là một từ chỉ có ý nghĩa đặt biệt khi nằm trong một ngữ cảnh nào đó
Từ dành riêng(reserved word) là từ đặt biệt mà người dùng không thể dùng để đặt tên cho một đối tượng
Tên được định nghĩa sẳn (predefined name) là một từ dành riêng nhưng người dùng có thể định nghĩa lại
•Ví dụ nhưng lệnh printf,scanf là từ dành riêng để xuất
và nhập dữ liệu trong c trong thư viện stdio.h, nếu
Trang 2trong chương trình không include thư viện này thì có thể dùng printf và scanf làm tên biến
• Biến
Là sự trừu tượng hóa của ô nhớ
Biến sẽ được liên kết với một tập các ô nhớ vật lý
Thay thế một địa chỉ tuyệt đối bằng một tên
Nếu chương trình không dùng biến để chỉ ô nhớ chưa dữ liệu, thì phải dùng địa chỉ tuyệt đối để lưu dữ liệu, khi thực hiện chương trình dữ liệu phải đươc đưa vào ô nhớ tuyệt đối này, nhưng với tên biến thì chương trình sẽ truy cập dữ liệu thông qua tên biến,khi chạy thì hệ thống mới cấp địa chỉ tuyệt đối cho biến
Một biến có sáu thuộc tính quan trọng
Name :
Địa chỉ(L-value):là địa chỉ của vùng nhớ đã cấp phát cho biến
Ví dụ :cho lệnh a=a+5
hệ thống sẽ thực hiện như sau :
L-value(a)=R-value(a)+5
Cùng một tên biến có thể có những địa chỉ khác nhau tại những vị trí khác nhau trong chương trình
•ví dụ : biến cục bộ :hai biến có tên giống nhau
có thể được khai báo trong hai chương trình con khác nhau
Một biến có thể có những địa chỉ khác nhau trong từng thời điểm khác nhau của chương trình
•Ví dụ gọi đệ qui một chương trình con A, trong chương trình con này có khai báo biến cục bộ I,tại mỗi thời điểm gọi đệ qui biến I được cấp phát những địa chỉ khác nhau
Có thể có hai hoặc nhiều biến cùng truy cập đến một địa chỉ vùng nhớ gọi là đa danh(aliases)
•Ảnh hưởng đến tính dễ đọc
•Khó Kiểm soát chương trình
•Ví dụ
Trang 3 Kiểu dữ liệu(Type):bao gồm 2 thành phần
Miền giá trị
Các toán tử được định nghĩa trên kiểu dữ liệu đó
Trang 4 Nếu kiểu dấu chấm động :kiểu cũng xác định độ chính xác
Giá trị (RValue):giá trị của vùng nhớ cấp phát cho biến
Thời gian sống(Lifetime )
Phạm vi( Scope)
Kết nối (Binding):là sự kết hợp giữa một thuộc tính và một thực thể hoặc giữa một toán tử và một ký hiệu
Thời điểm kết nối(Binding time) : là thời điểm mà tại đó kết nối xảy ra
Thời điểm thiết kế chương trình :ví dụ như kết nối giữa ký hiệu của toán tử với toán tử(ký hiệu + :dùng cộng hai số)
Thời điểm xây dựng chương trình dịch :kết nối kiểu
có dấu chấm động với một biểu diển của nó
Thời gian biên dịch :giữa tên biến và kiểu của nó
Link time:gọi đến các hàm thư viện
Load time:Tải chương trình vào bộ nhớ,cấp phát vùng nhớ cho biến toàn cục
Runtime :tại thời điểm thực thi biến cục bộ sẽ được khởi tạo khi hàm chứa nó được gọi
Ví dụ cho đoạn lệnh
Int count;
… Count=count+5
•Định nghĩa kiểu int :thời điểm thiết kế chương trình
•Định nghĩa kiểu của biến Count :tại thời điểm biên dịch
•Định nghĩa Lệnh gán :thời điểm thiết kế trình biên dịch
•Gán giá trị cho biến count :tại thời điểm thực thi câu lệnh gán
•Định nghĩa toán tử +:thời điểm thiết kế chương trình
•Ngữ nghĩa của phép + sẽ được kết nối tại thời điểm biên dịch do khi đó hệ thống mới biết là phép cộng được thực hiện trên kiểu dữ liệu gì
•Giá trị 5 :thời điểm xây dựng trình biên dịch
Sự kết nối được xem là tĩnh nếu nó xảy ra trước thời điểm chạy chương trình(Thời điểm biên dịch)và không đổi trong suốt thời gian thực thi chương trình
Một kết nối là động nếu nó xảy ra trong khi thực thi chương trình và có thể thay đổi trong lúc thực thi
• Kết nối kiểu
Trang 5 Trước khi biến được tham chiếu trong chương trình nó phải được liên kết với một kiểu dữ liệu nào đó
Có hai vấn đề quan trọng trong khi phải làm rõ
Làm sao để chỉ ra kiểu dữ liệu của biến
Khi nào kết nối kiểu sẽ xãy ra
Kiểu của biến có thể được chỉ ra khi khai báo, khai báo có thể tường minh hay không tường minh
Việc khai báo biến tường minh hay không tường minh tạo một kết nối tĩnh khi thực thi chương trình
Một khai báo tường minh là câu lệnh dùng để khai báo kiểu cho biến
Ví dụ
• int a;//khai báo a có kiểu interger
Một khai báo không tường minh là kỹ thuật dùng biến mà không cần khai báo,việc xác định kiểu của biến thông qua lệnh truy xuất đầu tiên đến biến
Ưu điểm
•Ưu điểm :Dễ viết :sử dụng biến mà không cần khai báo
•Khuyết điểm :độ tin cậy giảm
Khai báo biến và định nghĩa biến
Khai báo :là lệnh cho biết biến có tên là gì, có kiểu dữ liệu gì? Nhưng không cấp phát vùng nhớ
Định nghĩa :là lệnh cho biết biến có tên là gì, có kiểu
dữ liệu gì và yêu cầu hệ thống cấp phát vùng nhớ
Ví dụ trong php ta có đoạn lênh sau:
<?php
$a = 1;//Định nghĩa
$b = 2;//Định nghĩa
function Sum()
{
global $a, $b;//khai báo $b = $a + $b;
}
?>
Kết nối kiểu động :kết nối xảy ra tại thời điểm thực thi lệnh gán
Ví dụ trong PHP
<?php
Trang 6$a=array(1,4,5);//$a là 1 mảng
$a=”vntan”;//@a là một chuổi
?>
Ưu điểm :linh động khi xây dựng chương trình con khi
đó ds tham số có thể nhận kiểu dữ liệu bất kỳ
Khuyết điểm:
•chi phí thời gian cao vì phải kiểm tra kiểu trong khi thực thi
•Dò tìm kiểu của trình biên dịch khó khăn, hệ thống thực thi ngôn ngữ để lưu lại kiểu dữ liệu trong thời gian thực thi chương trình
•Chỉ dùng trình thông dịch để dịch chương trình vì nếu dùng trình biên dịch để dịch thì đối với một biết chỉ được liên kết với kiểu dữ liệu một lần khi biên dịch và các toán tữ được dùng với biến hay danh sách tham số của chương trình con sẽ được xác định tại thời gian biên dịch
Suy luận kiểu :được dùng trong các ngôn ngữ ML, Miranda and Haskell, kiểu được suy luận thông qua phép gán trong một ngữ cảnh nào đó
Ví dụ :
• Kết nối vùng nhớ và thời gian sống
Kết nối vùng nhớ cho một biến là quá trình cấp pháp hay thu hồi vùng nhớ của một biến
Cấp phát vùng nhớ :yêu cầu một vùng nhớ từ vùng nhớ khả dụng
Giải phòng vúng nhớ : trả vùng nhớ được dùng cho biến về vùng nhớ khả dụng
Trang 7 Thời gian sống của một biến là khoảng thời gian từ khi biến được khởi tạo cho đến khi biến bị thu hồi vùng nhớ
• Phân loại biến theo thời gian sống : có 4 loại biến
Biến tĩnh(Static variable) :là loại biến kết nối với vùng nhớ trước khi thực thi và duy trì cho đến khi kết thúc chương trình (biến toàn cục)
Ưu điểm :
•Tính hiệu quả : truy xuất trực tiếp đến địa chỉ của ô nhớ
•Hỗ trợ quản lý thông tin sử dụng chương trình con (biến cục bộ có kiểu static trong c)
Nhược điểm :
•thiếu tính linh động, không đệ quy
•không chia sẽ vùng nhớ cho biến khác
Biến ngăn xếp(Stack-dynamic variable) :kết nối vùng nhớ khi câu lệnh khai báo biến cục bộ được khai báo trong chương trình coni, hầu hết các thuộc tính của biên đều được xác định tại thời điểm biên dịch nhưng địa chỉ của ô nhớ mà biến truy xuất sẽ được cấp phát lúc thực thi
Kết nối vùng nhớ khi câu lệnh khai báo được thực thi
Ưu điểm
•Hỗ trợ đệ quy
•Các chương trình con có thể chia sẻ bộ nhớ
Nhược điểm
•Phải cấp phát và giải phóng vùng nhớ mỗi khi gọi hàm
•Không lưu trữ thông tin
•Truy xuất gián tiếp thông qua bản ghi hoạt động của chương trình con(lấy địa chỉ của con trỏ +địa chỉ tương đối được cấp cho biến trong ngăn xếp)
Biến được cấp phát động trong vùng nhớ Head(Explicit heap-dynamic) :việc cấp phát này thông qua các lệnh cấp phát tường minh
Ví dụ như các hàm cấp phát malloc, hoặc alloc trong c
Khuyết điểm
•Sự an toàn của biến phụ thuộc vào người lập trình
•Tham chiếu gián tiếp thông qua con trỏ hoặc tham chiếu
Ưu điểm :quản lý hiệu quả vùng nhớ
Biến động sử dụng bộ nhớ Head:vùng nhớ cấp phát cho biến thay đổi khi thực thi
Trang 8 ví dụ
•list=array(1,2,3)
•
•List=’vntan’
Ưu điểm
•Linh động
Nhược điểm
•Kém hiệu quả
1. Ví dụ : List=array(1,3,4) List[100]=5
//cấp phát lại vùng nhớ, sao chép dữ liệu và giải phóng vùng nhớ cũ
•Mất đi khả năng dò tìm lổi của trình biên dịch
Định kiểu mạnh
Một ngôn ngữ lập trình được gọi là định kiểu mạnh nếu các lỗi về kiểu đều được phát hiện
Một ngôn ngữ định kiểu mạnh có các thuộc tính:
•Tất cả các biến trong chương trình đều phải có duy nhất một kiểu và kiểu dữ liệu của biến sẽ được xác định tại thời điểm biên dịch
•Có thể phát hiện tất cả các lỗi sai về kiểu của biến
•Phát hiện các lỗi về kiểu đối với những biến có nhiều kiểu dữ liệu(kiểu Union)
•Ví dụ trong đoạn chương trình sau ta khai báo một record(union) định nghĩa hai kiễu dữ liệu là real và một mảng int , hai kiểu này dùng chung vùng nhớ 4 byte,trong chương trình ta khai báo một biến x có kiểu bảng ghi và chọn kiểusố thực để gán dữ liệu nhưng khi xuất thì xuất mảng nhưng chương trình không báo lỗi =>Pascal không phải là ngôn ngữ định kiểu mạnh
Trang 9•Trong C người lập trình có thể sử dụng toán tử ép kiểu =>có rủi ro=>c ,hỗ trợ union, không có kiểm tra kiểu khi truyền tham số không phải là ngôn ngữ định kiểu mạnh
• Kiểm tra kiểu :
là quá trình được sử dụng để đảm bảo các toán hạng của một toán tử phải tương thích kiểu với nhau
tương thích kiểu là :được chấp nhận bởi toán hạng hoặc được ngôn ngữ hỗ trợ chuyển kiểu về các kiểu hợp lệ
lỗi về kiểu là thực hiện một toán tử trên các toán hạn có kiểu không phù hợp
nếu kết nối kiểu là tĩnh thì kiểm tra kiểu là tĩnh và ngược lại
Ba luật kiểm tra kiểu
Luật tương đương kiểu
Luật tương thích kiểu
Luật suy luận kiểu
Luật tương đương kiểu :có hai loại tương dương
Tương đương cấu trúc :có cấu trúc giống nhau thì tương đương nhau
Tương đương tên:nếu hai biến được khai báo cùng một tên kiểu thì tương đương
Trang 10 Các ngôn ngữ thường kết hợp hai kiểu tương đương này
• Tương đương theo cấu trúc :tùy theo ngôn ngữ lập trình mà khác nhau
Ví dụ
Để kiểm tra hai kiểu có tương đương cấu trúc hay không, toàn bộ cấu trúc của hai kiểu sẽ được so sánh
Quá trình kiểm tra sẽ thay đổi các kiểu được định nghĩa về các toán tử dựng kiểu (array)và các kiểu cơ bản
Nếu sau khi phân tích nhận được hai chuổi như nhau thì tương đương kiểu với nhau
Ví dụ
#define Songuyen int
#define Mang int[50]
Hai biến
Int a;
Songuyen b;
Tương đương kiểu Int a[50];
Mang b;
Tương đương kiểu với nhau
Trang 11• Tương đương theo tên
Nếu người lập trình đã định nghĩa tên của hai tên kiểu mới, thì người lập trình muốn ngầm định đây là hai kiểu khác nhau
Ví dụ
• #define Songuyenduong int
• #define Songuyen int;
• Songuyenduong a;
• Songuyen=b;
• a=b;//báo lỗi nếu xét tương đương tên
•
Hai biến được gọi tương đương theo tên thì hệ thống sẽ so sánh theo tên
Có hai loại tương đương tên
Tương đương theo tên tuyệt đối(strict name equivalence)
• #define Songuyenduong int
• #define Songuyen int;
• Songuyenduong a;
• Songuyen=b;
• a=b;//báo lỗi nếu xét tương đương tên
Tương đương theo tên tương đối(loose name equivalence)
• #define Songuyenduong int
• #define Songuyen int;
• Songuyenduong a;
• Songuyen=b;
• a=b;//không báo lỗi nếu xét tương đương tên
Trong ngôn ngữ lập trình ada cho phép hai khả năng tương đương theo tên thông qua hai khái niệm kiểu con (subtype)
và kiểu dẫn xuất(derived type)
Kiểu con tương thích với kiểu cơ sở, các kiểu con mà cùng kiểu cơ sở thì tương thích với nhau( tương thích theo tên tương đối)
Trang 12 Kiểu dẫn xuất :kế thừa mọi đặc tích của kiểu gốc nhưng không tương đương với kiểu gốc(tương đương theo tên tuyệt đối)
Ví dụ
• subtype int is integer; //tương thích
• type celsius is new integer; //không tương thích
• type fahrenheit is new integer;
• int a;
• interger b;
• a=a+b;//tương thích
• celsius d;
• fahrenheit e;
• d=d+e;//không tương thích
• d=d+a;//không tương thích
• ví dụ
•
• So sánh tương đương theo tên và tương đương theo cấu trúc
Tương đương theo cấu trúc linh động hơn tương đương theo tên
Cho dù người viết chương trình định nghĩa hai kiểu dữ liệu khác nhau trên cùng một kiểu cơ sở với ngụ ý là hai kiểu dữ liệu khác nhau nhưng nếu ngôn ngữ hỗ trợ tương đương logic thì hai biến này là tương thích
• Ví dụ
Trang 13 Lỗi hay không phụ thuộc vào ngôn ngữ lập trình
• Hầu hết các ngôn ngữ lập trình không yêu cầu phải tương đương kiểu mà chì cần tương thích
Một biểu thức đúng nếu các toán hạng tương thích với nhau
về kiểu
Khi truyền tham số vào chương trình con, tham số thực và tham số hình thức phải tương thích với n hau
• Chuyển kiểu(Coercion)
Chuyển kiểu được thực hiện tự động do trình biên dịch thực hiện (khác ép kiểu)
Việc chuyễn kiểu tùy ý của lập trình viên ảnh hướng đến tính định kiểu mạnh của ngôn ngữ
Ví dụ trong c,C++ người sử dụng có thể chuyển từ số thực sang số nguyên ->nguy cơ làm mất đi giá trị của phần thập phân
Trang 14II. Phạm vi của biến :
• là vùng lệnh có thể sử dụng biến
• Biến không cục bộ là biến có thể sử dụng trong chương trình con mà không cần khai báo(biến toàn cục là một loại biến không cục bộ)
• Biến cục bộ là biến được khai báo và sử dụng trong một lô lệnh, trong một chương trình con
• Luật phạm vi sử dụng biến :cách thức mà chương trình dịch sẽ tìm vị trí khai báo biên
• Có hai loại phạm vi biến
Phạm vi tĩnh :phạm vi xác định lúc biên dịch, khi gặp lệnh
sử dụng biến, trình biên dịch sẽ kiểm tra “từ trong ra ngoài”
để xác định vị trí khai báo biến
Trang 15 Ví dụ :
•
Khi sử dụng biến trong chương trình con, nếu biến vừa được khai báo trong chương trình con, vừa được khai báo toàn cục, thì trình biên dịch sẽ sử dụng biến được khai báo trong chương trình con
•
Muốn truy xuất đến biến ngoài cùng tên với biến cục bộ trong C++ dùng cú pháp ::tên biên
•
Phạm vi biến trong khối lệnh :đây là phạm vi khai báo biến nhỏ nhất
•
Ví dụ :
•
• if (FIRST > SECOND) {
• int TEMP; //
• TEMP = FIRST;
• FIRST = SECOND;
• SECOND = TEMP;
Trang 16 Trình biên dịch xem một khối lệnh giống như một chương trình con(cung cấp vùng nhớ trong ngăn xếp)
Phạm vi động :dựa vào dãy lời gọi của chương trình con, dựa vào quan hệ thời
gian gọi chương trình con ví dụ chương trình con A gọi chương trình con B, chương trình con B gọi chương trình con C…,
việc xác định phạm vi biến chỉ xác định được tại thời điểm chạy chương trình dựa vào các lời gọi chương trình con
ví dụ :