Thiết kế trình biên dịch: Môi trường thực thi

Thiết kế trình biên dịch: Môi trường thực thi

Trong hướng dẫn trước, bạn sẽ tìm hiểu bảng ký hiệu là gì? Cách trình biên dịch sử dụng bảng ký hiệu trong thiết kế trình biên dịch. Nếu bỏ lỡ thì bạn có thể xem ở đây:

Thiết kế trình biên dịch: Bảng ký hiệu
Trong hướng dẫn này, bạn sẽ tìm hiểu bảng ký hiệu là gì? Cách trình biên dịch sử dụng bảng ký hiệu trong thiết kế trình biên dịch.

Một chương trình giống như một mã nguồn chỉ đơn thuần là một tập hợp các văn bản (mã, các câu lệnh, v.v.) và để làm cho nó tồn tại, nó yêu cầu các hành động được thực hiện trên máy đích.

Một chương trình cần tài nguyên bộ nhớ để thực hiện các lệnh. Một chương trình chứa tên cho các thủ tục, số nhận dạng, v.v., yêu cầu ánh xạ với vị trí bộ nhớ thực trong thời gian chạy.

Theo trình thực thi, chúng có nghĩa là một chương trình đang được thực thi. Môi trường thực thi là một trạng thái của máy đích, có thể bao gồm các thư viện phần mềm, các biến môi trường, v.v., để cung cấp dịch vụ cho các tiến trình đang chạy trong hệ thống.

Hệ thống hỗ trợ thực thi là một gói, phần lớn được tạo bằng chính chương trình thực thi và tạo điều kiện cho quá trình giao tiếp giữa tiến trình và môi trường thực thi. Nó đảm nhiệm việc cấp phát bộ nhớ và hủy cấp phát trong khi chương trình đang được thực thi.

Cây kích hoạt

Chương trình là một chuỗi các lệnh được kết hợp thành một số thủ tục. Các chỉ dẫn trong một thủ tục được thực hiện tuần tự.

Một thủ tục có một dấu phân cách bắt đầu và kết thúc và mọi thứ bên trong nó được gọi là phần thân của thủ tục.

Định danh thủ tục và chuỗi các lệnh hữu hạn bên trong nó tạo nên phần thân của thủ tục.

Việc thực hiện một thủ tục được gọi là sự kích hoạt của nó. Một bản ghi kích hoạt chứa tất cả các thông tin cần thiết cần thiết để gọi một thủ tục. Bản ghi kích hoạt có thể chứa các đơn vị sau (tùy thuộc vào ngôn ngữ nguồn được sử dụng).

Tạm thời Lưu trữ các giá trị tạm thời và trung gian của một biểu thức.
Dữ liệu cục bộ Lưu trữ dữ liệu cục bộ của thủ tục được gọi.
Trạng thái máy Lưu trữ trạng thái máy như Thanh ghi, Bộ đếm chương trình, v.v., trước khi thủ tục được gọi.
Kiểm soát liên kết Lưu trữ địa chỉ của hồ sơ kích hoạt của trình gọi thủ tục.
Truy cập liên kết Lưu trữ thông tin dữ liệu nằm ngoài phạm vi cục bộ.
Các thông số thực tế Lưu trữ các tham số thực, tức là các tham số được sử dụng để gửi đầu vào cho thủ tục được gọi.
Giá trị trả về Lưu trữ các giá trị trả về.

Bất cứ khi nào một thủ tục được thực thi, bản ghi kích hoạt của nó được lưu trữ trên ngăn xếp, còn được gọi là ngăn xếp điều khiển.

Khi một thủ tục gọi một thủ tục khác, quá trình thực thi của trình gọi sẽ bị tạm dừng cho đến khi thủ tục được gọi kết thúc thực thi. Tại thời điểm này, bản ghi kích hoạt của thủ tục được gọi được lưu trữ trên ngăn xếp.

Chúng ta giả sử rằng chương trình điều khiển chạy theo cách tuần tự và khi một thủ tục được gọi, điều khiển của nó được chuyển sang thủ tục được gọi.

Khi một thủ tục được gọi được thực thi, nó sẽ trả lại quyền điều khiển cho người gọi. Loại luồng điều khiển này giúp dễ dàng biểu diễn một loạt các hoạt động dưới dạng cây, được gọi là cây kích hoạt.

