Nghiên cứu về Kỹ thuật phần mềm để đưa ra quy tắc cho các hoạt động này bằng cách cung cấp mô hình, các ngôn ngữ, các phương pháp luận, các công cụ, … Nghiên cứu về Kỹ thuật phần mềm đượ
Trang 1Lời nói đầu
Ngày nay phần mềm được ứng dụng trong hầu hết các lĩnh vực của cuộc sống hiện đại Trong cuộc sống hàng ngày chúng ta đều tiếp xúc một cách trực tiếp hoặc gián tiếp đến các hệ thống phần mềm với quy mô từ nhỏ đến lớn Từ các thiết bị dân dụng trong gia đình như TV, máy giặt, lò vi sóng,… đều được ứng dụng các phần mềm nhúng để điều khiển hoạt động đến các hệ thống lớn như trong các ngân hàng, các công ty, các trường học,… Tất cả đều được ứng dụng phần mềm trong một phần hay toàn bộ công việc
Cùng với xu thế áp dụng phần mềm trong mọi hoạt động của đời sống xã hội thì việc các sinh viên sau khi ra trường được trang bị một phần kiến thức liên quan đến lĩnh vực xây dựng phần mềm cũng như quy trình sản xuất phần mềm để đảm bảo có thể xây dựng được một phần mềm có chất lượng tốt phục vụ công việc cũng như xây dựng được các phần mềm thương mại đảm bảo chất lượng tốt
Từ nhu cầu thực tiễn đó, bộ môn Điện tử Tin học, khoa Điện tử Viễn thông Trường Đại học Bách khoa Hà Nội được giao nhiệm vụ đảm nhiệm môn học Kỹ thuật phần mềm là một môn học bắt buộc của sinh viên Điện tử viễn thông Cuốn giáo trình kỹ thuật phần mềm này được viết nhằm hỗ trợ sinh viên Điện tử viễn thông sử dụng làm giáo trình học tập cũng như được sử dụng làm tài liệu tham khảo của các cá nhân hiện đang muốn tìm hiểu cũng như đang làm các công việc liên quan đến phần mềm
Cấu trúc của cuốn sách này được chia thành 5 chương:
Chương 1: Giới thiệu
Trong chương này giới thiệu một số khái niệm liên quan đến phần mềm, các thế hệ phần mềm, đề cập đến khái niệm kỹ thuật phần mềm
Chương 2: Dịch ngôn ngữ bậc cao
Chương này giới thiệu nguyên lý của chương trình dịch và các bước cơ bản chương trình dịch thực hiện việc chuyển đổi chương trình nguồn thành chương trình dưới dạng mã trung gian hoặc mã máy
Chương 3: Cơ sở dữ liệu
Cơ sở dữ liệu là một thành phần quan trọng và không thể thiếu được trong hầu hết các hệ thống phần mềm hiện đại, đặc biệt là các phần mềm thông tin quản
Trang 2lý Chương này nhằm giới thiệu các khái niệm cơ bản về CSDL cũng như các cách thiết kế một cơ sở dữ liệu một cách hiệu quả nhất khi tiến hành xây dựng phần mềm
Chương 4: Ngôn ngữ SQL
Chương này nhằm giới thiệu một cơ chế khai thác CSDL trong các hệ thống phần mềm bằng cách sử dụng một loại ngôn ngữ phi thủ tục
Chương 5: Các pha trong phát triển phần mềm
Chương này nhằm giới thiệu các bước cần phải trải qua của một quá trình xây dựng một phần mềm nhằm tạo ra các phần mềm có chất lượng
Do thời gian có hạn cũng như kinh nghiệm chưa nhiều nên chắc hẳn cuốn giáo trình này còn có nhiều thiếu sót Rất mong nhận được ý kiến đóng góp của độc giả để cuốn giáo trình được bổ sung, hoàn thiện tốt hơn Mọi ý kiến đóng góp xin gửi về:
Bộ môn Điện tử tin học – Khoa Điện tử viễn thông Đại học Bách Khoa Hà Nội
P401 – C9
Email: nvhuong5650@yahoo.com;
Xin trân trọng cảm ơn
Hà nội, ngày tháng năm
Bộ môn Điện tử Tin học
Trang 3MỤC LỤC
Lời nói đầu
Chương 1 Giới thiệu 5
1.1 Các thành tựu và các vấn đề tồn tại của kỹ thuật phần mềm 5
1.2 Các ứng dụng của phần mềm 9
1.3 Định nghĩa kỹ thuật phần mềm 12
1.4 Quá trình, phương pháp, công cụ 15
1.5 Khái niệm đạo đức trong kỹ thuật phần mềm 16
Chương 2 Dịch ngôn ngữ bậc cao 18
2.1 Các vấn đề về dịch ngôn ngữ 18
2.1.1 Cú pháp ngôn ngữ lập trình 19
2.1.2 Tiêu chuẩn cú pháp cơ bản 20
2.1.3 Các phần tử cú pháp của một ngôn ngữ 23
2.1.4 Cấu trúc chương trình tổng thể - chương trình con 26
2.2 Giới thiệu chương trình dịch 27
2.2.1 Giới thiệu chung: 27
2.2.2 Trình biên dịch(compiler) là gì 29
2.3 Các pha của một trình biên dịch 30
2.3.1 Giai đoạn phân tích từ vựng: 31
2.3.2 Phân tích cú pháp 32
2.3.3 Phân tích : 35
2.3.4 Giai đoạn tạo mã trung gian 35
2.3.5 Giai đoạn tạo mã đích 36
2.4 Quản lý bảng ký hiệu 37
2.5 Xử lý macro và thao tác trong thời gian biên dịch 38
2.6 Kiểm tra lỗi và thông báo lỗi 39
Chương 3 Cơ sở dữ liệu 40
3.1 Khái niệm cơ bản về cơ sở dữ liệu 40
3.1.1 Giới thiệu về CSDL 40
a) Khái niệm về CSDL 41
b) Đặc điểm của CSDL 43
c) Ưu điểm của CSDL 44
d) Kiến trúc một hệ CSDL 48
e) Ứng dụng CSDL 50
3.1.2 Các loại mô hình CSDL 51
a) Mô hình quan hệ (Relational Data Model) 51
b) Mô hình phân cấp (Hierarchical Data Model) 55
c) Mô hình mạng (Network Data Model) 59
d) Kết luận 62
3.1.3 Tính độc lập dữ liệu 63
a) Độc lập dữ liệu ở mức logic: 63
b) Độc lập dữ liệu ở mức vật lý: 63
3.2 Mô hình cơ sở dữ liệu quan hệ 64
3.2.1 Các khái niệm cơ bản 65
Trang 4a) Miền 65
b) Tích đề các của các miền 66
c) Lược đồ khái niệm 66
d) Quan hệ 66
e) Miền và thuộc tính 67
f) Chuẩn hoá 69
3.2.2 Khoá 71
a) Khoá chính (Primary key) 71
b) Khoá ứng cử (Candidate key) 72
c) Khoá ngoại lai (Foreign key) 73
d) Khoá phụ (Secondary key): 74
3.2.3 Kết luận 74
3.2.4 Các phép toán trên CSDL quan hệ 77
a) Phép chèn (INSERT) 77
b) Phép loại bỏ (DEL) 78
c) Phép thay đổi (CHANGE hoặc UPDATE) 79
3.3 Chuẩn hoá 80
3.3.1 Giới thiệu 80
3.3.2 Phụ thuộc hàm 81
3.3.3 Các dạng chuẩn thứ nhất, thứ hai và thứ ba 84
3.3.4 Các quan hệ với hơn một khoá ứng cử 94
3.3.5 Các bước để chuyển mô hình dữ liệu quan hệ thành các quan hệ ở dạng chuẩn hóa 4NF: 101
a) Bước 1: 101
b) Bước 2 109
c) Bước 3: 110
3.3.6 Kết luận: 111
3.4 Thiết kế một cơ sở dữ liệu 112
3.4.1 Tám bước thiết kế một Cơ sở dữ liệu 112
a) Bước 1: Phân tích toàn bộ các yêu cầu 112
b) Bước 2: Nhận diện các thực thể 112
c) Bước 3: Nhận diện các mối tương quan giữa các thực thể 112
d) Bước 4: Xác định khoá chính (Primary Key - PK) 112
e) Bước 5: Nhận diện khoá ngoại lai (Foreign Key - FK) 113
f) Bước 6: Thêm các thuộc tính không phải khoá vào bảng dữ liệu 113
g) Bước 7: Tiếp theo và là bước quan trọng, đó là thực hiện chuẩn hoá các bảng dữ liệu 113
h) Bước 8: Khai báo phạm vi của mỗi thuộc tính 114
3.4.2 Các quy tắc toàn vẹn dữ liệu trong mô hình quan hệ 114
a) Qui tắc Toàn vẹn thực thể (Entity Integrity Rule - EI): 114
b) Qui tắc Toàn vẹn quy chiếu (Referential Integrity Rule - RI): 114
3.4.3 Ví dụ chuẩn hoá bảng dữ liệu 116
Chương 4 Ngôn ngữ SQL 119
4.1 Giới thiệu ngôn ngữ SQL 119
4.2 Phân loại SQL 120
Trang 54.2.1 DDL (Data Definition Language) 120
4.2.2 DML (Data Manipulation Language) 120
4.2.3 DCL (Data Control Language) 120
4.3 Các lệnh SQL điển hình 120
4.3.1 Các phép tính tìm kiếm 120
a) Mệnh đề SELECT 121
b) Select list 121
c) Mệnh đề FROM 121
d) Mệnh đề WHERE 123
e) Mệnh đề GROUP BY 123
f) Mệnh đề HAVING 124
g) Mệnh đề ORDER BY 124
4.3.2 Lệnh thêm bản ghi vào bảng 125
4.3.3 Các phép tính cập nhật 125
4.3.4 Các phép xoá dữ liệu 126
4.3.5 Các toán tử 126
a) Arithmetic Operators 127
b) Assignment Operator 127
c) Comparison Operators 127
d) Logical Operators 128
4.3.6 Các hàm thư viện 128
a) Các hàm tổng hợp (Aggregate Functions) 128
b) Các hàm xử lý dữ liệu 129
4.3.7 Các lệnh định nghĩa cơ sở dữ liệu 129
a) Các lệnh tạo bảng 129
b) Lệnh thêm cột cho bảng 130
c) Lệnh bớt cột của bảng 131
d) Lệnh loại bỏ bảng khỏi CSDL 131
4.3.8 Các phép tính đảm bảo an toàn dữ liệu 131
a) Phân quyền thực hiện các lệnh trong CSDL: 131
b) Phân quyền truy cập và xử lý các đối tượng CSDL 132
c) Tước quyền xử lý CSDL của người dùng 132
4.4 Một số ví dụ áp dụng SQL 133
a) Các phép tính tìm kiếm 134
b) Các phép tính cập nhập 137
c) Các hàm thư viện 138
d) Các phép tính định nghĩa CSDL 139
e) Các phép tính về bảo đảm an toàn dữ liệu 140
Chương 5 Các pha trong phát triển phần mềm 141
5.1 Đặt vấn đề 141
5.1.1 Đặc điểm của phần mềm 141
5.1.2 Các vấn đề của phát triển phần mềm 142
5.1.3 Các mô hình phát triển phần mềm 144
a) Mô hình tuyến tính (Cascade model) 144
b) Mô hình bản mẫu (Prototype model) 144
Trang 6c) Mô hình phát trển ứng dụng nhanh (RAD) 144
d) Mô hình xoắn ốc (Siral model) 144
e) Các mô hình khác 144
5.1.4 Các kỹ thuật thế hệ thứ 4 144
5.2 Các pha trong phát triển phần mềm 144
5.2.1 Nghiên cứu yêu cầu (Requirements and Specifications) 144
a) Yêu cầu của hệ thống 144
b) Nghiên cứu hệ thống 148
c) 2.4 Các phương pháp nghiên cứu 150
d) Nghiên cứu tình huống 157
5.2.2 Phân tích và thiết kế (System Analysis and Design) 161
a) Phân tích hệ thống 166
5.2.3 Triển khai (Coding and UnitTest) 236
5.2.4 Thử nghiệm (Test) 236
5.2.5 Cài đặt và bảo trì (Deployment and Maintenance) 241
a) Triển khai hệ thống 242
b) Bảo trì hệ thống và hỗ trợ khách hàng 251
Chương 6 - Bảo vệ phần mềm 256 Tài liệu tham khảo
Trang 7Chương 1 Giới thiệu
1.1 Các thành tựu và các vấn đề tồn tại của kỹ thuật phần mềm
Máy tính khác với các máy móc thông thường ở điểm nó có thể thực hiện các nhiệm vụ rất khác nhau bằng cách sử dụng các phần mềm khác nhau Tức là phần mềm tạo ra sự khác biệt giữa các máy tính và cũng quyết định năng lực của máy tính
Cho đến những năm 1990, xu hướng của ngành công nghiệp máy tính là phát triển phần cứng nhằm giảm giá thành hệ thống và tăng năng lực xử lý cũng như lưu trữ dữ liệu Do nhu cầu phần mềm tăng lên nhanh chóng, thách thức hay mục tiêu của ngành công nghiệp máy tính hiện nay là sự cải thiện chất lượng và giảm giá thành của phần mềm
Có thể nói khả năng của phần cứng biểu thị cho tiềm năng của hệ thống còn phần mềm là một cơ chế giúp chúng ta khai thác tiềm năng này
Sự tiến hóa của phần mềm gắn liền với sự tiến hóa của phần cứng và có thể chia làm 4 giai đoạn:
a Những năm đầu (từ 1950 đến 1960):
- Giai đoạn này phần cứng thay đổi liên tục, số lượng máy tính rất ít và phần lớn mỗi máy đều được đặt hàng chuyên dụng cho một ứng dụng đặc biệt
- Phương thức chính là xử lý theo lô (batch), tức là “gói” các chương trình
có sử dụng kết quả của nhau lại thành một khối dể tăng tốc độ thực hiện
- Thời kỳ này lập trình máy tính được coi là nghệ thuật “theo bản năng”, chưa có phương pháp hệ thống Việc phát triển phần mềm chưa được quản lý
- Môi trường lập trình có tính chất cá nhân; thiết kế, tiến trình phần mềm không tường minh, thường không có tài liệu Sản xuất có tính đơn chiếc, theo đơn đặt hàng Người lập trình thường là người sử dụng và kiêm cả việc bảo trì và sửa lỗi
b Thời kỳ từ những năm 1960 đến giữa những năm 1970:
- Các hệ thống đa nhiệm, đa người sử dụng (ví dụ: Multics, Unix, ) xuất hiện dẫn đến khái niệm mới về tương tác người máy Kỹ thuật này mở ra thế giới mới cho các ứng dụng và đòi hỏi mức độ tinh vi hơn cho cả phần mềm và phần cứng
- Nhiều hệ thống thời gian thực với các đặc trưng thu thập, phân tích và biến đổi dữ liệu từ nhiều nguồn khác nhau và phản ứng (xử lý, tạo output) trong một khoảng thời gian nhất định xuất hiện
Trang 8- Tiến bộ lưu trữ trực tuyến làm xuất hiện thế hệ đầu tiên của hệ quản trị CSDL
- Số lượng các hệ thống dựa trên máy tính phát triển, nhu cầu phân phối
mở rộng, thư viện phần mềm phát triển, quy mô phần mềm ngày càng lớn làm nẩy sinh nhu cầu sửa chữa khi gặp lỗi, cần sửa đổi khi người dùng có yêu cầu hay phải thích nghi với những thay đổi của môi trường phần mềm (phần cứng, hệ điều hành, chương trình dịch mới) Công việc bảo trì phần mềm dần dần tiêu tốn nhiều công sức và tài nguyên đến mức báo động
c Thời kỳ từ giữa những năm 1970 đến đầu những năm 1990:
- Hệ thống phân tán (bao gồm nhiều máy tính, mỗi máy thực hiện một chức năng và liên lạc với các máy khác) xuất hiện làm tăng quy mô và độ phức tạp của phần mềm ứng dụng trên chúng
- Mạng toàn cục và cục bộ, liên lạc số giải thông cao phát triển mạnh làm tăng nhu cầu thâm nhập dữ liệu trực tuyến, nảy sinh yêu cầu lớn phát triển phần mềm quản lý dữ liệu
- Công nghệ chế tạo các bộ vi xử lý tiến bộ nhanh khiến cho máy tính cá nhân, máy trạm để bàn, và các thiết bị nhúng (dùng cho điều khiển trong robot, ô tô, thiết bị y tế, đồ điện gia dụng, ) phát triển mạnh khiến cho nhu cầu về phần mềm tăng nhanh
- Thị trường phần cứng đi vào ổn định, chi phí cho phần mềm tăng nhanh
và có khuynh hướng vượt chi phí mua phần cứng
d Thời kỳ sau 1990:
- Kỹ nghệ hướng đối tượng là cách tiếp cận mới đang nhanh chóng thay thế nhiều cách tiếp cận phát triển phần mềm truyền thống trong các lĩnh vực ứng dụng
- Sự phát triển của Internet làm cho người dùng máy tính tăng lên nhanh chóng, nhu cầu phần mềm ngày càng lớn, quy mô và độ phức tạp của những hệ thống phần mềm mới cũng tăng đáng kể
- Phần mềm trí tuệ nhân tạo ứng dụng các thuật toán phi số như hệ chuyên gia, mạng nơ ron nhân tạo được chuyển từ phòng thí nghiệm ra ứng dụng thực tế mở ra khả năng xử lý thông tin và nhận dạng kiểu con người
Khác với các sản phẩm công nghiệp khác, phần mềm có các đặc trưng riêng biệt làm cho việc sản xuất phần mềm trở nên khó khăn Đó chính là tính Phức tạp, tính Thích hợp, Khả năng có thể thay đổi và tính Vô hình Thêm nữa,
sự tham gia của con người vào các hoạt động định nghĩa, thiết kế, triển khai và thử nghiệm còn tạo nên sự yếu kém cho tính phức tạp, tính thích hợp, tính ổn định, tính vô hình Đồng thời còn làm khó khăn thêm cho việc kiểm soát, cộng tác nhóm thành viên
Trang 9Nghiên cứu về Kỹ thuật phần mềm để đưa ra quy tắc cho các hoạt động này bằng cách cung cấp mô hình, các ngôn ngữ, các phương pháp luận, các công
cụ, …
Nghiên cứu về Kỹ thuật phần mềm được bắt đầu từ năm 1968 trong một cuộc hội thảo của NATO được tổ chức tại Đức, bắt nguồn từ thất bại của một số
dự án phần mềm lớn, đã trải qua các giai đoạn như sau:
¾ 1968-1980: Hình thành khái niệm vòng đời của phần mềm với công
nghệ được đưa ra cho mỗi pha là từ dưới lên trên
9 Sự phát triển của phương pháp lập trình và phương pháp thiết
kế cho phép cấu trúc chương trình có thể dễ dàng kiểm tra và hiệu chỉnh
Các ngôn ngữ lập trình ảnh hưởng bởi các kết quả này là Pascal, C, các ngôn ngữ lập trình hướng đối tượng
9 Sự phát triển công nghệ mô tả các yêu cầu của người dùng, kỹ thuật yêu cầu:
• COCOMO của B.Boehm
phần mềm)
• Phương pháp V của R.MaCabe
• Phương pháp E của H.Halstead
•
Trang 10o Dự tính giới hạn của hoạt động test
• Mô hình gia tăng độ tin cậy phần mềm
mềm với sự hỗ trợ của việc quản trị dự án và quản trị cấu hình
¾ 1980-1990: Chuẩn hoá và tự động hoá từng phần từ trên xuống
9 Sự xuất hiện của công nghệ bản mẫu: Mô hình thác nước bị loại bỏ vì nó tỏ ra không thể dùng để định nghĩa yêu cầu người sử dụng một cách hoàn hảo Mô hình thay đổi của công nghệ phần mềm là chuyển từ mô hình thác nước sang mô hình bản mẫu
9 Sự phát triển các công cụ phát triển phần mềm có sự hỗ trợ của máy tính - CASE (Computer Aided Software Engineering)
¾ 1985-1995: Mô hình gia công phần mềm và môi trường tích hợp;
phân tích và hỗ trợ của nhân tố con người
9 Gia công phần mềm:
(Software Process Improvement)
9 Môi trường tích hợp:
9 Phân tích và hỗ trợ của nhân tố con người
¾ 1985-1999: Công nghệ hướng đối tượng; Tính toán phân tán; Văn
hoá microsoft
9 Công nghệ hướng đối tượng:
OOSE, UML (U M Language)
9 Tính toán phân tán:
Trang 11o Ngôn ngữ hướng đối tượng (tập trung và phân tán)
giao diện
9 Văn hoá microsoft:
Công nghệ thông tin phát triển nhanh chóng, công nghệ phần mềm không thể luôn đáp ứng được mọi yêu cầu của mỗi giai đoạn đổi mới Theo sau từng bước việc nghiên cứu kỹ thuật phần mềm là việc tạo ra các công nghệ mới và sự tiến hoá của bản thân nó
Kết luận: Phần mềm là một cấu phần quan trọng của một hệ thống thông tin, nó là cách gọi khác của chương trình Nó bao gồm tập lệnh để từng bước hướng dẫn máy tính làm việc nhằm chuyển đổi dữ liệu thành thông tin
1.2 Các ứng dụng của phần mềm
Phần mềm có thể được áp dụng trong bất kỳ bài toán nào mà một tập hợp các bước theo thủ tục (ví dụ: một thuật toán) đã được xác định trước Nội dung và việc xác định thông tin là các yếu tố quan trọng trong việc quyết định loại phần mềm ứng dụng Nội dung được đề cập đến ý nghĩa và các dạng thông tin đi/đến
Ví dụ, rất nhiều phần mềm nghiệp vụ sử dụng dữ liệu đầu vào có cấu trúc và tạo
ra các báo cáo được định dạng Phần mềm điều khiển máy tự động chấp nhận dữ liệu rời rạc với cấu trúc hạn chế và tạo ra các lệnh máy độc lập một cách liên tiếp
Xác định thông tin đề cập đến khả năng đoán trước thứ tự và thời gian của thông tin Một chương trình phân tích kỹ thuật nhận dữ liệu đã được định nghĩa cấu trúc trước, thực hiện thuật toán phân tích và tạo ra dữ liệu kết quả dưới dạng báo cáo hay dạng đồ hoạ Các ứng dụng kiểu này được gọi là các ứng dụng đã được xác định Mặt khác, một hệ điều hành đa người dùng nhận dữ liệu đầu vào thông qua ngắt được tạo ra bởi các điều kiện bên ngoài và tạo ra dữ liệu đầu ra thay đổi theo một hàm phục thuộc môi trường và thời gian Các ứng dụng kiểu này được gọi là các ứng dụng không định trước
Các lĩnh vực phần mềm sau chỉ ra khả năng của các ứng dụng tiềm năng:
Trang 12¾ Phần mềm hệ thống: Phần mềm hệ thống là một tập hợp các chương trình
được viết để phục vụ chương trình khác Một số phần mềm hệ thống (ví dụ: trình biên dịch, trình soạn thảo, và các ứng dụng quản lý file) xử lý phức tạp, nhưng có cấu trúc thông tin xác định Các ứng dụng hệ thống khác (như là các thành phần của hệ điều hành, trình điều khiển thiết bị, các
bộ xử lý truyền thông) xử lý mội khối lượng lớn dữ liệu trung gian Trong trường hợp khác, phần mềm hệ thống được tiêu biểu bởi khối lượng lớn giao tiếp với các thiết bị phần cứng của máy tính, sử dụng nhiều bởi các người dùng, các hoạt động đồng thời yêu cầu được lập lịch, chia sẻ tài nguyên, và quản lý phức tạp, cấu trúc dữ liệu phức tạp và giao tiếp với nhiều thiết bị ngoại vi
Tóm lại, các đặc trưng của loại phần mềm hệ thống là:
- Là một tập hợp các chương trình được viết để phục vụ cho các chương trình khác
- Xử lý các cấu trúc thông tin phức tạp nhưng xác định (trình biên dịch, trình soạn thảo, tiện ích quản lý tệp)
- Đặc trưng bởi tương tác chủ yếu với phần cứng máy tính
- Phục vụ nhiều người dùng
- Cấu trúc dữ liệu phức tạp và nhiều giao diện ngoài
¾ Phần mềm thời gian thực: là các chương trình giám sát/phân tích/điều
khiển các sự kiện trong thế giới thực ngay khi nó xảy ra Các phần tử của phần mềm thời gian thực bao gồm: thành phần thu thập dữ liệu mà thu nhận và chuẩn hoá thông tin từ môi trường ngoài, rồi phân tích, thành phần phân tích chuyển đổi thông tin mỗi khi chương trình yêu cầu, thành phần điều khiển đầu ra tác động trở lại môi trường ngoài, thành phần giám sát điều phối mọi hoạt động của các thành phần khác để có thể kiểm soát được đáp ứng thời gian thực Cần chú ý phân biệt “thời gian thực” với “tương tác” hay “chia sẻ thời gian” Một hệ thống thời gian thực phải đáp ứng trong một giới hạn chặt chẽ về thời gian Đáp ứng của một hệ thống tương tác thường chậm mà không dẫn đến kết quả nguy hại
Điển hình của phần mềm thời gian thực là các phần mềm điều khiển các thiết bị tự động Phần mềm thời gian thực bao gồm các thành tố:
- Thành phần thu thập dữ liệu để thu và định dạng thông tin từ môi trường ngoài
- Thành phần phân tích để biến đổi thông tin theo yêu cầu của ứng dụng
- Thành phần kiểm soát hoặc đưa ra đáp ứng môi trường ngoài
- Thành phần điều phối để điều hòa các thành phần khác sao cho có thể duy trì việc đáp ứng thời gian thực Hệ thống thời gian thực phải đáp ứng những ràng buộc thời gian chặt chẽ
¾ Phần mềm nghiệp vụ: Xử lý nghiệp vụ là một lĩnh vực phần mềm ứng
dụng riêng biệt và rộng nhất Các hệ thống tách rời (như hệ thống trả lương, giao dịch tài khoản, thông kê, ) khai thác thông tin từ các phần mềm hệ thống quản lý thông tin mà truy cập đến một hoặc nhiều cơ sở dữ
Trang 13liệu lớn chứa thông tin nghiệp vụ Phần mềm ứng dụng thuộc lĩnh vực này tính toán lại dữ liệu bằng cách làm cho các hoạt động nghiệp vụ hay đưa ra các quyết định quản lý dễ dàng Cùng với ứng dụng xử lý dữ liệu quy ước, các ứng dụng phần mềm nghiệp vụ cũng bao gồm các ứng dụng tương tác
và ứng dụng khách/chủ (ví dụ: giao dịch tại các điểm bán hàng) Tóm lại, phần mềm nghiệp vụ có đặc trưng:
vụ của tổ chức, doanh nghiệp
ứng dụng tương tác như xử lý giao tác cho các điểm bán hàng
¾ Phần mềm khoa học và kỹ thuật: Phần mềm khoa học kỹ thuật được đặc
trưng bởi số lượng thuật toán rất lớn Được ứng dụng trong các lĩnh vực khoa học khác nhau: từ thiên văn học đến núi lửa, từ tự động phân tích trọng âm đến tính toán quỹ đạo tàu vũ trụ không gian, và từ sinh học phân
tử đến sản xuất tự động Tuy nhiên, các ứng dụng mới trong khoa học kỹ thuật đang tách khỏi việc xử lý các thuật toán thông thường Thiết kế có sự
hỗ trợ của máy tính, các hệ thống mô phỏng, và các ứng dụng tương tác khác bắt đầu tính đến thời gian thực và các đặc điểm của phần mềm hệ thống Tóm lại, phần mềm khoa học và công nghệ có đặc trưng:
- Có số lượng lớn các thuật toán (tính toán trên ma trận số, mô phỏng )
- Thường đòi hỏi phần cứng có năng lực tính toán cao
¾ Phần mềm nhúng: Các thiết bị thông minh ngày càng trở nên thông dụng
trên thị trường tiêu dùng và công nghiệp Phần mềm nhúng nằm trong bộ nhớ và được sử dụng để điều khiển thiết bị Phần mềm nhúng thực hiện các chức năng một cách hạn chế và đóng kín (ví dụ: bàn điều khiển lò vi sóng) hoặc cung cấp các chức năng chính và khả năng điều khiển (ví dụ: các chức năng số trong ô tô như là bộ điều khiển nhiên liệu, hệ thống phanh, đồng hồ đo, ) Đặc trưng của phần mềm nhúng:
- Nằm trong bộ nhớ chỉ đọc và được dùng để điều khiển các sản phẩm và
hệ thống cho người dùng và thị trường công nghiệp
- Có các đặc trưng của phần mềm thời gian thực và phần mềm hệ thống
¾ Phần mềm cho máy tính cá nhân: Thị trường phần mềm cho máy tính cá
nhân phát triển mạnh trong khoảng hơn 1 thập kỷ trở lại đây Các phần mềm xử lý văn bản, xử lý bảng tính, phần mềm xử lý đồ hoạ, giải trí đa phương tiện, quản trị CSDL, ứng dụng tài chính phục vụ công việc và cá nhân, và các ứng dụng mạng hay truy cập CSDL chỉ là những ví dụ nhỏ của ứng dụng máy tính cá nhân
- Bùng nổ từ khi xuất hiện máy tính cá nhân, giải quyết các bài toán nghiệp
vụ nhỏ như xử lý văn bản, trang tính, đồ họa, quản trị CSDL nhỏ
- Yếu tố giao diện người-máy rất được chú trọng
Trang 14¾ Phần mềm trí tuệ nhân tạo: Phần mềm trí tuệ nhân tạo sử dụng các thuật
toán phi thương mại để xử lý các vấn đề phức tạp mà không tuân theo các thuật toán rõ ràng Lĩnh vực phần mềm trí tuệ nhân tạo còn gọi là hệ chuyên gia, hay còn gọi là hệ thống dựa trên tri thức Tuy nhiên lĩnh vực ứng dụng khác của phần mềm trí tuệ nhân tạo là các hệ thống nhận dạng (tiếng nói và hình ảnh), chứng minh định lý, và các trò chơi Trong những năm gần đây, một lĩnh vực mới của phần mềm trí tuệ nhân tạo đã mở ra đó
là mạng nơron nhân tạo Một mạng nơron mô phỏng cấu trúc xử lý của bộ não con người và có thể sẽ được áp dụng để xây dựng một loạt phần mềm mới có thể nhận dạng các cấu trúc phức tạp hay có thể học được từ những
1.3 Định nghĩa kỹ thuật phần mềm
Mặc dù có rất nhiều tác giả đã có các định nghĩa mang tính cá nhân về “kỹ nghệ phần mềm”, nhưng định nghĩa của Fritz Bauer trong một cuộc hội thảo về phần mềm vẫn đầy đủ hơn cả:
Kỹ nghệ phần mềm là quá trình sử dụng các nguyên tắc kỹ thuật có cơ sở
để xây dựng các phần mềm một cách kinh tế để hoạt động tin cậy và chạy hiệu quả trên hệ thống máy thật
Hầu như mọi độc giả đều cố gắng thêm thắt cho định nghĩa này Định nghĩa này mới chỉ đề cập rất ít đến khía cạnh kỹ thuật của chất lượng phần mềm;
nó chưa chỉ ra được đảm bảo thoả mãn yêu cầu khách hàng hay triển khai sản phẩm đúng tiến độ; nó bỏ qua tầm quan trọng của các phép kiểm thử Tuy nhiên định nghĩa của Bauer cung cấp cho chúng ta ý chính của vấn đề “Nguyên tắc kỹ thuật có cơ sở” là gì, áp dụng vào quá trình phát triển phần mềm như thế nào? Chúng ta xây dựng phần mềm một cách kinh tế để hoạt động ổn định như thế nào? Những gì cần phải làm để tạo ra được các chương trình hoạt động hiệu quả
Trang 15không chỉ trên một mà trên nhiều máy Đó là các câu hỏi mở mà vẫn tiếp tục là vấn đề cần nghiên cứu của các kỹ sư phần mềm
Tổ chức IEEE đã phát biểu một định nghĩa đầy đủ hơn:
Kỹ nghệ phần mềm: (1) Việc áp dụng cách tiếp cận có hệ thống, quy tắc,
có thể xác định số lượng để phát triển, vận hành và bảo trì phần mềm; nghĩa là áp dụng kỹ thuật vào phần mềm (2) Nghiên cứu các cách tiếp cận như trong (1)
Các vấn đề chủ yếu liên quan đến Kỹ nghệ phần mềm:
1 Kỹ nghệ phần mềm đề cập đến việc xây dựng các chương trình lớn
Giới hạn giữa chương trình lớn và nhỏ là không rõ ràng: một chương trình khoảng 100 dòng lệnh là nhỏ, một chương trình khoảng 50 000 dòng lệnh thì chắc chắn không thể là nhỏ Việc viết chương trình nhỏ thường chỉ cần
1 người trong một khoảng thời gian ngắn Việc xây dựng chương trình lớn thường đề cập đến nhiều người xây dựng trong một khoảng thời gian dài, thường là trên 6 tháng
Kỹ thuật lập trình cổ điển dựa vào các công cụ mà mục đích chủ yếu là để xây dựng các chương trình nhỏ, không chỉ bao gồm các ngôn ngữ lập trình
mà cả các công cụ như là biểu đồ, và phương thức lập trình như là lập trình
có cấu trúc Chúng không thể dùng để xây dựng các chương trình lớn
2 Trung tâm của vấn đề là kiểm soát sự phức tạp
Nhìn chung, các vấn đề thường không thể được xem xét trong sự toàn vẹn của chúng Một vấn đề được chia nhỏ thành từng phần mà mỗi phần có thể hiểu được và dễ dàng liên kết các phần với nhau Độ phức tạp của toàn bộ vấn đề không giảm nhưng là có thể kiểm soat được.Trong một hệ thống âm thanh stereo có các thành phần như là bộ khuếch đại, bộ thu tín hiệu, bộ điều chỉnh,… giao tiếp với nhau bằng các dây dẫn nhỏ Trong phần mềm, chúng ta cũng cố gắng chia nhỏ vấn đề như vậy Trong một chương trình
tự động hóa thư viện, các thành phần liên quan đến nhau như là xử lý việc tìm kiếm, lưu trữ dữ liệu có thể được tách rời với việc tìm ra các cách để trao đổi thông tin giữa các thành phần này Cần chú ý là độ phức tạp của các phần trong một hệ thống phần mềm không phải do bản chất độ phức tạp của hệ thống quyết định mà là do độ chi tiết phải xử lý
3 Tiến hóa phần mềm
Hầu hết mô hình phần mềm đều là một phần của thế giới thực, Chẳng hạn như yêu cầu xử lý trong thư viện, hay việc lưu trữ thông tin các bước trong giao dịch chuyển tiền ở ngân hàng Thực tế luôn thay đối Nếu muốn phần mềm không quá lạc hậu, thì nó luôn phải tiến hóa theo thực tế mà đã được
mô hình hóa Điều này có nghĩa là các chi phí sẽ phát sinh sau khi triển khai phần mềm và chúng ta phải luôn ghi nhớ việc tiến hóa phần mềm trong suốt quá trình phát triển
Trang 164 Hiệu quả của phát triển phần mềm là tối quan trọng
Tổng chi phí và thời gian phát triển của một dự án là lớn Chi phí và thời gian cho việc bảo trì phần mềm cũng vậy Việc tìm ra một phần mềm mới tốn rất nhiều công sức Khoảng cách giữa yêu cầu và khả năng đáp úng của phần mềm ngày càng lớn Vấn đề quan trọng của kỹ thuật phần mềm là tìm
ra được phương pháp và công cụ hiệu quả cho quá trình xây dựng phát triển cũng như bảo trì phần mềm
5 Sự phối hợp thường xuyên giữa các cá nhân trong là cần thiết trong các
dự án phần mềm lớn
Một khi vấn đề cần giải quyết rất lớn, sẽ có rất nhiều người làm việc cùng nhau để giải quyết bài toán Rõ ràng phải có một sự sắp xếp hợp lý trong quá trình phân công công việc, phương pháp trao đổi, trách nhiệm của mối
cá nhân,…Để bắt mọi người tuân theo cần phải dùng đến các quy tắc cũng như các thủ tục Các quy tắc và thủ tục có thể được giảm đi bằng cách sử dụng các công cụ
6 Bản thân phần mềm phải hỗ trợ người dùng hiệu quả
Phần mềm được phát triển nhằm hỗ trợ người sử dụng trong công việc Các tính năng của chương trình cần phải phù hợp với yêu cầu công việc của người dùng Người dùng không thỏa mãn với phần mềm sẽ tìm cách phá hỏng nó, hay ít nhất cũng sẽ lên tiếng đưa ra yêu cầu mới ngay lập tức Nếu chỉ phát triển hệ thống theo đúng cách thì chưa đủ, mà chúng ta còn phải xây dựng đúng hệ thống cần thiết.Hỗ trợ người dùng hiệu quả nghĩa là chúng ta phải nghiên cứu thật kỹ công việc của người dùng để xác định chính xác yêu cầu chức năng của chương trình và phải chỉ ra tính khả dụng cũng như khía cạnh chất lượng của sản phẩm như là độ tin cậy, khả năng đáp ứng, và tính thân thiện Cũng có nghĩa là phát triển phần mềm đòi hỏi nhiều hơn quá trình triển khai phần mềm Có thể phải viết các tài liệu hướng dẫn và đào tạo người dùng, và phải chỉ ra các lưu ý về môi trường
mà hệ thống sẽ được triển khai để phát triển phần mềm
7 Kỹ nghệ phần mềm là lĩnh vực mà ở đó các thành viên tạo ra sản phẩm vì lợi ích của lĩnh vực khác
Các kỹ sư phần mềm là chuyên gia trong một vài lĩnh vực như là lập trình Java, thiết kế kiến trúc phần mềm, kiểm thử, hoặc sử dụng ngôn ngữ mô hình hóa Nhưng họ lại không thành thạo trong các lĩnh vực khác như là quản lý thư viện, điện tử hàng không, hay ngân hàng Nhưng họ lại phải phát triển hệ thống cho các lĩnh vực này
Các kỹ sư phần mềm không chỉ thiếu kiến thức nghiệp vụ của lĩnh vực họ phải xây dựng phần mềm mà có thể còn thiếu kiến thức về mặt văn hóa ở
đó
Trang 171.4 Quá trình, phương pháp, công cụ
Kỹ nghệ phần mềm là một công nghệ có phân lớp Bất kỳ cách tiếp cận mang tĩnh kỹ thuật nào (bao gồm cả kỹ thuật phần mềm) phải dựa trên một sự đảm bảo có tổ chức về chất lượng
a Các phương pháp
- Chỉ ra cách làm về mặt kỹ thuật để xây dựng phần mềm, được sử dụng trong các bước: lập kế hoạch, ước lượng dự án, phân tích yêu cầu hệ thống và phần mềm, thiết kế cấu trúc dữ liệu, kiến trúc chương trình và thủ tục thuật toán, mã hóa kiểm thử và bảo trì
- Các phương pháp cho kỹ nghệ phần mềm thường đưa ra các ký pháp đồ họa hay hướng ngôn ngữ đặc biệt, cách thức thực hiện và một tập các tiêu chuẩn về chất lượng của sản phẩm phần mềm
b Các công cụ
- Cung cấp sự hỗ trợ tự động hay bán tự động để phát triển phần mềm theo từng phương pháp khác nhau Khi các công cụ được tích hợp đến mức các thông tin do chúng tạo ra có thể đ-ợc dùng cho các công cụ khác thì
hệ thống hỗ trợ phát triển phần mềm đã được thiết lập và còn được gọi là
kỹ nghệ phần mềm có máy tính hỗ trợ (CASE - Computer Aided Software Engineering)
c Các thủ tục
Quản lý chất lượng
Xử lý Phương pháp Công cụ
Trang 18-Các thủ tục là chất keo dán các phương pháp và công cụ lại với nhau làm cho chúng được sử dụng hợp lý và đúng hạn trong quá trình phát triển phần mềm Thủ tục bao gồm:
- Xác định ra trình tự các phương pháp sẽ được áp dụng cho mỗi dự án
- Tạo sản phẩm cần bàn giao (tài liệu báo cáo, bản mẫu, ) cần cho việc kiểm soát để đảm bảo chất lượng và điều hòa thay đổi
- Xác định những cột mốc mà tại đó có các sản phẩm nhất định được bàn giao để cho người quản lý phần mềm nắm đ-ợc tiến độ và kiểm soát được kết quả
Cở sở trong kỹ thuật phần mềm là lớp xử lý, đây là lớp có vai trò liên kết các lớp khác tạo khả năng phát triển phần mềm một cách hợp lý và chính xác Lớp xử lý tạo ra một khung làm việc gồm một tập hợp các vùng xử lý trọng tâm (key process areas-KPAs) bắt buộc phải có để triển khai một cách hiệu quả công nghệ kỹ thuật phần mềm Các vùng xử lý trọng tâm tạo thành cơ sở cho việc kiểm soát quản lý các dự án phần mềm và tạo ra ngữ cảnh phương pháp kỹ thuật được áp dụng, các sản phẩm (mô hình, tài liệu, dữ liệu, báo cáo, form,…) được tạo ra, các điểm mốc được ghi lại, chất lượng được đảm báo, và mọi thay đổi đều được quản lý tốt
Các phương pháp trong kỹ thuật phần mềm cung cấp khái niệm “làm thế nào” (“how to’s”) để xây dựng phần mềm Các phương pháp gồm rất nhiều mảng nhiệm vụ trong đó bao gồm cả việc phân tích yêu cầu, thiết kế, xây dựng chương trình, kiểm thử (testing) và bản trì
Công cụ trong kỹ thuật phần mềm cung cấp phương tiện tự động hoặc bán
tự động hỗ trợ xử lý và các phương pháp Khi các công cụ được tích hợp thì thông tin được tạo ra bằng công cụ này sẽ được sử dụng cho với các công cụ khác, một hệ thống hỗ trợ phát triển phần mềm được tạo ra gọi là kỹ thuật phần mềm có sự hỗ trợ của máy tính (CASE – Computer Aided Software Engineering) CASE bao gồm phần mềm, phần cứng, và cơ sở dữ liệu (một kho
dữ liệu chứa các thông tin về việc phân tích, thiết kế, xây dựng chương trình và kiểm thử) để tạo ra một môi trường kỹ thuật phần mềm tương tự như CAD/CAE trong thiết kế phần cứng
1.5 Khái niệm đạo đức trong kỹ thuật phần mềm
Việc phát triển các hệ thống phần mềm phức tạp liên quan đến nhiều người: người phát triển, người kiểm thử, quản lý kỹ thuật, quản lý chung, khách hàng, Trong tổ chức tạm thời này quan hệ giữa các cá nhân thường không tương
Trang 19đồng: có người thì có kiến thức rộng hơn người khác Ví dụ: một người phát triển
hệ thống có nhiều kiến thức về hệ thống khi xây dựng hơn người quản lý anh ta Mối quan hệ không tương đồng này đòi hỏi sự tin tưởng: nếu người phát triển nói rằng việc phát triển một số thành phần nào đó đúng kế hoạch, người quản lý của anh ta không thể nhưng vẫn phải tin ít nhất là ngay lúc đó Sự tin tưởng này tạo ra
cơ hội cho một hành động không đúng nguyên tắc như là việc tham ô
Những người trong cộng đồng phát triển kỹ thuật phần mềm đã thảo luận một quy tắc đạo đức trong kỹ thuật phần mềm bao gồm 8 nguyên tắc sau (Bảng 1.1)
Cộng đồng Mọi kỹ sư phần mềm đều hành động trước
sau như một vì lợi ích chung Khách hàng và ông
hàng và ông chủ nghĩa là tuân theo lợi ích chung
phẩm của mình và các hiệu chỉnh liên quan thoả mãn tiêu chuẩn nghề nghiệp cao nhất có thể
trực và độc lập trong việc đánh giá nghề nghiệp của mình
người trưởng nhóm phải góp phần thúc đẩy cách tiếp cận về mặt đạo đức trong việc quản
lý quá trình phát triển và bảo trì phần mềm
và danh tiếng của nghề nghiệp phù hợp với lợi ích chung lên trên hết
trợ đồng nghiệp
tập rèn luyện trong nghề nghiệp và thúc đẩy hướng tiếp cận mang tính đạo đức trong hoạt động nghề nghiệp
Trang 20Chương 2 Dịch ngôn ngữ bậc cao
Thông thường các phần mềm được phát triển bằng cách sử dụng các công
cụ lập trình cùng với một hoặc một vài ngôn ngữ lập trình phù hợp với bài toán cũng như khả năng của người lập trình Tuy nhiên các hệ thống máy thật chỉ hiểu
và thực hiện các lệnh ở mức thấp gọi là mã máy Do đó để chương trình có thể chạy được trên các hệ thống máy thật thì phải trải qua một quá trình chuyển đổi
từ các lệnh được viết bằng ngôn ngữ lập trình thành mã mà máy có thể hiểu được
và thi hành Quá trình đó gọi là quá trình dịch
Có thể định nghĩa quá trình dịch là một quá trình chuyển đổi ngôn ngữ giữa các mức khác nhau
Quá trình chuyển đổi từ ngôn ngữ bậc cao xuống ngôn ngữ bậc thấp gọi là quá trình dịch xuôi Quá trình này nhằm tạo ra chương trình ở dạng mã máy mà
có thể triển khai được trên hệ thống máy thật gọi là chương trình có thể thực thi
Quá trình chuyển đổi từ ngôn ngữ bậc thấp trở lại ngôn ngữ bậc cao gọi là quá trình dịch ngược Quá trình này được sử dụng khi chỉ có chương trình có thể thực thi mà không có chương trình nguồn nhưng ta lại muốn tìm hiểu hoạt động cũng như cách xử lý bên trong một phần nào đó của chương trình
Quá trình chuyển đổi chương trình được viết bằng ngôn ngữ bậc cao này thành chương trình tương đương dưới ngôn ngữ bậc cao khác gọi là quá trình chuyển đổi ngôn ngữ Quá trình này được sử dụng khi ta có chương trình nguồn được viết bởi một ngôn ngữ nào đó nhưng lại muốn dịch trên một hệ thống mà không được hỗ trợ ngôn ngữ đó Tuy nhiên với cách này chỉ hiệu quả đối với các chương trình nhỏ, còn các chương trình lớn và phức tạp thì thường phải chỉnh sửa nhiều, thậm trí mất nhiều công sức hơn là viết mới từ đầu
Mỗi cách dịch đều cần có các nguyên lý và kỹ thuật riêng Trong phạm vi giáo trình này chỉ đề cập đến quá trình dịch xuôi, là một quá trình không thể thiêu trong khi xây dựng và phát triển phần mềm
2.1 Các vấn đề về dịch ngôn ngữ
• Trong những ngày đầu khi thiết kế ngôn ngữ lập trình (chẳng hạn trong
suốt thời kỳ hình thành và phát triển các ngôn ngữ FORTRAN, ALGOL, COBOL, và LISP trong khoảng thập kỷ 60 của thế kỷ 20), người ta tin rằng, một cú pháp hình thức là tất cả yêu cầu để xác định các chương trình
Trang 21• Khái niệm ngữ pháp phi ngữ cảnh (context-free grammar) hay ngữ pháp
dạng Backus-Naur (BNF-Backus-Naur form) đã phát triển và được sử dụng
thành công để xác định cú pháp của ngôn ngữ
• Hiện nay, đó vẫn là kỹ thuật cơ bản để mô tả các thành phần của chương
trình
2.1.1 Cú pháp ngôn ngữ lập trình
• Cú pháp, được định nghĩa là “sự sắp xếp các từ (các phần tử trong một
câu) để thể hiện mối quan hệ giữa chúng”
• Cú pháp mô tả thứ tự các ký hiệu để tạo thành một chương trình hợp lệ
• Câu lệnh C: X=Y + Z là đúng thứ tự, trong khi đó XY+- không phải là thứ
tự đúng của chương trình được viết bằng ngôn ngữ C
Cú pháp cung cấp thông tin quan trọng cần thiết để hiểu chương trình và thông tin rất cần để có thể dịch chương trình nguồn thành chương trình đối tượng
Ví dụ, hầu hết mọi người sẽ hiểu biểu thức 2+3x4 có giá trị 14 chứ không phải là
20 Nghĩa là, biểu thức sẽ được hiểu theo cách viết 2+(3x4) chứ không được hiểu theo cách (2+3)x4 Nếu muốn, chúng ta có thể xác định bất kỳ cách biểu diễn nào
để hướng dẫn bộ biên dịch thực hiện định giá biểu thức này
Tuy nhiên, Việc chỉ phát triển cú pháp của ngôn ngữ là không đủ để xác định một cách rõ ràng cấu trúc của câu lệnh Ví dụ trong câu lệnh X=2.45+3.67,
cú pháp không thể chỉ ra rằng đây là một câu lệnh khai báo biến X, hay là khai báo biến X có kiểu số thực Kết quả X =5; X=6 hay X=6.12 đều có thể xảy ra nếu tương ứng X và phép + đều biểu hiện số nguyên; X biểu hiện số nguyên, phép +
là cộng số thực hay cả X và phép + đều biểu hiện số thực
Nghĩa là không thể chỉ sử dụng các cấu trúc cú pháp mà có thể định nghĩa một cách đầy đủ một ngôn ngữ lập trình Các thuộc tính khác có ý nghĩa về mặt ngữ nghĩa như là khai báo biến, phép toán, điều khiển luồng, và các biến môi trường tham chiếu, đối với một biến không bao giờ có thể định nghĩa được bằng các quy tắc cú pháp
Mặc dù các mô tả về cú pháp của một ngôn ngữ lập trình không đủ để có thể hiểu được toàn bộ ngôn ngữ lập trình, nhưng chúng là các thuộc tính quan trọng của ngôn ngữ lập trình Việc áp dụng ý nghĩa về mặt ngữ nghĩa để sinh ra một chương trình đối tượng hiệu quả từ một chương trình nguồn cho trước vẫn cần nhiều kỹ thuật cũng như việc ứng dụng một số phương pháp chính tắc trong quá trình xử lý
Trang 222.1.2 Tiêu chuẩn cú pháp cơ bản
Mục đích chính của cú pháp là cung cấp một quy ước để trao đổi giữa người lập trình và bộ xử lý ngôn ngữ lập trình (Programming Language Processor) Tuy nhiên sự lựa chọn cấu trúc cú pháp cụ thể bị ảnh hưởng bởi sự cần thiết để truyền đạt các phần tử cụ thể của thông tin Chẳng hạn, trong thực tế việc một biến có kiểu dữ liệu là kiểu số thực có thể được biểu diễn bằng rất nhiều cách khác nhau trong chương trình như là cách khai báo biến tường minh trong Pascal, hay thông qua quy ước đặt tên trong FORTRAN, Chi tiết cú pháp được lựa chọn một cách rộng rãi dựa trên tiêu chí thứ cấp mà không liên quan đến mục đích chính của việc truyền thông tin tới bộ xử lý ngôn ngữ, ví dụ: tính dễ đọc
Có rất nhiều tiêu chí thứ cấp, nhưng chúng được phân loại một cách rõ ràng dựa trên các mục đích cơ bản của quá trình viết chương trình là: dễ đọc (Readability), dễ viết (Writeability), dễ dịch (Easy to translation), và rõ ràng (Unambiguous hoặc Lack of ambiguity) Chúng ta sẽ xem xét cụ thể từng cách
mà cấu trúc ngôn ngữ lập trình được thiết kế để đảm bảo những mục đích này
1 Tính dễ đọc (Readability): Một chương trình được gọi là dễ đọc nếu cấu trúc
dữ liệu và giải thuật là rõ ràng ngay khi xem xét mã chương trình Một
chương trình dễ đọc, còn được gọi là self-documenting, có thể hiểu được mà
không cần phải tham khảo thêm bất kỳ tài liệu đi kèm chương trình nào khác (mặc dù trong thực tế rất khó đạt được điều này) Tính dễ đọc được tăng cường bằng các đặc điểm của ngôn ngữ như là khuôn dạng câu lệnh tự nhiên, câu lệnh có cấu trúc, sử dụng từ khóa, khả năng nhúng lời giải thích trong chương trình, không giới hạn độ dài từ định danh, các ký hiệu phép toán số học, và đầy đủ cấu trúc dữ liệu để khai báo Tất nhiên tính dễ đọc không chỉ được đảm bảo bằng thiết kế của ngôn ngữ mà còn phụ thuộc rất nhiều vào việc lập trình
Nhìn chung, những ngôn ngữ chỉ cung cấp hạn chế cấu trúc cú pháp khác nhau sẽ khó có thể tạo ra được chương trình dễ đọc Ví dụ, trong APL hay SNOBOL4 chỉ có duy nhất một cấu trúc lệnh Việc phân biệt các phep gán,
lời gọi chương trình con, cách sử dụng câu lệnh goto, kết quả trả về của
chương trình con, các điều kiện rẽ nhánh,… được phân biệt chỉ thông qua một vài ký hiệu toán tử trong một biểu thức phức tạp
2 Tính dễ viết (Writeability): Các đặc điểm cú pháp để chương trình dễ viết
thường đối lập với đặc điểm cú pháp để giúp chương trình dễ đọc Tính dễ viết được tăng cường bằng việc sử dụng các cấu trúc cú pháp đúng quy tắc và ngắn gọn, trong khi các câu có cấu trúc dài thường dễ đọc, dễ hiểu hơn Thật không may là ngôn ngữ C lại có khả năng cung cấp các chương trình rất chính xác nhưng lại khó đọc, mặc dù nó rất hữu dụng
Trang 23Quy ước cấu trúc rõ ràng cho phép việc khai báo biểu thức khuyết vế trái làm cho chương trình ngắn hơn và dễ viết hơn nhưng lại khó đọc hơn Các đặc điểm khác đáp ứng được cả 2 mục đích: như là sử dụng lệnh có cấu trúc, ký hiệu phép toán số học, khuôn dạng câu lệnh tự nhiên, và không giới hạn tên định danh thường giúp cho việc viết chương trình dễ hơn bằng cách cho phép
dữ liệu và giải thuật của bài toán được thể hiện trong chương trình theo cấu trúc tự nhiên
Một cú pháp là dư thừa nếu nó trao đổi cùng một phần tử thông tin theo hơn một cách Trong một số trường hợp, việc dư thừa là hữu ích trong cú pháp ngôn ngữ lập trình vì nó làm cho chương trình dễ đọc hơn và đồng thời
dễ dàng kiểm soát lỗi trong quá trình dịch Nhược điểm của sự dư thừa là làm cho chương trình rườm rà và khó viết Hầu hết các quy tắc mặc định về ý nghĩa của các cấu trúc ngôn ngữ có xu hướng làm giảm sự dư thừa bằng cách hạn chế trình bày tường minh về ý nghĩa trong trường hợp ý nghĩa này có thể luận ra từ ngữ cảnh
3.Tính dễ kiểm tra (Easy of Verifiability): Liên quan tới tính dễ đọc và tính dễ
viết là khái niệm chương trình được viết đúng hay việc kiểm tra chương trình Sau nhiều năm kinh nghiệm người ta rút ra được kết luận là nếu mỗi câu lệnh của ngôn ngữ lập trình tương đối dễ hiểu thì toàn bộ quá trình tạo ra các chương trình là cực kỳ khó khăn Vì vậy chúng ta cần những kỹ thuật để có thể tạo ra được những chương trình được chứng minh là đúng về mặt toán học
3 Tính dễ dịch: Một mục tiêu thứ 4 là làm thế nào để tạo ra được chương trình
dễ dàng dịch thành dạng có thể chạy được Tính dễ đọc và tính dễ viết liên quan trực tiếp tới nhu cầu của người lập trình viên Tính dễ dịch liên quan đến chương trình dịch mà xử lý các chương trình nguồn Vấn đề mấu chốt của tính dễ dịch chính là tính quy tắc của cấu trúc Ví dụ, cú pháp của LISP cho ta một ví dụ về cấu trúc chương trình vừa dễ đọc, vừa dễ viết nhưng cực
kỳ khó dịch Việc dịch chương trình viết bằng COBOL cực kỳ khó khăn bởi
nó cho phép số lượng lớn các dạng câu lệnh và khai báo mặc dù về mặt ngữ nghĩa của ngôn ngữ này không hề phức tạp
4 Tính rõ ràng (Unambiguous) hay có thể nói là “Hạn chế sự tối nghĩa” (Lack
of ambiguity): Sự tối nghĩa là vấn đề chính của mọi ngôn ngữ Về mặt lý
tưởng, một định nghĩa ngôn ngữ cung cấp một ý nghĩa duy nhất cho từng cấu trúc cú pháp để lập trình viên có thể viết chương trình Cấu trúc tối nghĩa sẽ dấn đến hai hoặc nhiều hơn cách dịch khác nhau Vấn đề tối nghĩa thường không phát sinh trong cấu trúc của từng thành phần trong chương trình mà xảy ra trong quá trình liên kết giữa các thành phần khác nhau Chẳng hạn,
Trang 24trong cả Pascal và ALGOL đều cho phép 2 dạng khác nhau của câu lệnh có điều kiện:
if biểu thức logic then câu lệnh 1 else câu lệnh 2
if biểu thức logic then câu lệnh 1
Việc diễn giải cả 2 dạng câu lệnh này là rõ ràng Tuy nhiên khi kết hợp cả 2 câu lệnh bằng cách cho câu lệnh 1 cùng câu lệnh 2 trong một câu điều kiện
thì cấu trúc sẽ khó xác định điều kiện else:
if biểu thức logic 1 then if biểu thức logic2 then câu lệnh 1 else câu lệnh 2
Câu lệnh này chính là một trường hợp tối nghĩa bởi vì rất khó xác định cách
thực hiện thứ tự câu lệnh (Hình 2.1)
Hình 2.1 - Hai cách hiểu của 1 câu lệnh có điều kiện Tương tự đối với trường hợp viết A(i,j) trong chương trình viết bằng ngôn ngữ FORTRAN có thể được hiểu là phần tử (i,j) của mảng hai chiều A nhưng cũng có thể được hiểu là một lời gọi hàm A với đối số i và j Thông thường thì việc xảy ra sự tối nghĩa này có ở hầu hết các ngôn ngữ lập trình
Trong thực tế vần đề tối nghĩa đều có thể được xử lý, chẳng hạn đối với câu
lệnh có điều kiện trên có thể sử dụng thêm cặp từ khoá begin end để phân
đề tối nghĩa: đối với câu lệnh có điều kiện lồng nhau như trên thì sẽ xử lý câu
lệnh else cuối cùng thuộc về câu lệnh có từ khoá then gần nhất Đối với dữ
liệu kiểu mảng, để có truy cập đến các phần tử của mảng sẽ dùng dấu ngoặc vuông thay vì dấu ngoặc tròn để tránh nhầm lẫn
Biểu thức logic 1
Biểu thức logic 2
Câu lệnh 2
Câu lệnh 1
Biểu thức logic 1
Biểu thức logic 1
Câu lệnh 2 Câu lệnh 1
false
false
false
false true
true
true
true
Trang 252.1.3 Các phần tử cú pháp của một ngôn ngữ
Phong cách cú pháp chung của một ngôn ngữ lập trình được tạo thành bởi một tập hợp các phần tử cú pháp cơ bản Chúng ta sẽ xem một cách ngắn gọn các phần tử chính:
Bộ ký tự (character set): Việc lựa chọn bộ ký tự là một trong những việc
phải làm đầu tiên khi thiết kế một ngôn ngữ Có rất nhiều bộ ký tự được sử dụng rộng rãi, chẳng hạn ASCII ngoài các chữ cái và chữ số, mỗi bộ ký tự đều chứa một tập hợp khác nhau các ký tự đặc biệt Việc lựa chọn bộ ký tự rất quan trọng trong việc xác định kiểu thiết bị để nhập dữ liệu và trả kết quả ra Ví dụ, bộ ký tự trong ngôn ngữ C đáp ứng được mọi thiết bị vào ra Trong khi đó bộ ký tự trong APL không thể sử dụng với hầu hết thiết bị vào ra
Việc thống nhất sử dụng bộ ký tự 8 bit để biểu diễn ký tự có lẽ là sự lựa chọn hợp lý khi ngành công nghiệp máy tính chuyển từ thế hệ 6 bits sang thế hệ 8 bits vào những năm 60 của thế kỷ 20 256 ký tự dường như là đủ để biểu dễn 52 chữ cái bao gồm cả chữ hoa và chữ thường, 10 chữ số, và một số ký hiệu dấu câu Tuy nhiên, ngày nay công nghiệp máy tính phát triển quy mô toàn cầu Các nước cùng sử dụng 26 chữ cái không nhiều Tiếng Tây Ban Nha sử dụng thêm dấu ngã (~), Tiếng Pháp sử dụng dấu trọng âm (’), và còn nhiều ký tự đặc biệt khác được
sử dụng trong các ngôn ngữ khác (như là ß,å,Ö, ) Hơn nữa các ngôn ngữ Hy Lạp, Ảrập, tiếng Do thái, sử dụng bộ ký tự hoàn toàn khác Các ngôn ngữ như tiếng Nhật, tiếng Trung Quốc còn sử dụng các ký tự đồ hoạ để diễn tả một từ hoặc một cụm từ yêu cầu một bộ ký tự gồm hơn 10 000 ký hiệu khác nhau Rõ ràng bộ ký tự 1 byte không thể đáp ứng được trong các trường hợp trên, vì thế các bộ xử lý ngôn ngữ sẽ phải sử dụng đến bộ ký tự 16 bits (sẽ bao gồm 65 536
ký hiệu) để biểu diễn các ngôn ngữ trên
Định danh (Identifiers): Cú pháp cơ bản cho định danh là một chuỗi gồm
các ký tự và chữ cái bắt đầu bởi chữ số, được chấp nhận một cách rộng rãi Sự đa dạng giữa các ngôn ngữ chủ yếu là ở các ký tự đặc biệt được sử dụng nhằm nâng cao tính dễ đọc trong một giới hạn nhất định về độ dài Giới hạn về độ dài (chẳng hạn ban đầu FORTRAN giới hạn tên định danh dưới 6 ký tự) sẽ hạn chế khả năng nhớ được hết ý nghĩa trong nhiều trường hợp và do đó sẽ giảm tính dễ đọc của chương trình
Trang 26Ký hiệu toán tử (Operator symbols): Phần lớn các ngôn ngữ sử dụng các
ký tự đặc biệt như là + và - để thể hiện 2 phép toán cơ bản Các phép toán sơ cấp
có thể được biểu diễn hoàn toàn bằng các ký tự đặc biệt Các ngôn ngữ sử dụng
tổ hợp các ký tự để thể hiện một số toán tử khác (ví dụ trong FORTRAN, EQ và
** được dùng tương ứng để thể hiện toán tử so sánh và toán tử luỹ thừa)
Từ khoá và từ dành riêng (Key words and Reserved words): Từ khoá là
một định danh được dùng như là một phần cố định trong cú pháp của câu lệnh Ví
dụ “IF” bắt đầu một câu lệnh có điều kiện Từ khoá (Key words) là các từ dành riêng (Reserved words) nếu nó không được phép dùng để khai báo các định danh khi lập trình viên viết chương trình Phần lớn các ngôn ngữ lập trình hiện nay sử dụng các từ dành riêng để nâng cao khả năng của chương trình dịch Các câu lệnh thường bắt đầu bằng các từ khoá như là:READ, IF, WHILE,
Việc phân tích cú pháp trong quá trình dịch được thực hiện dễ dàng hơn bằng cách sử dụng các từ dành riêng Ví dụ, việc phân tích cú pháp trong ngôn ngữ FORTRAN sẽ gặp khó khăn đối với các câu lệnh bắt đầu bằng “DO” hay
“IF” vì có thể nó không phải là câu lệnh hay câu lệnh có điều kiện thật sự do DO
và IF trong FORTRAN không phải là từ dành riêng cho nên lập trình viên có thể đặt tên biến trùng với những tên này COBOL lại sử dụng quá nhiều từ dành riêng, có quá nhiều từ dành riêng nên sẽ làm lập trình viên không thể nhớ hết được và không tránh khỏi việc đặt tên trùng với các từ dành riêng Tuy nhiên khó khăn chính khi sử dụng từ dành riêng là khi một ngôn ngữ cần thiết được mở rộng để thêm vào những câu lệnh mới Việc thêm các từ dành riêng mới sẽ làm cho các chương trình đã viết từ trước có sử dụng các biến có tên trùng với từ dành riêng mới sẽ không thể được kiểm tra đúng cú pháp
Từ nhiễu (Noise words): Các từ nhiễu là các từ tùy chọn và được chèn vào
các câu lệnh để tăng tính dễ đọc COBOL cung cấp nhiều các tùy nhọn như vậy
Ví dụ, trong câu lệnh GOTO mà được viết là “GO TO label” (label-nhãn), từ
‘GO” là bắt buộc phải có nhưng từ “TO” là tùy chọn Từ “TO” không mang thông tin và được sử dụng chỉ với mục đích tăng tính dễ đọc (Readability)
Chú thích (comments): Việc cho phép các lời chú thích được chèn vào
trong chương trình sẽ nâng cao tính dễ đọc của chương trình Một ngôn ngữ có thể cho phép chú thích bằng nhiều cách: (1) Tách biệt các dòng chú thích trong chương trình (FORTRAN); (2) Đặt trong các ký tự đánh dấu đặc biệt (trong C lời
Trang 27chú thích được đặt giữa 2 cặp /* */); (3) Bắt đầu tại bất kỳ vị trí nào của dòng lệnh miễn là bắt đầu bởi ký tự đặc biệt (chẳng hạn trong C sử dụng //, trong FORTRAN sử dụng !)
Khoảng trống (Blanks; Spaces): Các quy tắc sử dụng khoảng trống trong
các ngôn ngữ là khác nhau Chẳng hạn, trong FORTRAN khoảng trống không được sử dụng một cách tuỳ ý ngoại trừ trong một chuỗi giá trị của biến kiểu chuỗi Các ngôn ngữ khác sử dụng khoảng trống để đánh dấu sự phân cách giữa các phần tử trong câu lệnh Vì vậy khoảng trống đóng một vai trò quan trọng
Dấu phân cách và dấu ngoặc (Delimiters và Brackets): Một dấu phân
cách là một phần tử được sử dụng chỉ để đánh dấu sự bắt đầu hay kết thúc một đơn vị cú pháp nào đó trong câu lệnh hay biểu thức Dấu ngoặc là dấu phân cách
đi theo cặp Ví dụ các dấu ngoặc vuông hay các cặp “Begin…end” Các dấu phân cách có thể được dùng chỉ để nâng cao tính dễ đọc hay đơn giản chỉ là để đơn giản hóa sự phân tích cú pháp Nhưng nó thường được dùng với mục đích quan trọng hơn, đó là để tránh sự mơ hồ bằng cách định nghĩa một cách tường minh giới hạn của một cấu trúc cú pháp riêng biệt
Khuôn dạng trường cố định (Fixed-fields formats) và tự do (Free-fields
formats): Một cú pháp được gọi là trường tự do (free field) nếu các câu lệnh của chương trình có thể được đặt ở bất kỳ vị trí nào trên dòng lệnh mà không phải lưu
ý đến vị trí của câu lệnh cũng như việc ngắt dòng trong chương trình Cú pháp trường cố định thường sử dụng các vị trí trên dòng lệnh để xác định thông tin đưa vào nghĩa là các phần tử của câu lệnh phải nằm ở những vị trí nhất định trên dòng lệnh Ví dụ, trong FORTRAN, 5 ký tự đầu tiên của dòng lệnh được sử dụng để xác định tên lệnh Các cấu trúc cố định ngày càng hiếm, thay vào đó là các cấu trúc tự do
Biểu thức (Expressions): Biểu thức là các hàm truy cập các đối tượng của
dữ liệu trong một chương trình và trả lại một giá trị nào đó Các biểu thức là khối
cú pháp cơ bản để tạo thành các câu lệnh và đôi khi tạo thành các chương trình Trong các ngôn ngữ mệnh lệnh (imperative language) như ngôn ngữ C, các biểu thức tạo thành các hoạt động cơ bản cho phép trạng thái của máy tính thay đổi theo từng lệnh Trong các ngôn ngữ ứng dụng như ML hay LISP, biểu thức tạo thành các luồng xử lý tuần tự để điều khiển sự thực hiện chương trình
Trang 28Câu lệnh (Statements): Câu lệnh là thành phần cú pháp chủ yếu nhất trong
ngôn ngữ mệnh lệnh mà là ngôn ngữđược dùng nhiều nhất ngày nay Cú pháp của câu lệnh ảnh hưởng nhiều đến toàn bộ tính quy tắc, tính dễ đọc, và tính dễ viết của một ngôn ngữ Một số ngôn ngữ lựa chọn cấu trúc câu lệnh đơn giản, trong khi một số lại sử dụng các cú pháp khác nhau đối với từng loại câu lệnh khác nhau Những ngôn ngữ trước đây thường chú ý đến tính quy tắc trong khi những ngôn ngữ xuất hiện sau này lại chú trọng đến tính dễ đọc
Một sự khác biệt quan trọng nữa trong cấu trúc câu lệnh là sự khác nhau giữa câu lệnh có cấu trúc (structured statement) hay còn gọi là câu lệnh lồng nhau (nested statement) và câu lệnh đơn giản (simple statement) Một câu lệnh đơn giản là câu lệnh không chứa câu lệnh khác nhúng trong nó APL và SNOBOL4 chỉ cho phép các câu lệnh đơn giản Một câu lệnh có cấu trúc là câu lệnh cho phép các câu lệnh khác được thực hiện lồng trong câu lệnh đó
2.1.4 Cấu trúc chương trình tổng thể - chương trình con
Việc tổ chức cú pháp tổng thể của một chương trình chính và của các chương trình con cũng thay đổi như các phương diện khác của cú pháp ngôn ngữ
Định nghĩa chương trình con riêng biệt (Separate subprogram
definitions): FORTRAN giải thích một tổ chức tổng thể là sự kết thúc định nghĩa một chương trình con được xử lý như một đơn vị cú pháp riêng biệt Mỗi chương trình con đều được biên dịch riêng biệt và các chương trình đã được dịch sẽ được liên kết lại tại thời điểm tải chương trình để chạy Hạn chế của cách tổ chức này
là mỗi chương trình con đều cần phải chứa đầy đủ mọi câu lệnh khai báo cho tất
cả các phần tử dữ liệu mặc dù có thể chúng cùng nằm trong một khối nhưng được gọi từ các chương trình con khác nhau
Định nghĩa dữ liệu riêng biệt (Separate data definitions): Một mô hình
khác là nhóm tất cả các phép toán xử lý cùng một đối tượng dữ liệu đã cho lại với nhau Ví dụ, một chương trình con có thể bao gồm tất cả các phép toán liên quan đến cùng một khuôn dạng dữ liệu riêng biệt nào đó trong chương trình, hoặc các phép toán khởi tạo một bản ghi dữ liệu, hoặc các phép toán để in một bản ghi dữ liệu, và có thể là các phép toán để tính toán bản ghi dữ liệu đã cho Đây chính là cách tiếp cận tổng quát của cơ chế lớp (class) trong ngôn ngữ C++ và Smalltalk
Trang 29Định nghĩa chương trình con lồng nhau (Nested subprogram
definitions): PASCAL giải thích cấu trúc chương trình lồng nhau trong đó các định nghĩa của chương trình con xuất hiện như các phần khai báo trong chương trình chính và có thể là bản thân chương trình con lại chứa các định nghĩa chương trình con khác được lồng bên trong nó với độ sâu khai báo là bất kỳ
Định nghĩa giao diện riêng biệt (Separate interface definitions): Cấu trúc
của FORTRAN cho phép dịch dễ dàng các chương trình con, nhưng lại gặp khó khăn trong việc trao đổi dữ liệu giữa các chương trình con Trong khi đó Pascal lại cho phép trình biên dịch truy cập sử dụng đến tất cả sự khai báo, tuy nhiên đổi lại nó lại phải dịch lại toàn bộ chương trình mỗi khi có bất kỳ sự thay đổi nào cho
dù chỉ là một câu lệnh
Trong những chương trình loại này, việc triển khai một chương trình bao gồm một vài chương trình con trao đổi dữ liệu với nhau.Tất cả những thành phần này được gọi là module sẽ được liên kết để tạo ra chương trình có thể chạy được Trong FORTRAN khi có bất kỳ sự thay đổi nào thì chỉ cần dịch lại phần tử có chứa sự thay đổi đó Trong khi đó, ở Pascal, dữ liệu trao đổi giữa các module phải được khai báo giống hệt nhau để trình biên dịch có thể nhận biết được Tuy nhiên để có thể trao đổi được dữ liệu giữa các module thì còn cần thêm các thông tin khác
2.2 Giới thiệu chương trình dịch
2.2.1 Giới thiệu chung:
Để ra lệnh cho máy tính thực hiện những nhiệm vụ do chúng ta yêu cầu thì trước hết chúng ta phải giao tiếp được với máy tính Chúng ta dùng các ngôn ngữ bậc cao như Java, C, Pascal .v.v để viết các chương trình mô tả các công việc máy tính phải làm, nhưng máy tính thì không thể hiểu được các ngôn ngữ bậc cao
do nó chỉ hiểu được các thông tin dưới dạng bit (0, 1) Do đó cần có một quá trình dịch để chuyển đổi các câu lệnh viết bằng ngôn ngữ bậc cao của chúng ta (“mã nguồn/source code”) sang mã máy để máy tính hiểu được (“mã đối tượng/object code”) Quá trình dịch này có thể được thực hiện theo hai cách như sau:
Biên dịch (compilation) Thông dịch (Intepretation)
Trang 30Việc dịch tiến hành theo cách thức nào là phụ thuộc vào ngôn ngữ mà chúng ta lựa chọn Một số ngôn ngữ bậc cao được biên dịch (ví dụ như Turbo Pascal, C, v.v) , trong khi một số khác được thông dịch (ví dụ như Q Basic) Các chương trình thông dịch hay biên dịch đơn giản chỉ là các phần mềm nhỏ, hay nói cách khác chúng chính là các chương trình máy tính, được viết ra với mục đích dịch các câu lệnh do chúng ta viết bằng một ngôn ngữ lập trình nào đó sang khuôn dạng mà máy tính hiểu được Sau đây là giải thích cho sự khác biệt giữa chương trình biên dịch và chương trình thông dịch
So sánh giữa người biên dịch và thông dịch truyền thống
Nếu hai người gốc Anh giao tiếp với nhau thì không có vấn đề gì đáng lưu tâm, nhưng trong trường hợp một người Anh muốn giao tiếp với một người Pháp thì sao? Có hai cách thức để hai người với ngôn ngữ khác nhau có thể giao tiếp được với nhau như sau:
1 Người Anh thuê một người thông dịch để phiên dịch Mỗi câu giao tiếp sẽ được thực hiện qua người thông dịch như sau : người Anh sẽ nói câu đó bằng tiếng Anh, người thông dịch nghe, suy luận để dịch câu nói đó sang tiếng Pháp, rồi nói lại bằng tiếng Pháp cho người Pháp hiểu Việc này được lặp đi lặp lại với từng câu nói Việc trao đổi theo kiểu thông dịch này rất chậm do cuộc trao đổi luôn phải dừng nhiều lần để người thông dịch có thời gian dịch lại nội dung
2 Người Anh viết ra giấy toàn bộ nội dung cần nói bằng tiếng Anh (bản tiếng Anh), sau đó tất cả nội dung đó sẽ được người biên dịch dịch và viết lại bằng tiếng Pháp trên một tờ giấy khác (bản tiếng Pháp) cho người Pháp đọc Có hai yếu tố cần chú ý khi dùng phương pháp này:
a Có một khoảng thời gian trễ lúc ban đầu để chờ bản tiếng Anh dịch xong
b Sau khi dịch xong, người Pháp có thể đọc bản dịch (bản tiếng Pháp) bất kỳ lúc nào với tốc độ đọc bình thường của anh ta
So sánh tương đối, chúng ta có thể coi tiếng Anh là mã nguồn chương trình
và tiếng Pháp là mã máy, và bộ não của người Pháp có thể coi như là bộ xử lý trung tâm CPU của máy tính
Tương tự lý do từ ví dụ trên, ta có thể nhận thấy sự khác nhau giữa hai loại chương trình sau:
1 Trình biên dịch dịch toàn bộ chương trình nguồn sang ngôn ngữ máy, toàn
bộ tệp tin mã nguồn được dịch hoàn toàn một lần sang một tệp tin khác dưới dạng mã máy Tệp tin dạng mãy máy này có thể được lưu trữ ở bất kỳ thiết bị lưu trữ nào ( ví dụ như ổ đĩa mềm, ổ đĩa cứng ) Điều này có nghĩa
là :
Trang 31a Chương trình chỉ có thể chạy khi toàn bộ mã nguồn được dịch sang
mã máy
b Khi có bất kỳ thay đổi nào trong mã nguồn thì phải dịch lại hoàn toàn
2 Trình thông dịch thì thực hiện dịch theo cách khác hoàn toàn, chương
trình viết bằng mã nguồn được CPU hiểu và thực hiện theo từng dòng lệnh một Trình thông dịch dịch dòng lệnh đầu tiên, sau đó chuyển cho máy tính thực hiện ngay Sau đó trình thông dịch tiếp tục dịch dòng lệnh kế tiếp, và lại chuyển cho máy tính thực hiện Việc này được lặp đi lặp lại cho đến khi chạy hết toàn bộ chương trình Điều này có nghĩa là:
a Trình thông dịch là một chương trình cần phải nạp vào bộ nhớ máy tính cùng với chương trình mã nguồn
b Các câu lệnh từ chương trình mã nguồn được lấy ra và chạy từng lệnh một
c Không có bản dịch nào tồn tại, và nếu chương trình muốn chạy lại thì quá trình thông dịch lại phải thực hiện lại
Chú ý:
Có một số vấn đề khác chúng ta cần quan tâm như sau :
- Khi chạy chương trình theo kiểu thông dịch thì sẽ tốn nhiều không gian bộ nhớ hơn so với biên dịch do bản thân trình thông dịch cũng cần phải nạp vào bộ nhớ cùng với chương trình nguồn Còn với kiểu biên dịch thì trình biên dịch chỉ cần nạp vào bộ nhớ khi dịch, còn khi chạy chương trình thì chỉ có chương trình đã được dịch (chương trình đích hay chương trình mã máy) cần phải nạp vào bộ nhớ mà thôi, do đó đòi hỏi ít bộ nhớ hơn
- Khi chúng ta có một file mã máy của chương trình nguồn sau khi biên dịch, thì chúng ta có thể chạy lại chuơng trình bất kỳ lúc nào mà không cần phải dịch lại Còn với kiểu thông dịch, mỗi khi chạy chương trình, chúng ta lại phải thông dịch lại từ đầu
- Chương trình được biên dịch chạy nhanh hơn chương trình được thông dịch
- Tuy nhiên, với kiểu thông dịch, việc sửa đổi chương trình sẽ nhanh hơn và
dễ dàng hơn so với biên dịch, và nhờ đó thời gian cần thiết để phát triển chương trình sẽ giảm đi
2.2.2 Trình biên dịch(compiler) là gì
Trình biên dịch là một chương trình mà dữ liệu đưa vào xử lý là chương trình viết bằng một ngôn ngữ (ngôn ngữ nguồn), sau khi xử lý xong (dịch xong) thì sản phẩm đưa ra là một chương trình có chức năng tương tự nhưng dưới dạng một ngôn ngữ khác (ngôn ngữ đích) Thông thường ngôn ngữ nguồn là các ngôn ngữ bậc cao như C hay Pascal và ngôn ngữ đích là các ngôn ngữ bậc thấp như Assembly hay ngôn ngữ máy Khi dịch chương trình, trình biên dịch còn có khả năng đưa ra các thông báo lỗi và cảnh báo nhằm trợ giúp cho người lập trình
Trang 32chỉnh sửa mã nguồn, đảm bảo cho quá trình dịch được hoàn thành Theo lý thuyết thì ngôn ngữ nguồn và ngôn ngữ đích có thể là bất kỳ ngôn ngữ nào, nhưng trình biên dịch được sử dụng chủ yếu cho việc dịch các chương trình nguồn ASCII viết trên các ngôn ngữ như C sang các ngôn ngữ đặc thù cho máy tính như Sparc Assembly để chạy trên các phần cứng nhất định Tuy chúng ta tập trung chủ yếu vào các trình biên dịch cho các ngôn ngữ lập trình, nhưng kỹ thuật chúng ta học rất có giá trị và hữu ích cho rất nhiều các ứng dụng phân tích và dịch khác (ví dụ như chuyển đổi các chú thích từ javadoc sang HTML, tạo ra một bảng từ kết quả của một truy vấn vào cơ sở dữ liệu SQL )
Theo quan niệm, một trình biên dịch hoạt động theo nhiều giai đoạn, trong mỗi giai đoạn, chuơng trình nguồn sẽ được chuyển đổi từ dạng mã này sang dạng
mã khác Thông thường một số giai đoạn có thể được nhóm lại với nhau và việc chuyển đổi dạng mã giữa các giai đoạn trong một nhóm không nhất thiết phải có
Ba pha đầu của trình biên dịch tạo nên phần chủ yếu của giai đoạn phân tích, thường hay được gọi là phần trước (front end) Phần trước (front end) này phụ thuộc chủ yếu vào ngôn ngữ nguồn và hầu như không phụ thuộc gì vào ngôn ngữ đích Phần trước này thông thường bao gồm các công việc như phân tích từ vựng và cú pháp, tạo bảng ký hiệu, phân tích ngữ nghĩa và tạo ra mã trung gian
Những giai đoạn còn lại của biên dịch, giai đoạn tổng hợp, phần sau (the back end), bao gồm các phần phụ thuộc vào ngôn ngữ đích Trong phần này, chúng ta sẽ thấy có các việc như tối ưu hóa mã chương trình, tạo ra các mã chương trình cùng với việc xử lý lỗi và hoạt động của bảng ký hiệu
Trình biên dịch làm việc như thế nào ?
Quá trình biên dịch được chia thành hai giai đoạn chính, phân tích và tổng hợp Giai đoạn phân tích chia chương trình nguồn thành nhiều phần nhỏ và tạo ra
mã trung gian (độc lập với các ngôn ngữ lập trình) của chương trình Sau đó, giai đoạn tổng hợp sẽ tạo ra chương trình đích từ mã trung gian đó Hai giai đoạn chính này được chia ra thành các giai đoạn nhỏ hơn và trong mỗi giai đoạn nhỏ
đó trình biên dịch thực hiện các công việc khác nhau
2.3 Các pha của một trình biên dịch
Theo quan niệm, một trình biên dịch hoạt động theo nhiều giai đoạn, trong mỗi giai đoạn, chuơng trình nguồn sẽ được chuyển đổi từ dạng mã này sang dạng
mã khác Hình vẽ 1 mô tả các giai đoạn của một trình biên dịch
Trang 33Hình 2.2 Các giai đoạn của biên dịch
Trong thực tế, một số giai đoạn có thể được nhóm lại với nhau và việc chuyển đổi dạng mã giữa các giai đoạn trong một nhóm không nhất thiết phải có
Năm giai đoạn của biên dịch bao gồm:
Phân tích từ vựng Phân tích cú pháp Phân tích ngữ nghĩa Tạo mã trung gian Tạo mã đích
Có hai hoạt động khác tương tác với 5 giai đoạn này, đó là quản lý bảng ký hiệu và xử lý lỗi Ta cũng có thể gọi hai hoạt động này là các giai đoạn khác của biên dịch
2.3.1 Giai đoạn phân tích từ vựng:
• Đây là giai đoạn đầu tiên của trình biên dịch Nhiệm vụ chính của nó là đọc các ký tự nhập vào và đưa ra kết quả là một chuỗi các từ tố (token) mà trình biên dịch sẽ sử dụng nó trong giai đoạn kế tiếp, giai đoạn phân tích
cú pháp
Trang 34• Ngoài nhiệm vụ chính đó, giai đoạn này còn thực hiện một số nhiệm vụ nhất định khác nữa Một trong các nhiệm vụ đó là cắt bỏ các chú giải và khoảng trống trong chương trình Ngoài ra, còn có nhiệm vụ tạo mối tương quan giữa các thông điệp lỗi từ trình biên dịch đến chương trình nguồn Ví dụ, nó giám sát số thứ tự của mỗi dòng lệnh được viết, và khi
có lỗi thì thông báo lỗi cùng với số thứ tự của dòng lệnh bị lỗi được đưa
ra
• Trong giai đoạn phân tích từ vựng, các ký tự tạo nên chương trình được đọc từ trái sang phải và được tập hợp lại thành nhóm tạo thành các từ tố token (là một dãy các ký tự có một ý nghĩa chung nào đó)
• Ví dụ trong 1 câu lệnh gán như sau:
vitri := bandau + tyle*60
có thể được nhóm thành các từ tố như sau:
◊ Trong phần này, chúng ta cùng tìm hiểu về một khái niệm mới là ngữ
pháp phi ngữ cảnh (context-free grammar)
+ Khái niệm này được sử dụng để định rõ cú pháp của một ngôn ngữ
+ Một ngữ pháp thông thường dùng để mô tả cấu trúc theo hệ thống của một ngôn ngữ
◊ Ví dụ, câu lệnh if –else của ngôn ngữ C có dạng như sau :
if (biểu thức) câu lệnh else câu lệnh + Câu lệnh này là ghép nối của từ khóa if, dấu mở ngoặc đơn, một biểu thức, dấu đóng ngoặc đơn, một câu lệnh, từ khóa else, và một câu lệnh khác
+ Ta thay thế biểu thức bằng biến expr, các câu lệnh bằng các biến stmt, stmt1, stmt2 và khi đó cấu trúc của câu lệnh sẽ có dạng sau : stmt -> if (expr) stmt1 else stmt2
+ Trong đó dấu -> được đọc là “có thể có dạng sau”
+ Quy tắc như thế này được gọi là một luật sinh (production)
Trong một luật sinh, những thành phần từ vựng như từ khóa if hay
dấu ngoặc đơn được gọi là các từ tố (tokens)
Trang 35+ Những biến như expr và stmt2 thể hiện tập hợp một chuỗi các từ
tố và được gọi là các ký hiệu chưa kết thúc (nonterminals)
◊ Một ngữ pháp phi ngữ cảnh bao gồm 4 thành phần sau :
+ Một tập các từ tố, được gọi là các ký hiệu kết thúc (terminal
symbol)
+ Một tập các ký hiệu chưa kết thúc + Một tập các luật sinh trong đó mỗi luật sinh bao gồm một ký hiệu
chưa kết thúc, được gọi là phần bên trái của luật sinh, một mũi tên, và một chuỗi các từ tố cùng với các ký hiệu chưa kết thúc, được gọi là phần bên phải của luật sinh
+ Một trong các ký hiệu chưa kết thúc được dùng làm ký hiệu bắt
đầu
◊ Sau đây là ví dụ cho việc sử dụng 1 ngôn ngữ được gọi là SMALL + Chúng ta định nghĩa SMALL là một ngôn ngữ chỉ có các con số (digit) và các dấu cộng (+) và trừ (-)
+ Ví dụ : các biểu thức của SMALL là 9-5+2, 3-1 và 7
+ Trong ngôn ngữ SMALL, dấu + và – phải nằm giữa hai con số và các biểu thức ở trên sẽ được coi là các danh sách (list) của các con
số phân cách bởi dấu + hay – “ + Ngữ pháp phi ngữ cảnh sau đây sẽ định rõ cú pháp của ngôn ngữ
này với list được coi là ký hiệu bắt đầu
1 : list -> list + digit
2 : list -> list – digit
3 : list -> digit
4 : digit -> 0|1|2|3|4|5|6|7|8|9
đưa thêm dấu + hay – và sau đó là một digit thì chúng ta
sẽ có được một list mới
list bằng các lập luận như sau:
∗ 9 là một list, dựa vào luật sinh (3), do 9 là một digit
∗ 9-5 là một list, dựa vào luật sinh (2), do 9 là một list
∗ 5 là một digit
∗ 9-5+2 là một list, dựa vào luật sinh (1), do 9-5 là một list
và 2 là một digit
Trang 36list
list
digit digit
• Việc diễn giải trên có thể được minh họa bằng hình cây như hình vẽ 2
• Mỗi đốt trên hình cây được ghi bằng một ký hiệu ngữ pháp Mỗi đốt bên trong và các nhánh của nó tương ứng với một luật sinh, trong đó mỗi đốt tương ứng với phần bên trái và các nhánh tương ứng với phần bên phải của luật sinh
• Các hình cây như trên được gọi là các cây phân tích cú pháp (parse trees)
• Giai đoạn phân tích cú pháp
◊ Mỗi ngôn ngữ lập trình đều có các quy tắc để qưy định cấu trúc cú pháp các chương trình đúng
◊ Trong Pascal, chương trình được xây dựng từ các khối , mỗi khối được xây dựng từ câu lệnh, mỗi câu lệnh được xây dựng từ các biểu thức, và các biểu thức thì từ các từ tố, v.v
◊ Cú pháp của một ngôn ngữ có thể được mô tả bởi các ngữ pháp phi ngữ cảnh
◊ Các ngữ pháp đưa đến những lợi ích rõ ràng cho cả người thiết kế ngôn ngữ cũng như người viết các trình biên dịch như sau :
+ Một ngữ pháp cung cấp một đặc tả có cú pháp, chính xác, dễ hiểu cho một ngôn ngữ lập trình
+ Từ các loại ngữ pháp nhất định, có thể xây dựng được một cách tự động một một bộ phân tích cú pháp hiệu quả để kiểm tra xem chương trình nguồn có được viết đúng cú pháp hay không
+ Một ngữ pháp xây dựng tốt tạo nên một cấu trúc tốt cho một ngôn ngữ lập trình, và nó rất hữu ích trong việc dịch chính xác từ chương trình nguồn sang mã đối tượng và trong việc phát hiện các lỗi
+ Các ngôn ngữ phát triển theo thời gian và chúng đòi hỏi có những cấu trúc mới để thực hiện được các nhiệm vụ mới Những cấu trúc
Trang 37mới đó có thể được đưa thêm vào ngôn ngữ một cách dễ dàng khi
có bổ sung dựa trên mô tả đúng ngữ pháp của ngôn ngữ
◊ Trong quá trình phân tích cú pháp, những từ tố của chương trình nguồn được nhóm lại thành các cụm từ theo ngữ pháp và những cụm
từ này sẽ được trình biên dịch dùng trong các giai đoạn kế tiếp
nguồn được thể hiện bởi 1 cây phân tích
2.3.4 Giai đoạn tạo mã trung gian
• Sau giai đoạn phân tích cú pháp và phân tích ngữ nghĩa, một số trình biên dịch tạo ra một dạng mã trung gian của chương trình nguồn Dạng mã trung gian này có thể coi là chương trình cho một máy tính trừu tượng
• Trong giai đoạn này, mã trung gian của chương trình nguồn được tạo ra Chúng ta muốn mã trung gian này dễ dàng được tạo ra và cũng dễ dàng chuyển đổi sang chương trình đích Má trung gian này có thể ở có nhiều dạng khác nhau, nhưng dạng thông thường nhất là mã 3 địa chỉ (TAC), rất giống với ngôn ngữ assembly Mã 3 địa chỉ là một loạt các lệnh đơn giản, mỗi lệnh có thể có tối đa 3 toán hạng
• Ví dụ tạo mã trung gian cho câu lệnh sau:
a = b*c+b+d
◊ Câu lệnh trên được chuyển thành 4 lệnh dưới dạng mã ba địa chỉ như sau
_t1 = b*c _t2 = b*d _t3 = _t1+_t2
Trang 38a = _t2
c = _t3
◊ Tối ưu hóa mã trung gian:
+ Phần tối ưu nhận các mã trung gian (ví dụ dưới dạng mã 3 địa chỉ)
và xử lý sau đó đưa ra một phiên bản chuẩn, tối ưu hóa của mã trung gian
+ Trong giai đoạn này trình biên dịch cố gắng đưa ra một phiên bản
mã trung gian nhỏ nhất, nhanh nhất và hiệu quả nhất bằng cách sử dụng một loạt các kỹ thuật khác nhau như:
9 Ngăn chặn việc tạo mã trung gian từ những phần mã nguồn chết (đây là những phần mã nguồn có trong chương trình nhưng không có tác dụng gì với chương trình, ví dụ một lệnh gán giá trị cho 1 biến, nhưng biến đó thì không hề được sử dụng trong suốt toàn bộ quá trình chạy chương trình)
9 loại bỏ những biến không dùng tới ( những biến có khai báo nhưng không dùng)
9 loại bỏ những phép tính nhân với 1 hay cộng với 0
9 tối ưu hóa các vòng lặp (ví dụ như loại bỏ những câu lệnh nằm trong vòng lặp nhưng không có thay đổi gì) loại bỏ những biểu thức phụ ( đây là việc thay thế hai trường hợp tính toán giống nhau bằng một lần tính toán duy nhất)
9 giảm bớt số lượng tính toán ( ví dụ thay thế một phép nhân bằng việc dùng một hằng số và phép cộng cùng phép dịch chuyển)
+ Giai đoạn tối ưu hóa có thể làm giảm tốc độ của trình biên dịch,
do đó nó là một giai đoạn tùy chọn
9 Ví dụ của tối ưu hóa mã trung gian của biểu thức 2*(b*c):
2.3.5 Giai đoạn tạo mã đích
• Giai đoạn cuối cùng của trình biên dịch là tạo mã đích, đó là quá trình chuyển mã trung gian thành những sản phẩm hữu dụng ví dụ như chương trình chạy được
Trang 39• Trong giai đoạn này, chương trình đích được tạo ra Sản phẩm đưa ra trong giai đoạn này thông thường là mã máy hoặc là mã assembly Những vùng nhớ được phân cho các biến, những lệnh chạy được lựa chọn tương ứng với các thao tác, mã 3 địa chỉ được chuyển thành các câu lệnh assembly hay lệnh của máy
MIPS assembly
• Tối ưu hóa mã đích(mã đối tượng)
◊ Đây là giai đoạn tối ưu đi kèm với giai đoạn tạo mã đích
◊ Trong giai đoạn này, mã đối tượng được tối ưu hóa để trở nên gọn và hiệu quả hơn
◊ Trong giai đoạn này người ta thường quan tâm nhiều đến đặc tính của phần cứng để có thể sử dụng hiệu quả bộ vi xử lý và các thanh ghi của
nó
◊ Trình biên dịch có thể tận dụng những ưu điểm riêng của phần cứng máy tính để tối ưu hóa mã đối tượng
◊ Cũng giống như với giai đoạn tối ưu hóa mã trung gian, giai đoạn tối
ưu hóa mã đối tượng là tùy chọn
2.4 Quản lý bảng ký hiệu
Một chức năng quan trọng của trình biên dịch là ghi lại những định danh (ví dụ đó có thể là tên biến, tên hàm, tên thủ tục, ) trong chương trình nguồn và thu thập thông tin về những thuộc tính khác nhau của mỗi định danh Những thuộc tính đó có thể cung cấp thông tin về định danh như kiểu của nó, phạm vi của nó với biến, hay trong trường hợp là tên thủ tục, thì thông tin có thể là số lượng và kiểu của các đối số, cách truyền tham số vào từng đối số, Bảng ký hiệu là một cấu trúc dữ liệu trong đó có chứa bản ghi cho từng định danh, với các trường là các thuộc tính của định danh đó Cấu trúc dữ liệu này cho phép chúng
ta tìm ra bản ghi cho một định danh cụ thể một cách nhanh chóng và lưu giữ hay lấy dữ liệu từ đó ra
Trong giai đoạn phân tích từ vựng, khi trình biên dịch tìm thấy một định danh trong chương trình nguồn thì nó sẽ đưa định danh này vào trong bảng ký hiệu Tuy nhiên, thuộc tính của một định danh thường không được xác định trong
Trang 40giai đoạn phân tích từ vựng Những giai đoạn còn lại sẽ đưa thông tin về định danh vào bảng ký hiệu và sau đó sử dụng chúng theo nhiều cách thức khác nhau
Ví dụ khi tiến hành phân tích ngữ nghĩa, trình biên dịch cần phải biết kiểu của định danh , và từ đó nó mới có thể kiểm tra được chương trình nguồn viết có đúng không
Thông thường, bảng ký hiệu sử dụng cho biên dịch được bỏ đi khi quá trình dịch kết thúc Tuy nhiên nó cũng có thể được lưu giữ trong suốt thời gian chương trình chạy, ví dụ như với những ngôn ngữ cho phép đưa thêm những định danh mới khi chạy chương trình, hay để hỗ trợ cho gỡ rối
2.5 Xử lý macro và thao tác trong thời gian biên dịch
Không phải tất cả các ngôn ngữ đều có đặc tính macro hay có các thao tác trong thời gian biên dịch Nhưng khi có những cái đó thì quá trình xử lý thường được thực hiện ở trong giai đoạn phân tích từ vựng
Một macro đơn giản nhất là một phần của chương trình nguồn dưới dạng text được định nghĩa riêng và được chèn vào chương trình trong thời gian dịch mỗi khi có một lệnh gọi đến macro xuất hiện trong chương trình nguồn Do đó macro rất giống với một chương trình con, chỉ khác ở chỗ chương trình con được dịch riêng và được gọi khi chương trình chạy, trong khi với macro, toàn bộ nội dung của nó được thay thế vào vị trí lệnh gọi trong khi dịch chương trình Macro
có thể chỉ đơn giản là những chuỗi ký tự dùng để thay thế, ví dụ thay thế cho số
PI người ta dùng macro với nội dung là 3.1416, và mỗi khi gặp PI trong chương trình thì 3.1416 được thay vào Thông dụng hơn, chúng giống như những chương trình con, với các tham số đã được xử lý trước khi chúng được thay thế vào chương trình khi có lệnh gọi macro
Khi được phép sử dụng macro, phần phân tích từ vựng phải xác định được lệnh gọi macro trong chương trình nguồn và tiến hành việc thay thế tương ứng nội dung macro vào vị trí lệnh gọi Thông thường việc này kéo theo việc ngắt các
bộ phân tích từ vựng và bộ phân tích cú pháp và giao cho chúng tiến hành việc phân tích chuỗi ký tự thay thế cho macro, sau đó mới quay lại với phần còn lại của chương trình Cách thực hiện khác là phần nội dung của macro được dịch trước một phần sao cho bộ phân tích từ vựng có thể xử lý nó ngay lập tức, chèn vào mã đối tượng tương ứng với vị trí gọi macro và đưa thông tin vào bảng ký hiệu trước khi tiếp tục phân tích chương trình nguồn