Làm thế nào các biến được lưu trữ trong một trình biên dịch ngôn ngữ hoặc trình thông dịch?


8

Giả sử chúng ta đặt một biến trong Python.

five = 5

Bùng nổ. Điều tôi đang tự hỏi là, cái này được lưu trữ như thế nào? Có phải trình biên dịch hoặc trình thông dịch chỉ đặt nó trong một biến như vậy không?

varname = ["five"]
varval  = [5]

Nếu đây là cách nó được thực hiện, nó được lưu trữ ở đâu? Có vẻ như điều này có thể tiếp tục mãi mãi.

Câu trả lời:


13

Thông dịch viên

Một intepreter sẽ làm việc theo cách bạn đoán. Trong một mô hình đơn giản, nó sẽ duy trì một từ điển với các tên biến là các khóa từ điển và các giá trị biến là giá trị từ điển. Nếu ngôn ngữ biết khái niệm các biến chỉ hiển thị trong các ngữ cảnh cụ thể, trình thông dịch sẽ duy trì nhiều từ điển để phản ánh các ngữ cảnh khác nhau. Bản thân trình thông dịch thường là một chương trình được biên dịch, vì vậy để lưu trữ, hãy xem bên dưới.

Trình biên dịch

(Điều này phụ thuộc rất nhiều vào ngôn ngữ và trình biên dịch và cực kỳ đơn giản, vì vậy nó chỉ nhằm đưa ra một số ý tưởng.)

Hãy nói rằng, chúng ta có một biến toàn cầu int five = 5. Một biến toàn cục chỉ tồn tại một lần trong chương trình, do đó trình biên dịch dự trữ một vùng nhớ gồm 4 byte (kích thước int) trong vùng dữ liệu. Nó có thể sử dụng một địa chỉ cố định, giả sử 1234. Trong tệp thực thi, trình biên dịch đặt thông tin rằng bốn byte bắt đầu từ 1234 là cần thiết cho bộ nhớ dữ liệu tĩnh, phải được điền vào số 5 khi bắt đầu chương trình và tùy chọn (cho hỗ trợ trình gỡ lỗi) thông tin rằng vị trí 1234 được gọi fivevà chứa một số nguyên. Bất cứ nơi nào một số dòng mã khác đề cập đến biến có tên five, trình biên dịch sẽ nhớ rằng nó được đặt ở 1234 và chèn một lệnh đọc hoặc ghi bộ nhớ cho địa chỉ 1234.