Để hiểu khái niệm này, chúng tôi lấy một đoạn mã làm ví dụ:

//. . .
printf(“Enter Your Name: “);
scanf(“%s”, username);
show_data(username);
printf(“Press any key to continue…”);
//. . .
int show_data(char *user)
{
    printf(“Your name is %s”, username);
    return 0;
}
//. . . 

Dưới đây là cây kích hoạt của mã được đưa ra.

Sơ đồ cây kích hoạt

Bây giờ chúng ta hiểu rằng các thủ tục được thực thi theo cách chuyên sâu, do đó, phân bổ ngăn xếp là hình thức lưu trữ phù hợp nhất để kích hoạt thủ tục.

Phân bổ lưu trữ

Môi trường thực thi quản lý các yêu cầu bộ nhớ thời gian chạy cho các thực thể sau:

  • : Nó được gọi là phần văn bản của chương trình không thay đổi trong thời gian chạy. Yêu cầu bộ nhớ của nó đã được biết tại thời điểm biên dịch.
  • Thủ tục: Phần văn bản của chúng là tĩnh nhưng chúng được gọi theo cách ngẫu nhiên. Đó là lý do tại sao, lưu trữ ngăn xếp được sử dụng để quản lý các lệnh gọi và kích hoạt thủ tục.
  • Biến: Các biến chỉ được biết trong thời gian chạy, trừ khi chúng là toàn cục hoặc hằng số. Lược đồ cấp phát bộ nhớ Heap được sử dụng để quản lý cấp phát và hủy cấp phát bộ nhớ cho các biến trong thời gian chạy.

Phân bổ tĩnh

Trong lược đồ cấp phát này, dữ liệu biên dịch được gắn vào một vị trí cố định trong bộ nhớ và nó không thay đổi khi chương trình thực thi.

Vì yêu cầu bộ nhớ và các vị trí lưu trữ đã được biết trước, nên không cần gói hỗ trợ thời gian chạy để cấp phát và hủy cấp phát bộ nhớ.

Phân bổ ngăn xếp

Các lệnh gọi thủ tục và các kích hoạt của chúng được quản lý bằng cách cấp phát bộ nhớ ngăn xếp.

Nó hoạt động theo phương pháp last-in-first-out (LIFO) và chiến lược phân bổ này rất hữu ích cho các cuộc gọi thủ tục đệ quy.

Phân bổ heap

Các biến cục bộ cho một thủ tục chỉ được cấp phát và hủy cấp phát trong thời gian chạy.

Cấp phát heap được sử dụng để cấp phát động bộ nhớ cho các biến và thu hồi nó trở lại khi các biến không còn được yêu cầu nữa.

Ngoại trừ vùng bộ nhớ được cấp phát tĩnh, cả bộ nhớ ngăn xếp và bộ nhớ heap đều có thể phát triển và thu nhỏ một cách động. Do đó, chúng không thể được cung cấp một lượng bộ nhớ cố định trong hệ thống.

Phân bổ heap

Như thể hiện trong hình trên, phần văn bản của mã được cấp phát một lượng bộ nhớ cố định. Bộ nhớ ngăn xếp và bộ nhớ heap được sắp xếp ở các cực của tổng bộ nhớ được phân bổ cho chương trình. Cả hai đều co lại và phát triển đối nghịch nhau.

Truyền tham số

Phương tiện giao tiếp giữa các thủ tục được gọi là truyền tham số. Giá trị của các biến từ một thủ tục gọi được chuyển sang thủ tục được gọi bởi một số cơ chế.

Trước khi tiếp tục, trước tiên hãy xem qua một số thuật ngữ cơ bản liên quan đến các giá trị trong một chương trình.

giá trị r

Giá trị của một biểu thức được gọi là giá trị r của nó. Giá trị chứa trong một biến đơn lẻ cũng trở thành giá trị r nếu nó xuất hiện ở phía bên phải của toán tử gán. Giá trị r luôn có thể được gán cho một số biến khác.

giá trị l

Vị trí của bộ nhớ (địa chỉ) nơi một biểu thức được lưu trữ được gọi là giá trị l của biểu thức đó. Nó luôn xuất hiện ở bên trái của toán tử gán.

Ví dụ:

