1. 編譯錯誤,怎麼回事
關於編譯問題,如果說是編譯錯誤發生在自己編寫源程序的過程中的話,那麼問題的原因還是比較復雜的。通常關於用戶編寫的源程序的編譯出錯問題,這是一個很復雜的問題。因為編譯錯誤有很多種。例如:語法錯誤、系統庫連接錯誤、語義錯誤、數組越界、或者內存越界等等。
通常語法錯誤是最好解決的,因為源程序的語法出錯了,連編譯這一關都通不過,並且會告訴你在哪一行出錯了,這時候是最容易調試程序的。最難調試的就是:源程序雖然編譯通過了,但是程序的運行結果卻是錯誤的,這種是最難調試的。所以說,你必須要把詳細的出錯信息寫出來,別人們才好幫助你進行分析。
2. 關於整個編譯過程和庫文件的問題
1 ABD, 會載入到生成的可執行文件中
2 ABC, 不會載入到生成的可執行文件中
3 不是. 編譯器會有對應的選項去幫助你生成所需的文件, 比如
gcc a.c -shared -o a.dll -Wl,--output-def,a.def,--out-implib,liba.a
就可以給你生成個.dll
3. keil4每次build target 都是全編譯是怎麼回事啊每次花很長時間啊,謝謝!!!
全編譯,顧名思義,就是把工程里所有的文件都編譯一遍,不管這個文件是否有過改動,所以時間很長,因為這個過程是編譯--鏈接---生成HEX文件,所以,如果你文件很多,代碼很多,時間就很長。
而半編譯是只對你改動過的文件進行重新的編譯,所以,過程是
編譯部分文件--重新鏈接--生成HEX.
如果你不是把Keil的優化等級調到了8級及以上,用半編譯完全沒問題。
4. c語言編譯沒有錯誤,連接有錯誤是怎麼回事
1 編譯階段,執行的是對單個文件的轉茄鉛換,從源代碼轉換成二進制文件。
2 鏈接階段,將每個源文件轉成的二頃納沖進制文件,及相關的系統庫文件共同打包生成可執行文件。
3 從定義上可以看出,鏈接階段,出雀殲現錯誤有兩種可能:
1) 函數中引用了不存在的函數。
2) 編譯環境有問題,導致ld出錯。
5. 編譯原理
C語言編譯過程詳解
C語言的編譯鏈接過程是要把我們編寫的一個C程序(源代碼)轉換成可以在硬體上運行的程序(可執行代碼),需要進行編譯和鏈接。編譯就是把文本形式源代碼翻譯為機器語言形式的目標文件的過程。鏈接是把目標文件、操作系統的啟動代碼和用到的庫文件進行組織形成最終生成可執行代碼的過程。過程圖解如下:
從圖上可以看到,整個代碼的編譯過程分為編譯和鏈接兩個過程,編譯對應圖中的大括弧括起的部分,其餘則為鏈接過程。
一、編譯過程
編譯過程又可以分成兩個階段:編譯和匯編。
1、編譯
編譯是讀取源程序(字元流),對之進行詞法和語法的分析,將高級語言指令轉換為功能等效的匯編代碼,源文件的編譯過程包含兩個主要階段:
第一個階段是預處理階段,在正式的編譯階段之前進行。預處理階段將根據已放置在文件中的預處理指令來修改源文件的內容。如#include指令就是一個預處理指令,它把頭文件的內容添加到.cpp文件中。這個在編譯之前修改源文件的方式提供了很大的靈活性,以適應不同的計算機和操作系統環境的限制。一個環境需要的代碼跟另一個環境所需的代碼可能有所不同,因為可用的硬體或操作系統是不同的。在許多情況下,可以把用於不同環境的代碼放在同一個文件中,再在預處理階段修改代碼,使之適應當前的環境。
主要是以下幾方面的處理:
(1)宏定義指令,如 #define a b。
對於這種偽指令,預編譯所要做的是將程序中的所有a用b替換,但作為字元串常量的 a則不被替換。還有 #undef,則將取消對某個宏的定義,使以後該串的出現不再被替換。
(2)條件編譯指令,如#ifdef,#ifndef,#else,#elif,#endif等。
這些偽指令的引入使得程序員可以通過定義不同的宏來決定編譯程序對哪些代碼進行處理。預編譯程序將根據有關的文件,將那些不必要的代碼過濾掉
(3) 頭文件包含指令,如#include "FileName"或者#include <FileName>等。
在頭文件中一般用偽指令#define定義了大量的宏(最常見的是字元常量),同時包含有各種外部符號的聲明。採用頭文件的目的主要是為了使某些定義可以供多個不同的C源程序使用。因為在需要用到這些定義的C源程序中,只需加上一條#include語句即可,而不必再在此文件中將這些定義重復一遍。預編譯程序將把頭文件中的定義統統都加入到它所產生的輸出文件中,以供編譯程序對之進行處理。包含到C源程序中的頭文件可以是系統提供的,這些頭文件一般被放在/usr/include目錄下。在程序中#include它們要使用尖括弧(<>)。另外開發人員也可以定義自己的頭文件,這些文件一般與C源程序放在同一目錄下,此時在#include中要用雙引號("")。
(4)特殊符號,預編譯程序可以識別一些特殊的符號。
例如在源程序中出現的LINE標識將被解釋為當前行號(十進制數),FILE則被解釋為當前被編譯的C源程序的名稱。預編譯程序對於在源程序中出現的這些串將用合適的值進行替換。
預編譯程序所完成的基本上是對源程序的「替代」工作。經過此種替代,生成一個沒有宏定義、沒有條件編譯指令、沒有特殊符號的輸出文件。這個文件的含義同沒有經過預處理的源文件是相同的,但內容有所不同。下一步,此輸出文件將作為編譯程序的輸出而被翻譯成為機器指令。
第二個階段編譯、優化階段。經過預編譯得到的輸出文件中,只有常量;如數字、字元串、變數的定義,以及C語言的關鍵字,如main,if,else,for,while,{,}, +,-,*,\等等。
編譯程序所要作得工作就是通過詞法分析和語法分析,在確認所有的指令都符合語法規則之後,將其翻譯成等價的中間代碼表示或匯編代碼。
優化處理是編譯系統中一項比較艱深的技術。它涉及到的問題不僅同編譯技術本身有關,而且同機器的硬體環境也有很大的關系。優化一部分是對中間代碼的優化。這種優化不依賴於具體的計算機。另一種優化則主要針對目標代碼的生成而進行的。
對於前一種優化,主要的工作是刪除公共表達式、循環優化(代碼外提、強度削弱、變換循環控制條件、已知量的合並等)、復寫傳播,以及無用賦值的刪除,等等。
後一種類型的優化同機器的硬體結構密切相關,最主要的是考慮是如何充分利用機器的各個硬體寄存器存放的有關變數的值,以減少對於內存的訪問次數。另外,如何根據機器硬體執行指令的特點(如流水線、RISC、CISC、VLIW等)而對指令進行一些調整使目標代碼比較短,執行的效率比較高,也是一個重要的研究課題。
2、匯編
匯編實際上指把匯編語言代碼翻譯成目標機器指令的過程。對於被翻譯系統處理的每一個C語言源程序,都將最終經過這一處理而得到相應的目標文件。目標文件中所存放的也就是與源程序等效的目標的機器語言代碼。目標文件由段組成。通常一個目標文件中至少有兩個段:
代碼段:該段中所包含的主要是程序的指令。該段一般是可讀和可執行的,但一般卻不可寫。
數據段:主要存放程序中要用到的各種全局變數或靜態的數據。一般數據段都是可讀,可寫,可執行的。
UNIX環境下主要有三種類型的目標文件:
(1)可重定位文件
其中包含有適合於其它目標文件鏈接來創建一個可執行的或者共享的目標文件的代碼和數據。
(2)共享的目標文件
這種文件存放了適合於在兩種上下文里鏈接的代碼和數據。
第一種是鏈接程序可把它與其它可重定位文件及共享的目標文件一起處理來創建另一個 目標文件;
第二種是動態鏈接程序將它與另一個可執行文件及其它的共享目標文件結合到一起,創建一個進程映象。
(3)可執行文件
它包含了一個可以被操作系統創建一個進程來執行之的文件。匯編程序生成的實際上是第一種類型的目標文件。對於後兩種還需要其他的一些處理方能得到,這個就是鏈接程序的工作了。
二、鏈接過程
由匯編程序生成的目標文件並不能立即就被執行,其中可能還有許多沒有解決的問題。
例如,某個源文件中的函數可能引用了另一個源文件中定義的某個符號(如變數或者函數調用等);在程序中可能調用了某個庫文件中的函數,等等。所有的這些問題,都需要經鏈接程序的處理方能得以解決。
鏈接程序的主要工作就是將有關的目標文件彼此相連接,也即將在一個文件中引用的符號同該符號在另外一個文件中的定義連接起來,使得所有的這些目標文件成為一個能夠被操作系統裝入執行的統一整體。
根據開發人員指定的同庫函數的鏈接方式的不同,鏈接處理可分為兩種:
(1)靜態鏈接
在這種鏈接方式下,函數的代碼將從其所在地靜態鏈接庫中被拷貝到最終的可執行程序中。這樣該程序在被執行時這些代碼將被裝入到該進程的虛擬地址空間中。靜態鏈接庫實際上是一個目標文件的集合,其中的每個文件含有庫中的一個或者一組相關函數的代碼。
(2) 動態鏈接
在此種方式下,函數的代碼被放到稱作是動態鏈接庫或共享對象的某個目標文件中。鏈接程序此時所作的只是在最終的可執行程序中記錄下共享對象的名字以及其它少量的登記信息。在此可執行文件被執行時,動態鏈接庫的全部內容將被映射到運行時相應進程的虛地址空間。動態鏈接程序將根據可執行程序中記錄的信息找到相應的函數代碼。
對於可執行文件中的函數調用,可分別採用動態鏈接或靜態鏈接的方法。使用動態鏈接能夠使最終的可執行文件比較短小,並且當共享對象被多個進程使用時能節約一些內存,因為在內存中只需要保存一份此共享對象的代碼。但並不是使用動態鏈接就一定比使用靜態鏈接要優越。在某些情況下動態鏈接可能帶來一些性能上損害。
我們在linux使用的gcc編譯器便是把以上的幾個過程進行捆綁,使用戶只使用一次命令就把編譯工作完成,這的確方便了編譯工作,但對於初學者了解編譯過程就很不利了,下圖便是gcc代理的編譯過程:
從上圖可以看到:
預編譯
將.c 文件轉化成 .i文件
使用的gcc命令是:gcc –E
對應於預處理命令cpp
編譯
將.c/.h文件轉換成.s文件
使用的gcc命令是:gcc –S
對應於編譯命令 cc –S
匯編
將.s 文件轉化成 .o文件
使用的gcc 命令是:gcc –c
對應於匯編命令是 as
鏈接
將.o文件轉化成可執行程序
使用的gcc 命令是: gcc
對應於鏈接命令是 ld
總結起來編譯過程就上面的四個過程:預編譯、編譯、匯編、鏈接。了解這四個過程中所做的工作,對我們理解頭文件、庫等的工作過程是有幫助的,而且清楚的了解編譯鏈接過程還對我們在編程時定位錯誤,以及編程時盡量調動編譯器的檢測錯誤會有很大的幫助的。
是否可以解決您的問題?
6. 編譯型語言和解釋型語言各自的優缺點是什麼
一、編譯型語言
優點:運行速度快,代碼效率高,編譯後的程序不可修改,保密性較好。
缺點:代碼需要經過編譯方可運行,可移植性差,只能在兼容的操作系統上運行 。
二、解釋型語言
優點:可移植性較好,只要有解釋環境,可在不同的操作系統上運行。
缺點:運行需要解釋環境,運行起來比編譯的要慢,佔用資源也要多一些,代碼效率低,代碼修改後就可運行,不需要編譯過程。
(6)編譯怎麼回事論述擴展閱讀:
編譯型語言:程序在執行之前需要一個專門的編譯過程,把程序編譯成 為機器語言的文件,運行時不需要重新翻譯,直接使用編譯的結果就行了。程序執行效率高,依賴編譯器,跨平台性差些。如C、C++、Delphi等。而相對的,解釋性語言編寫的程序不進行預先編譯,以文本方式存儲程序代碼。在發布程序時,看起來省了道編譯工序。但是在運行程序的時候,解釋性語言必須先解釋再運行。
7. 請教無法編譯是怎麼回事
「無法編譯」的原因可能很多種。能詳述一下你的問題嗎?
在有更多的信息之前,大概列一下「無法編譯」的可能情況:
找不到「編譯器」。這種通常是在「命令行」或者「shell」情況下出現。在「集成環境(IDE)下通常較少出現。
解決方法:(1)安裝必要的編譯器。(2)如果已有編譯器,把編譯器的可執行文件的路徑,加入環境變數「PATH」。(3)加入必要的其它設置,如「鏈接庫」的位置。
源程序有錯誤。
解決辦法:根據出錯信息,排除源程序的錯誤。
缺乏必要的「鏈接庫」。
解決方法:找到需要的鏈接庫,並適當安裝。
最好能針對你實際的問題,對症下葯。有問題可以繼續交流,謝謝。
8. VHDL 編譯,闡述和 綜合 之間的關系
編譯、精細化、綜合是數字系統設計中的概念,而VHDL只是數字系統設計中的一種硬體描述語言。
所謂編譯(Compile)是借用軟體系統中的概念,在數字系統設計中,是指包含多個環節的一個完整過程。這些環節主要有:分析(Analysis)、精細化(Elabortion)、綜合(Synthesis)、適配(Fitter)、匯編(Assembler)等。分析——檢查語法;精細化——建立資料庫、為綜合進行初始化;綜合——將高層次描述轉化為低層次描述並優化代碼、適配——布局和布線、匯編——產生配置數據。
9. c++編譯不了是怎麼回事,也沒顯示錯誤
因為褲稿變數未初始化握猛。是C++編程中最為常見和易犯的錯誤之一。在C++中,為變數所分配的內存空間並不是完全「干凈的」,也不會在分配空間時自動做清零處理。c++編譯不了也沒顯示錯誤是因為變數未初始化。C++是一種計算機高級程序設計語言,由C語言擴展升級而產生,最早於1979年由本賈尼·斯段純橋特勞斯特盧普在AT&T貝爾工作室研發。
10. 誰能簡單闡述下java編譯執行的過程
Java虛擬機(JVM)是可運行Java代碼的假想計算機。
只要根據JVM規格描述將解釋器移植到特定的計算機上,就能保證經過編譯的任何Java代碼能夠在該系統上運行。
本文首先簡要介紹從Java文件的編譯到最終執行的過程,隨後對JVM規格描述作一說明。
一.Java源文件的編譯、下載、解釋和執行
Java應用程序的開發周期包括編譯、下載、解釋和執行幾個部分。
Java編譯程序將Java源程序翻譯為JVM可執行代碼?位元組碼。
這一編譯過程同C/C++的編譯有些不同。
當C編譯器編譯生成一個對象的代碼時,該代碼是為在某一特定硬體平台運行而產生的。
因此,在編譯過程中,編譯程序通過查表將所有對符號的引用轉換為特定的內存偏移量,以保證程序運行。
Java編譯器卻不將對變數和方法的引用編譯為數值引用,也不確定程序執行過程中的內存布局,而是將這些符號引用信息保留在位元組碼中,由解釋器在運行過程中創立內存布局,然後再通過查表來確定一個方法所在的地址。
這樣就有效的保證了Java的可移植性和安全性。
運行JVM位元組碼的工作是由解釋器來完成的。
解釋執行過程分三部進行:代碼的裝入、代碼的校驗和代碼的執行。
裝入代碼的工作由"類裝載器"(classloader)完成。
類裝載器負責裝入運行一個程序需要的所有代碼,這也包括程序代碼中的類所繼承的類和被其調用的類。
當類裝載器裝入一個類時,該類被放在自己的名字空間中。
除了通過符號引用自己名字空間以外的類,類之間沒有其他辦法可以影響其他類。
在本台計算機上的所有類都在同一地址空間內,而所有從外部引進的類,都有一個自己獨立的名字空間。
這使得本地類通過共享相同的名字空間獲得較高的運行效率,同時又保證它們與從外部引進的類不會相互影響。
當裝入了運行程序需要的所有類後,解釋器便可確定整個可執行程序的內存布局。
解釋器為符號引用同特定的地址空間建立對應關系及查詢表。
通過在這一階段確定代碼的內存布局,Java很好地解決了由超類改變而使子類崩潰的問題,同時也防止了代碼對地址的非法訪問。
隨後,被裝入的代碼由位元組碼校驗器進行檢查。
校驗器可發現操作數棧溢出,非法數據類型轉化等多種錯誤。
通過校驗後,代碼便開始執行了。
Java位元組碼的執行有兩種方式:
1.即時編譯方式:解釋器先將位元組碼編譯成機器碼,然後再執行該機器碼。
2.解釋執行方式:解釋器通過每次解釋並執行一小段代碼來完成Java位元組碼程序的所有操作。
通常採用的是第二種方法。
由於JVM規格描述具有足夠的靈活性,這使得將位元組碼翻譯為機器代碼的工作
具有較高的效率。
對於那些對運行速度要求較高的應用程序,解釋器可將Java位元組碼即時編譯為機器碼,從而很好地保證了Java代碼的可移植性和高性能。
二.JVM規格描述
JVM的設計目標是提供一個基於抽象規格描述的計算機模型,為解釋程序開發人員提很好的靈活性,同時也確保Java代碼可在符合該規范的任何系統上運行。
JVM對其實現的某些方面給出了具體的定義,特別是對Java可執行代碼,即位元組碼(Bytecode)的格式給出了明確的規格。
這一規格包括操作碼和操作數的語法和數值、標識符的數值表示方式、以及Java類文件中的Java對象、常量緩沖池在JVM的存儲映象。
這些定義為JVM解釋器開發人員提供了所需的信息和開發環境。
Java的設計者希望給開發人員以隨心所欲使用Java的自由。
JVM定義了控制Java代碼解釋執行和具體實現的五種規格,它們是:
JVM指令系統
JVM寄存器
JVM棧結構
JVM碎片回收堆
JVM存儲區
2.1JVM指令系統
JVM指令系統同其他計算機的指令系統極其相似。
Java指令也是由操作碼和操作數兩部分組成。
操作碼為8位二進制數,操作數進緊隨在操作碼的後面,其長度根據需要而不同。
操作碼用於指定一條指令操作的性質(在這里我們採用匯編符號的形式進行說明),如iload表示從存儲器中裝入一個整數,anewarray表示為一個新數組分配空間,iand表示兩個整數的"與",ret用於流程式控制制,表示從對某一方法的調用中返回。
當長度大於8位時,操作數被分為兩個以上位元組存放。
JVM採用了"bigendian"的編碼方式來處理這種情況,即高位bits存放在低位元組中。
這同Motorola及其他的RISCCPU採用的編碼方式是一致的,而與Intel採用的"littleendian"的編碼方式即低位bits存放在低位位元組的方法不同。
Java指令系統是以Java語言的實現為目的設計的,其中包含了用於調用方法和監視多先程系統的指令。
Java的8位操作碼的長度使得JVM最多有256種指令,目前已使用了160多種操作碼。
2.2JVM指令系統
所有的CPU均包含用於保存系統狀態和處理器所需信息的寄存器組。
如果虛擬機定義較多的寄存器,便可以從中得到更多的信息而不必對棧或內存進行訪問,這有利於提高運行速度。
然而,如果虛擬機中的寄存器比實際CPU的寄存器多,在實現虛擬機時就會佔用處理器大量的時間來用常規存儲器模擬寄存器,這反而會降低虛擬機的效率。
針對這種情況,JVM只設置了4個最為常用的寄存器。
它們是:
pc程序計數器
optop操作數棧頂指針
frame當前執行環境指針
vars指向當前執行環境中第一個局部變數的指針
所有寄存器均為32位。
pc用於記錄程序的執行。
optop,frame和vars用於記錄指向Java棧區的指針。
2.3JVM棧結構
作為基於棧結構的計算機,Java棧是JVM存儲信息的主要方法。
當JVM得到一個Java位元組碼應用程序後,便為該代碼中一個類的每一個方法創建一個棧框架,以保存該方法的狀態信息。
每個棧框架包括以下三類信息:
局部變數
執行環境
操作數棧
局部變數用於存儲一個類的方法中所用到的局部變數。
vars寄存器指向該變數表中的第一個局部變數。
執行環境用於保存解釋器對Java位元組碼進行解釋過程中所需的信息。
它們是:上次調用的方法、局部變數指針和操作數棧的棧頂和棧底指針。
執行環境是一個執行一個方法的控制中心。
例如:如果解釋器要執行iadd(整數加法),首先要從frame寄存器中找到當前執行環境,而後便從執行環境中找到操作數棧,從棧頂彈出兩個整數進行加法運算,最後將結果壓入棧頂。
操作數棧用於存儲運算所需操作數及運算的結果。
2.4JVM碎片回收堆
Java類的實例所需的存儲空間是在堆上分配的。
解釋器具體承擔為類實例分配空間的工作。
解釋器在為一個實例分配完存儲空間後,便開始記錄對該實例所佔用的內存區域的使用。
一旦對象使用完畢,便將其回收到堆中。
在Java語言中,除了new語句外沒有其他方法為一對象申請和釋放內存。
對內存進行釋放和回收的工作是由Java運行系統承擔的。
這允許Java運行系統的設計者自己決定碎片回收的方法。
在SUN公司開發的Java解釋器和HotJava環境中,碎片回收用後台線程的方式來執行。
這不但為運行系統提供了良好的性能,而且使程序設計人員擺脫了自己控制內存使用的風險。
2.5JVM存儲區
JVM有兩類存儲區:常量緩沖池和方法區。
常量緩沖池用於存儲類名稱、方法和欄位名稱以及串常量。
方法區則用於存儲Java方法的位元組碼。
對於這兩種存儲區域具體實現方式在JVM規格中沒有明確規定。
這使得Java應用程序的存儲布局必須在運行過程中確定,依賴於具體平台的實現方式。
JVM是為Java位元組碼定義的一種獨立於具體平台的規格描述,是Java平 *** 立性的基礎。
目前的JVM還存在一些限制和不足,有待於進一步的完善,但無論如何,JVM的思想是成功的。
對比分析:如果把Java原程序想像成我們的C++原程序,Java原程序編譯後生成的位元組碼就相當於C++原程序編譯後的80x86的機器碼(二進製程序文件),JVM虛擬機相當於80x86計算機系統,Java解釋器相當於80x86CPU。
在80x86CPU上運行的是機器碼,在Java解釋器上運行的是Java位元組碼。
Java解釋器相當於運行Java位元組碼的「CPU」,但該「CPU」不是通過硬體實現的,而是用軟體實現的。
Java解釋器實際上就是特定的平台下的一個應用程序。
只要實現了特定平台下的解釋器程序,Java位元組碼就能通過解釋器程序在該平台下運行,這是Java跨平台的根本。
當前,並不是在所有的平台下都有相應Java解釋器程序,這也是Java並不能在所有的平台下都能運行的原因,它只能在已實現了Java解釋器程序的平台下運行。