Nếu int six = 6là một biến cục bộ trong một hàm, nó sẽ tồn tại một lần cho mỗi lệnh gọi hiện đang hoạt động của hàm này (có thể có nhiều vì đệ quy hoặc đa luồng). Vì vậy, mọi hàm gọi ngăn xếp đủ không gian trên ngăn xếp để giữ các biến của nó (bao gồm bốn byte cho sixbiến của chúng tôi . Trình biên dịch quyết định nơi đặt sixbiến trong khung ngăn xếp này, có thể là 8 byte từ khung bắt đầu và ghi nhớ vị trí tương đối đó. Vì vậy, các hướng dẫn mà trình biên dịch tạo ra cho hàm là:

  • nâng cao con trỏ ngăn xếp bằng đủ byte cho tất cả các biến cục bộ của hàm.

  • lưu trữ số 6 (giá trị ban đầu của six) vào vị trí mẹ 8 byte phía trên con trỏ ngăn xếp.

  • Bất cứ nơi nào hàm tham chiếu đến six, trình biên dịch sẽ chèn một lệnh đọc hoặc ghi cho vị trí mẹ 8 byte phía trên con trỏ ngăn xếp.

  • khi kết thúc hàm, tua lại con trỏ ngăn xếp về giá trị cũ của nó.

Một lần nữa, đó chỉ là một mô hình rất đơn giản, không bao gồm tất cả các loại biến, nhưng có lẽ nó giúp hiểu được ...


Dễ hiểu cho một người có trình độ hiểu biết của tôi. Cám ơn rất nhiều! Bạn thực sự làm sáng tỏ điều này!
baranskistad

Nó thậm chí còn điên rồ hơn khi trình biên dịch không biên dịch toàn bộ điều này thành một nhị phân nguyên khối, mà là các đơn vị biên dịch nhỏ (giả sử, một đơn vị cho mỗi tệp nguồn). Sau đó, tất cả các công cụ bên ngoài (chẳng hạn như toàn cầu) phải giải quyết các địa chỉ của nó sau đó, bằng một công cụ riêng gọi là trình liên kết (sẽ kết hợp tất cả các tệp "đối tượng" nhỏ này thành một tệp nhị phân lớn hơn).
Kat

Ngoài ra, nếu một người quen thuộc với các phiên bản C cũ hơn, bản chất của cách các biến cục bộ hoạt động làm cho rõ ràng lý do tại sao C sử dụng để yêu cầu tất cả các biến cục bộ được khai báo ở đầu hàm. Sau đó, chúng tôi quyết định có thể thực hiện đủ để chỉ phân tích toàn bộ hàm trước, để cuối cùng bạn có thể có các khai báo biến ở bất cứ nơi nào bạn muốn.
Kat

Cuối cùng, có lẽ cũng có liên quan rằng không có gì ngăn bạn sử dụng từ điển cho các biến cục bộ với trình biên dịch. Nó chắc chắn sẽ làm cho việc gõ động dễ dàng hơn nhiều (các kiểu động không thể được lưu trữ hoàn toàn trên ngăn xếp vì chúng không có kích thước đã biết vào thời gian biên dịch). Đơn giản là từ điển chậm. Ngẫu nhiên tại sao hầu hết các ngôn ngữ biên dịch sử dụng gõ tĩnh.
Kat

10

Nó phụ thuộc vào việc thực hiện.

Ví dụ, trình biên dịch C có thể duy trì bảng ký hiệu trong quá trình biên dịch. Đây là một cấu trúc dữ liệu phong phú cho phép đẩy và xuất hiện các phạm vi, vì mỗi dấu ngoặc mở câu lệnh ghép có {khả năng giới thiệu một phạm vi mới cho các biến cục bộ mới. Ngoài việc xử lý phạm vi đến và đi, nó còn ghi lại các biến được khai báo và cho mỗi biến bao gồm tên và loại của chúng.

Cấu trúc dữ liệu của bảng ký hiệu này cũng hỗ trợ tìm kiếm thông tin của một biến theo tên, ví dụ như bằng mã định danh của nó và trình biên dịch thực hiện điều này khi liên kết thông tin biến đã khai báo với mã định danh thô mà nó nhìn thấy trong phân tích, vì vậy điều này khá sớm trong quá trình biên dịch.

Tại một số điểm, trình biên dịch gán vị trí cho các biến. Có lẽ các bài tập vị trí được ghi lại trong cùng một cấu trúc dữ liệu bảng biểu tượng. Trình biên dịch có thể thực hiện gán vị trí trực tiếp trong quá trình phân tích cú pháp, nhưng có khả năng có thể thực hiện công việc tốt hơn nếu nó chờ đợi không chỉ sau khi phân tích cú pháp, mà sau khi tối ưu hóa chung.

Tại một số điểm, đối với các biến cục bộ, trình biên dịch gán vị trí ngăn xếp hoặc thanh ghi CPU (có thể phức tạp hơn ở chỗ biến đó thực sự có thể có nhiều vị trí, chẳng hạn như vị trí ngăn xếp cho một số phần của mã được tạo và CPU đăng ký cho các phần khác).

Cuối cùng, trình biên dịch tạo mã thực tế: hướng dẫn máy tham chiếu trực tiếp các giá trị của biến bằng các thanh ghi CPU hoặc vị trí ngăn xếp được chỉ định, khi cần để thực thi mã được biên dịch. Mỗi dòng mã nguồn biên dịch thành một chuỗi các hướng dẫn mã máy riêng, do đó, các hướng dẫn được tạo sẽ mã hóa không chỉ các hoạt động (thêm, trừ) mà còn cả các vị trí của các biến được tham chiếu.

Mã đối tượng cuối cùng đi ra khỏi trình biên dịch không còn có tên và kiểu biến; chỉ có vị trí, vị trí ngăn xếp hoặc thanh ghi CPU. Hơn nữa, không có bảng vị trí, mà thay vào đó, các vị trí này được sử dụng bởi mỗi lệnh máy biết vị trí lưu trữ giá trị của biến. Không tìm kiếm các mã định danh trong mã thời gian chạy, mỗi bit mã được tạo chỉ đơn giản là biết thao tác để thực hiện và (các) vị trí sẽ sử dụng.

Khi gỡ lỗi được kích hoạt trong quá trình biên dịch, trình biên dịch sẽ xuất ra một dạng của bảng ký hiệu để, ví dụ, trình gỡ lỗi sẽ biết tên của các biến tại các vị trí ngăn xếp khác nhau.

Một số ngôn ngữ khác có nhu cầu tra cứu định danh động trong thời gian chạy, do đó cũng có thể cung cấp một số dạng bảng biểu tượng để hỗ trợ các nhu cầu đó.


Thông dịch viên có một loạt các tùy chọn. Họ có thể duy trì cấu trúc dữ liệu giống như bảng biểu tượng để sử dụng trong quá trình thực thi (ngoài việc sử dụng trong quá trình phân tích cú pháp), thay vì chỉ định / theo dõi vị trí ngăn xếp, chỉ cần lưu trữ giá trị cho biến, được liên kết với mục nhập của biến trong bảng ký hiệu cấu trúc dữ liệu.

Một bảng biểu tượng có thể được lưu trữ trong heap chứ không phải trên stack (mặc dù sử dụng stack cho phạm vi và biến là điều chắc chắn, và hơn nữa nó có thể bắt chước một stack trong heap để có được lợi thế thân thiện với bộ đệm của việc đóng gói các giá trị của biến gần nhau khác), do đó, một trình thông dịch có thể đang sử dụng bộ nhớ heap để lưu trữ các giá trị của biến trong khi trình biên dịch sử dụng các vị trí ngăn xếp. Nói chung, trình thông dịch cũng không có quyền tự do sử dụng các thanh ghi CPU làm bộ lưu trữ cho các giá trị của biến vì các thanh ghi CPU đang bận chạy các dòng mã của chính trình thông dịch ...


-3

Cách tốt nhất để hiểu mã của bạn đang được biên dịch thành gì là biên dịch mã của bạn để tập hợp . Mã lắp ráp gần nhất với các hướng dẫn của bộ xử lý đang được thực thi.


1
Dường như với tôi rằng OP không hỏi về mã được biên dịch, nhưng những gì xảy ra trong quá trình biên dịch vì trình biên dịch đang phân tích mã.
dùng1118321
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.