day = 1;
week = day * 7;
month = 1;
year = month * 12;

Từ ví dụ này, chúng ta hiểu rằng các giá trị không đổi như 1, 7, 12 và các biến như ngày, tuần, tháng và năm, tất cả đều là giá trị r. Chỉ các biến có giá trị l vì chúng cũng đại diện cho vị trí bộ nhớ được gán cho chúng.

Ví dụ:

7 = x + y;

là lỗi giá trị l, vì hằng số 7 không đại diện cho bất kỳ vị trí bộ nhớ nào.

Tham số chính thức

Các biến lấy thông tin được truyền bởi thủ tục người gọi được gọi là các tham số chính thức. Các biến này được khai báo trong định nghĩa của hàm được gọi.

Các tham số thực tế

Các biến có giá trị hoặc địa chỉ được truyền đến thủ tục được gọi được gọi là tham số thực tế. Các biến này được chỉ định trong lệnh gọi hàm dưới dạng đối số.

Ví dụ:

void fun_one()
{
    int actual_parameter = 10;
    call fun_two(int actual_parameter);
}

void fun_two(int formal_parameter)
{
    print formal_parameter;
}

Các tham số thực tế giữ thông tin của tham số thực, tùy thuộc vào kỹ thuật truyền tham số được sử dụng. Nó có thể là một giá trị hoặc một địa chỉ.

Truyền giá trị

Trong cơ chế truyền giá trị, thủ tục gọi chuyển giá trị r của các tham số thực tế và trình biên dịch đưa giá trị đó vào bản ghi kích hoạt của thủ tục được gọi.

Các tham số chính thức sau đó giữ các giá trị được truyền bởi thủ tục gọi. Nếu các giá trị được nắm giữ bởi các tham số chính thức được thay đổi, nó sẽ không ảnh hưởng đến các tham số thực tế.

Truyền tham chiếu

Trong cơ chế truyền tham chiếu, giá trị l của tham số thực được sao chép vào bản ghi kích hoạt của thủ tục được gọi.

Bằng cách này, thủ tục được gọi bây giờ có địa chỉ (vị trí bộ nhớ) của tham số thực và tham số chính thức đề cập đến cùng một vị trí bộ nhớ.

Do đó, nếu giá trị được trỏ bởi tham số chính thức bị thay đổi, tác động sẽ được nhìn thấy trên tham số thực vì chúng cũng phải trỏ đến cùng một giá trị.

Truyền sao chép-khôi phục

Cơ chế truyền tham số này hoạt động tương tự như ‘truyền tham chiếu’ ngoại trừ việc các thay đổi đối với tham số thực được thực hiện khi thủ tục được gọi kết thúc.

Khi gọi hàm, giá trị của các tham số thực tế được sao chép trong bản ghi kích hoạt của thủ tục được gọi.

Các tham số chính thức nếu được thao tác sẽ không có tác dụng theo thời gian thực đối với các tham số thực (khi các giá trị l được truyền), nhưng khi thủ tục được gọi kết thúc, các giá trị l của các tham số chính thức được sao chép sang các giá trị l của các tham số thực tế.

Ví dụ:

int y; 
void calling_procedure() 
{
    y = 10;      
    copy_restore(y); //l-value of y is passed
    printf y; //prints 99 
}

void copy_restore(int x) 
{      
    x = 99; // y still has value 10 (unaffected)
    y = 0; // y is now 0 
}

Khi hàm này kết thúc, giá trị l của tham số chính thức x được sao chép vào tham số thực tế y. Ngay cả khi giá trị của y được thay đổi trước khi thủ tục kết thúc, giá trị l của x được sao chép sang giá trị l của y khiến nó hoạt động giống như lệnh gọi bằng tham chiếu.

Truyền tên

Các ngôn ngữ như Algol cung cấp một loại cơ chế truyền tham số mới hoạt động giống như bộ tiền xử lý trong ngôn ngữ C.

Trong cơ chế truyền tên, tên của thủ tục đang được gọi được thay thế bằng phần thân thực của nó.

Truyền tên thay thế bằng văn bản các biểu thức đối số trong một lệnh gọi thủ tục cho các tham số tương ứng trong phần thân của thủ tục để bây giờ nó có thể hoạt động trên các tham số thực tế, giống như kiểu truyền qua tham chiếu.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *