2011-06-18 25 views
5

在C中以可移植的方式可以自修改代碼嗎?C標準允許自修改代碼嗎?

我問的原因是,在某種程度上,OOP依靠自修改代碼(因爲在運行時執行的代碼實際上是作爲數據生成的,例如在一個v-表中),然而,它看起來,如果這太過分了,它會阻止編譯器中的大多數優化。

例如:

void add(char *restrict p, char *restrict pAddend, int len) 
{ 
    for (int i = 0; i < len; i++) 
     p[i] += *pAddend; 
} 

優化編譯器可以扯起*pAddend圈外的,因爲它不會與p干擾。但是,這不再是自修改代碼中的有效優化。

通過這種方式,似乎C沒有允許自修改代碼,但在同一時間,那不是意味着你不能用C做一些事情就像OOP? C真的支持自修改代碼嗎?

+2

C有函數指針,這是所有你需要建立一個運行時的調度機制。你不需要「自我修改代碼」。 – Nemo

+0

我不會說(說)C++中的OOP實現使用自修改代碼,在我看來,它只是數據驅動的代碼。虛擬功能在概念上與簡單的'switch'完全不同。 – Vlad

+0

@Nemo:那還不是一種自修改代碼的形式嗎? – Mehrdad

回答

7

自修改代碼是不可能用C的原因很多,其中最重要的是:

  1. 由編譯器生成的代碼是完全由編譯器,可能不會看起來像什麼什麼程序員試圖編寫修改自己期望的代碼。這是一個基本的根本就是做SMC的問題,而不僅僅是一個可移植性問題。
  2. 功能和數據指針是完全獨立的用C;該語言無法在它們之間來回轉換。這個問題並不重要,因爲某些實現或更高級別的標準(POSIX)確保代碼和數據指針共享表示。

除此之外,自修改代碼只是一個非常糟糕的主意。 20年前它可能有一些用途,但是現在它只會導致bug,糟糕的性能和可移植性故障。請注意,在某些ISA上,指令高速緩存是否看到對高速緩存代碼所做的更改可能是未指定/不可預知的!

最後,虛函數表有沒有關係自修改代碼。這純粹是修改函數指針,它們是數據,而不是代碼。

+1

+1你的最後一句話是關鍵。出於某種原因,我認爲像'jmp EAX'這樣的間接指令會在EAX變化時自行修改......思維上的錯誤。感謝你的回答。 – Mehrdad

+1

這是不正確的。研究POSIX和WinAPI上的頁面保護機制。沒有任何東西可以阻止你在運行時生成機器代碼(除了iOS內核和類似的代碼簽名外),將頁面保護標誌設置爲EXEC並使用C風格的函數指針將控制權交給它。 –

3

嚴格地說,自修改代碼不能在便攜式方式用C來實現,或者C++,如果我理解正確的標準。在C/C++

自修改代碼就意味着是這樣的:

uint8_t code_buffer[FUNCTION_SIZE]; 
void call_function(void) 
{ 
    ... modify code_buffer here to the machine code we'd like to run. 
    ((void (*)(void))code_buffer)(); 
} 

這是不合法的,並會崩潰上最先進的架構。這是不可能在哈佛架構上實現的,因爲可執行代碼是嚴格只讀的,所以它不能成爲任何標準的一部分。

大多數現代的操作系統確實有一個設施,能夠做到這一點兩輪牛車,用於動態重編譯的一個。例如,在Unix中使用mprotect()。

+1

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *正如Mehrdad所觀察到的,如果您不知道代碼是如何生成/優化的,那麼修改編譯器生成的代碼是非常困難的,因爲機器指令不一定與AST有任何特別明顯的關係。 –

+0

儘管無法寫入「自我修改C」,但C編譯器肯定有可能發出「自我修改機器代碼」。我認爲這個問題考慮到了這兩個問題,OP是否都記住我不知道,是否有任何C編譯器這樣做過,我也不知道。 – hippietrail