Tôi đã viết một tập lệnh bash và tôi đã thực hiện nó mà không cần biên dịch nó trước. Nó hoạt động hoàn hảo. Nó có thể hoạt động với hoặc không có quyền, nhưng khi nói đến các chương trình C, chúng ta cần biên dịch mã nguồn. Tại sao?
Tôi đã viết một tập lệnh bash và tôi đã thực hiện nó mà không cần biên dịch nó trước. Nó hoạt động hoàn hảo. Nó có thể hoạt động với hoặc không có quyền, nhưng khi nói đến các chương trình C, chúng ta cần biên dịch mã nguồn. Tại sao?
Câu trả lời:
Điều đó có nghĩa là các tập lệnh shell không được biên dịch, chúng được diễn giải: shell diễn giải các tập lệnh một lệnh tại một thời điểm và tìm ra mỗi lần cách thực hiện mỗi lệnh. Điều đó có ý nghĩa đối với các kịch bản shell vì dù sao họ cũng dành phần lớn thời gian để chạy các chương trình khác.
Mặt khác, các chương trình C thường được biên dịch: trước khi chúng có thể được chạy, một trình biên dịch sẽ chuyển đổi chúng thành toàn bộ mã máy, một lần và mãi mãi. Trước đây đã có người phiên dịch C (như người phiên dịch C của HiSoft trên Atari ST) nhưng họ rất bất thường. Ngày nay trình biên dịch C rất nhanh; TCC nhanh đến mức bạn có thể sử dụng nó để tạo "tập lệnh C", với #!/usr/bin/tcc -run
shebang, vì vậy bạn có thể tạo chương trình C chạy giống như tập lệnh shell (theo quan điểm của người dùng).
Một số ngôn ngữ thường có cả trình thông dịch và trình biên dịch: BASIC là một ví dụ gây chú ý.
Bạn cũng có thể tìm thấy cái gọi là trình biên dịch shell shell nhưng những cái tôi đã thấy chỉ là các trình bao bọc che giấu: chúng vẫn sử dụng shell để thực sự diễn giải kịch bản. Như mtraceur chỉ ra mặc dù trình biên dịch shell shell thích hợp chắc chắn là có thể, chỉ là không thú vị lắm.
Một cách nghĩ khác về điều này là xem xét rằng khả năng diễn giải kịch bản của shell là một phần mở rộng của khả năng xử lý dòng lệnh của nó, điều này dẫn đến một cách tiếp cận được diễn giải. Mặt khác, C được thiết kế để tạo ra các nhị phân độc lập; điều này dẫn đến một cách tiếp cận tổng hợp. Các ngôn ngữ thường được biên dịch cũng có xu hướng mọc lên các trình thông dịch, hoặc ít nhất là các trình phân tích cú pháp dòng lệnh (được gọi là REPL, các vòng lặp đọc-in ; bản thân vỏ là REPL).
execve
, open
, close
, read
, write
, và pipe
syscalls, xen kẽ với một số getenv
, setenv
và các hoạt động nội bộ hashmap / mảng (cho các biến phi xuất khẩu ), v.v. Vỏ Bourne và các dẫn xuất cũng không phải là ngôn ngữ lập trình được hưởng lợi nhiều từ các chỉnh sửa trình biên dịch cấp thấp như sắp xếp lại mã, v.v.
Hãy xem xét chương trình sau:
2 Mars Bars
2 Milks
1 Bread
1 Corn Flakes
Trên bash
đường đi, bạn đi lang thang quanh cửa hàng để tìm các quán rượu, cuối cùng tìm thấy chúng, sau đó đi lang thang tìm sữa v.v ... Điều này hiệu quả bởi vì bạn đang chạy một chương trình phức tạp có tên "Người mua hàng có kinh nghiệm" có thể nhận ra bánh mì khi bạn nhìn thấy và tất cả các phức tạp khác của mua sắm. bash
là một chương trình khá phức tạp.
Ngoài ra, bạn có thể đưa danh sách mua sắm của bạn cho một trình biên dịch mua sắm. Trình biên dịch suy nghĩ một lúc và cung cấp cho bạn một danh sách mới. Danh sách này là DÀI , nhưng bao gồm nhiều hướng dẫn đơn giản hơn:
... lots of instructions on how to get to the store, get a shopping cart etc.
move west one aisle.
move north two sections.
move hand to shelf three.
grab object.
move hand to shopping cart.
release object.
... and so on and so forth.
Như bạn có thể thấy, trình biên dịch biết chính xác mọi thứ trong cửa hàng nên không cần đến toàn bộ giai đoạn "tìm kiếm mọi thứ".
Đây là một chương trình theo đúng nghĩa của nó và không cần "Người mua hàng có kinh nghiệm" để thực hiện. Tất cả những gì nó cần là một con người với "Hệ điều hành cơ bản của con người".
Quay trở lại các chương trình máy tính: bash
là "Người mua hàng có kinh nghiệm" và có thể lấy một tập lệnh và chỉ cần thực hiện nó mà không cần biên dịch bất cứ điều gì. Trình biên dịch AC tạo ra một chương trình độc lập không còn cần sự trợ giúp để chạy.
Cả phiên dịch viên và trình biên dịch đều có những ưu điểm và nhược điểm.
Tất cả là do sự khác biệt về kỹ thuật giữa cách chương trình bạn có thể đọc / ghi khi con người được chuyển đổi thành máy hướng dẫn máy tính của bạn hiểu - và những ưu điểm và nhược điểm khác nhau của mỗi phương pháp là lý do tại sao một số ngôn ngữ được viết cần trình biên dịch và một số được viết để được giải thích.
(Lưu ý: Tôi đơn giản hóa rất nhiều ở đây để giải quyết câu hỏi. Để hiểu sâu hơn, các ghi chú kỹ thuật ở cuối câu trả lời của tôi chi tiết / tinh chỉnh một số đơn giản hóa ở đây và nhận xét về câu trả lời này có một số làm rõ và thảo luận hữu ích là tốt ..)
Về cơ bản có hai loại ngôn ngữ lập trình chung:
C nằm trong danh mục đầu tiên ( trình biên dịch C dịch ngôn ngữ C sang mã máy của máy tính của bạn : mã máy được lưu vào một tệp và sau đó khi bạn chạy mã máy đó, nó sẽ thực hiện những gì bạn muốn).
bash thuộc loại thứ hai (trình thông dịch bash đọc ngôn ngữ bash và trình thông dịch bash thực hiện những gì bạn muốn: vì vậy không có "mô-đun trình biên dịch" mỗi se, trình thông dịch thực hiện phiên dịch và thực thi, trong khi trình biên dịch đọc và dịch) .
Bạn có thể đã nhận thấy điều này có nghĩa là gì:
Với C, bạn thực hiện bước "phiên dịch" một lần , sau đó bất cứ khi nào bạn cần chạy chương trình, bạn chỉ cần nói với máy tính của mình thực thi mã máy - máy tính của bạn có thể chạy trực tiếp mà không cần phải thực hiện thêm "suy nghĩ" nào.
Với bash, bạn phải thực hiện bước "phiên dịch" mỗi khi bạn chạy chương trình - máy tính của bạn đang chạy trình thông dịch bash và trình thông dịch bash thực hiện thêm "suy nghĩ" để tìm ra những gì nó cần làm cho mỗi lệnh, mỗi lần .
Vì vậy, các chương trình C cần nhiều CPU, bộ nhớ và thời gian hơn để chuẩn bị (bước biên dịch) nhưng tốn ít thời gian và công sức hơn để chạy. các chương trình bash tốn ít CPU, bộ nhớ và thời gian để chuẩn bị, nhưng cần nhiều thời gian và công việc hơn để chạy. Bạn có thể không nhận thấy những khác biệt này hầu hết thời gian vì máy tính ngày nay rất nhanh, nhưng nó tạo ra sự khác biệt và sự khác biệt đó cộng lại khi bạn cần chạy các chương trình lớn hoặc phức tạp, hoặc nhiều chương trình nhỏ.
Ngoài ra, vì các chương trình C được chuyển đổi thành mã máy ("ngôn ngữ bản địa") của máy tính, bạn không thể lấy một chương trình và sao chép nó vào một máy tính khác có mã máy khác (ví dụ: Intel 64 bit vào Intel 32 -bit, hoặc từ Intel sang ARM hoặc MIPS hoặc bất cứ điều gì). Bạn phải dành thời gian để biên dịch nó cho ngôn ngữ máy khác đó một lần nữa . Nhưng một chương trình bash chỉ có thể được chuyển sang một máy tính khác có cài đặt trình thông dịch bash và nó sẽ chạy tốt.
Các nhà sản xuất của C đã viết một hệ điều hành và các chương trình khác trên phần cứng từ nhiều thập kỷ trước, điều đó khá hạn chế bởi các tiêu chuẩn hiện đại. Vì nhiều lý do, chuyển đổi các chương trình thành mã máy của máy tính là cách tốt nhất hướng tới mục tiêu đó cho họ vào thời điểm đó. Thêm vào đó, họ đang thực hiện loại công việc mà điều quan trọng là mã họ viết đã chạy hiệu quả .
Và các nhà sản xuất shell Bourne và bash muốn ngược lại: Họ muốn viết các chương trình / lệnh có thể được thực thi ngay lập tức - trên dòng lệnh, trong một thiết bị đầu cuối, bạn muốn chỉ viết một dòng, một lệnh và có nó hành hình. Và họ muốn các tập lệnh mà bạn đã viết để làm việc ở bất cứ nơi nào bạn đã cài đặt trình thông dịch / chương trình shell.
Vì vậy, trong ngắn hạn, bạn không cần một trình biên dịch cho bash nhưng bạn cần một trình biên dịch cho C vì các ngôn ngữ đó được chuyển đổi thành các hành động máy tính thực tế khác nhau và những cách làm khác nhau đó được chọn vì các ngôn ngữ có mục tiêu khác nhau.
Bạn thực sự có thể tạo một trình thông dịch C hoặc trình biên dịch bash. Không có gì ngăn cản điều đó là có thể: đó chỉ là những ngôn ngữ được tạo ra cho các mục đích khác nhau. Việc viết lại chương trình bằng ngôn ngữ khác thường dễ hơn là viết một trình thông dịch hoặc trình biên dịch tốt cho một ngôn ngữ lập trình phức tạp. Đặc biệt là khi những ngôn ngữ đó có một điều cụ thể mà họ giỏi và được thiết kế với một cách làm việc nhất định ngay từ đầu. C được thiết kế để biên dịch, do đó, nó thiếu rất nhiều tốc ký tiện lợi mà bạn muốn trong một vỏ tương tác, nhưng nó rất tốt để thể hiện dữ liệu / bộ nhớ ở mức độ thấp rất cụ thể và tương tác với hệ điều hành , đó là những nhiệm vụ bạn thường thấy mình làm khi bạn muốn viết mã được biên dịch hiệu quả. Trong khi đó, bash rất giỏi trong việc thực hiện các chương trình khác,
Chi tiết nâng cao hơn: Có những ngôn ngữ lập trình thực sự là sự pha trộn của cả hai loại (chúng dịch mã nguồn "hầu hết mọi cách", để chúng có thể thực hiện hầu hết việc diễn giải / "suy nghĩ" một lần và chỉ làm một chút của việc giải thích / "suy nghĩ" sau này). Java, Python và nhiều ngôn ngữ hiện đại khác thực sự là những hỗn hợp như vậy: chúng cố gắng mang lại cho bạn một số lợi ích về tính di động và / hoặc phát triển nhanh của các ngôn ngữ được dịch và một số tốc độ của ngôn ngữ được biên dịch. Có rất nhiều cách có thể để kết hợp các cách tiếp cận như vậy và các ngôn ngữ khác nhau thực hiện theo cách khác nhau. Nếu bạn muốn đi sâu vào chủ đề này, bạn có thể đọc các ngôn ngữ lập trình biên dịch thành "mã byte" (giống như biên dịch thành "ngôn ngữ máy" tự tạo của bạn
Bạn đã hỏi về bit thực thi: thực ra, bit thực thi chỉ ở đó để báo cho hệ điều hành biết rằng tệp đó được phép thực thi. Tôi nghi ngờ rằng lý do duy nhất các tập lệnh bash hoạt động cho bạn mà không có sự cho phép thực thi là vì bạn đang chạy chúng từ bên trong một bash shell. Thông thường, hệ điều hành, khi được yêu cầu thực thi một tệp mà không thiết lập bit thực thi, sẽ chỉ trả về một lỗi. Nhưng một số shell như bash sẽ thấy lỗi đó và tự mình chạy nó để chạy tệp, bằng cách mô phỏng các bước mà hệ điều hành thường sẽ thực hiện (tra cứu dòng "#!" Ở đầu tệp và thử để thực thi chương trình đó để giải thích tệp, với mặc định là chính nó hoặc /bin/sh
nếu không có dòng "#!").
Đôi khi một trình biên dịch đã được cài đặt trên hệ thống của bạn và đôi khi các IDE đi kèm với trình biên dịch riêng của chúng và / hoặc chạy trình biên dịch cho bạn. Điều này có thể làm cho một ngôn ngữ được biên dịch "cảm thấy" giống như một ngôn ngữ không được biên dịch để sử dụng, nhưng sự khác biệt về kỹ thuật vẫn còn đó.
Một ngôn ngữ "được biên dịch" không nhất thiết phải được biên dịch thành mã máy và toàn bộ việc biên dịch đây là một chủ đề. Về cơ bản, thuật ngữ này được sử dụng rộng rãi: nó thực sự có thể đề cập đến một vài điều. Theo một nghĩa cụ thể, "trình biên dịch" chỉ là một trình dịch từ một ngôn ngữ (thường là ngôn ngữ "cấp cao hơn" dễ sử dụng hơn) sang ngôn ngữ khác (thường là ngôn ngữ "cấp thấp hơn" mà máy tính dễ sử dụng hơn - đôi khi, nhưng thực sự không thường xuyên, đây là mã máy). Ngoài ra, đôi khi khi mọi người nói "trình biên dịch", họ thực sự đang nói về nhiều chương trình làm việc cùng nhau (đối với trình biên dịch C điển hình, thực tế có bốn chương trình: "tiền xử lý", trình biên dịch, "trình biên dịch" và "trình biên dịch" liên kết ").
Ngôn ngữ lập trình / kịch bản có thể được biên dịch hoặc giải thích.
Các tệp thực thi được biên dịch luôn nhanh hơn và nhiều lỗi có thể được phát hiện trước khi thực thi.
Các ngôn ngữ được giải thích thường đơn giản hơn để viết và thích nghi ít nghiêm ngặt hơn các ngôn ngữ được biên dịch và không yêu cầu biên dịch giúp chúng dễ phân phối hơn.
Hãy tưởng tượng rằng tiếng Anh không phải là ngôn ngữ mẹ đẻ của bạn (điều đó có thể khá dễ dàng với bạn nếu tiếng Anh không phải là ngôn ngữ mẹ đẻ của bạn).
Có 3 cách bạn có thể đọc nó:
Máy tính có "ngôn ngữ bản địa" gồm nhiều loại - sự kết hợp các hướng dẫn mà bộ xử lý hiểu và hướng dẫn mà hệ điều hành (ví dụ: Windows, Linux, OSX, v.v.) hiểu. Ngôn ngữ này không thể đọc được bởi con người.
Các ngôn ngữ script, như Bash, thường rơi vào loại 1 và 2. Chúng lấy một dòng tại một thời điểm, dịch dòng đó và chạy nó, sau đó chuyển sang dòng tiếp theo. Trên Mac và Linux, khá nhiều trình thông dịch khác nhau được cài đặt theo mặc định cho các ngôn ngữ khác nhau, chẳng hạn như Bash, Python và Perl. Trên Windows, bạn phải tự cài đặt chúng.
Nhiều ngôn ngữ kịch bản xử lý trước một chút - cố gắng tăng tốc độ thực thi bằng cách biên dịch các đoạn mã sẽ được chạy thường xuyên hoặc điều đó sẽ làm chậm ứng dụng. Một số thuật ngữ bạn có thể nghe về bao gồm biên dịch Ahead-of-time (AOT) hoặc Just-in-time (JIT).
Cuối cùng, các ngôn ngữ được biên dịch - như C - dịch toàn bộ chương trình trước khi bạn có thể chạy chúng. Điều này có lợi thế là bản dịch có thể được thực hiện trên một máy khác để thực thi, vì vậy khi bạn đưa chương trình cho người dùng, trong khi vẫn có thể có lỗi, một số loại lỗi có thể được xóa sạch. Giống như nếu bạn đưa cái này cho người dịch của bạn, và tôi đề cập đến cách garboola mizene resplunks
, nó có thể trông giống tiếng Anh hợp lệ với bạn nhưng người dịch có thể nói với bạn rằng tôi đang nói chuyện vô nghĩa. Khi bạn chạy một chương trình được biên dịch, nó không cần một trình thông dịch - nó đã có trong ngôn ngữ mẹ đẻ của máy tính
Tuy nhiên, có một nhược điểm của ngôn ngữ được biên dịch: Tôi đã đề cập rằng máy tính có ngôn ngữ bản địa, bao gồm các tính năng từ phần cứng và hệ điều hành - tốt, nếu bạn biên dịch chương trình của mình trên Windows, bạn sẽ không mong đợi chương trình được biên dịch chạy trên máy Mac. Một số ngôn ngữ khắc phục điều này bằng cách biên dịch thành một loại ngôn ngữ nửa vời - hơi giống tiếng Anh Pidgin - theo cách đó, bạn có được lợi ích của ngôn ngữ được biên dịch, cũng như tăng tốc độ nhỏ, nhưng điều đó có nghĩa là bạn cần phải bó một trình thông dịch với mã của bạn (hoặc sử dụng một trình thông dịch đã được cài đặt).
Cuối cùng, IDE của bạn có thể đã biên dịch các tệp của bạn cho bạn và có thể cho bạn biết về các lỗi trước khi bạn chạy mã. Đôi khi, kiểm tra lỗi này có thể sâu hơn trình biên dịch sẽ làm. Một trình biên dịch thường sẽ chỉ kiểm tra nhiều như nó cần để nó có thể tạo ra mã gốc hợp lý. Một IDE thường sẽ chạy một vài kiểm tra bổ sung và có thể cho bạn biết, ví dụ, nếu bạn đã xác định một biến hai lần hoặc nếu bạn đã nhập một cái gì đó mà bạn chưa sử dụng.
Rất nhiều người đang nói về giải thích so với biên dịch nhưng tôi nghĩ rằng điều này có thể gây hiểu lầm đôi chút nếu bạn nhìn kỹ vào nó vì một số ngôn ngữ được giải thích thực sự được biên dịch thành mã byte trung gian trước khi thực thi.
Cuối cùng, lý do thực sự khiến các chương trình C cần được biên dịch thành định dạng thực thi là máy tính cần thực hiện nhiều công việc để chuyển đổi mã trong tệp nguồn C thành một thứ gì đó có thể chạy để lưu sản phẩm một cách hợp lý của tất cả những gì hoạt động thành một tập tin thực thi, do đó bạn không cần phải làm lại mỗi lần bạn muốn chạy chương trình của mình.
Mặt khác, trình thông dịch Shell cần thực hiện rất ít công việc để chuyển đổi tập lệnh shell thành "hoạt động của máy". Về cơ bản chỉ cần đọc dòng script theo từng dòng, phân tách nó trên khoảng trắng, thiết lập một số chuyển hướng tập tin và đường ống và sau đó thực hiện một fork + exec. Do chi phí phân tích và xử lý đầu vào văn bản của tập lệnh shell rất nhỏ so với thời gian khởi chạy các quy trình trong tập lệnh shell, nên sẽ quá mức để biên dịch tập lệnh shell sang định dạng máy trung gian thay vì chỉ diễn giải mã nguồn trực tiếp.