2011-06-27 114 views
5

我很好奇,想知道到底爲什麼C89編譯器將在爲你傾倒,當您嘗試例如混合變量聲明和代碼,就像這樣:C89,混合變量聲明和代碼

[email protected]:~$ cat test.c 
#include <stdio.h> 

int 
main(void) 
{ 
    printf("Hello World!\n"); 
    int x = 7; 
    printf("%d!\n", x); 
    return 0; 
} 
[email protected]:~$ gcc -std=c89 -pedantic test.c 
test.c: In function ‘main’: 
test.c:7: warning: ISO C90 forbids mixed declarations and code 
[email protected]:~$ 

是的,你可以通過遠離剽竊來避免此類事情。但是,你的代碼不再符合標準。任何能夠回答這個帖子的人都可能已經知道,這不僅僅是理論上的問題。像微軟C編譯器這樣的平臺在任何情況下都可以在標準中快速實施。

給定的C如何古代是,我會想象這個功能是因爲一些歷史問題,可以追溯到上世紀70年代的非凡硬件限制,但我不知道的細節。還是我完全錯了?

+0

Heh。這已經有一段時間了,但我記得它與無法在特定平臺上混合聲明/堆棧分配和代碼有關。之後,添加了嵌套塊,即使在這些平臺上(或多或少地將它們作爲內部函數處理),也允許有限的混合形式。雖然我沒有找到參考,而且時間很久以至於我不太相信自己的記憶,這就是爲什麼這個答案沒有被解決。 – geekosaur

+3

爲什麼不問dmr?我給他發了一封電子郵件(他的地址是公開的,在他的網站上)。我將在這裏發佈任何回覆...儘管我希望他會創建一個賬戶並回答他自己:-) –

回答

7

C標準說:「你不可」,因爲它沒有在其中C89標準規範早期的C編譯器允許的。創建一種可用於編寫操作系統及其實用程序的語言足夠激進。這個概念可能根本就沒有考慮到 - 當時沒有其他語言允許它(Pascal,Algol,PL/1,Fortran,COBOL),所以C也不需要。而且它可能會使編譯器稍微難以處理(更大),並且原始編譯器受到PDP 11系列中允許的64 KiB代碼和64 KiB數據空間的空間限制(大型機器;小型機器只允許64 KiB用於代碼和數據,AFAIK)。所以,額外的複雜性並不是一個好主意。

它是允許聲明和變量交錯的C++,但C++不是,也從來沒有C。但是,C99終於趕上了C++(這是一個有用的特性)。可悲的是,微軟從未實施過C99。

+0

我對K&R C感到好奇。在K&R C中,聲明必須從非常開始功能塊或在任何塊的開始處允許聲明。我的意思是'foo(){int i; for(i = 0; i

+0

@Zboson:預標準C允許在大括號'{...}'中的任何複合語句的開始處使用變量定義。初始化有限制,但不是定義本身。該標準在很大程度上遵循了現有的做法。 –

0

編寫語言的編譯器要求在函數的開頭聲明所有變量要容易得多。有些語言甚至需要在函數代碼之外的特定子句中聲明變量(想到Pascal和Smalltalk)。

的原因是它更容易映射這個變量堆棧(或寄存器,如果你的編譯器足夠聰明),如果他們是已知的,不會改變。

任何其它語句(特別是函數調用)可以修改堆棧/寄存器,使得變量映射更加複雜。

+0

Pascal允許在塊的開始處使用變量定義 - 以及任何塊之外的全局變量。 –

2

它類似於需要在使用前要聲明的函數 - 它允許頭腦簡單的編譯器在一次通過操作,從頂部到底部,發射目標代碼,因爲它去。

在這種特殊情況下,編譯器可以順利通過的聲明,加起來所需的堆棧空間。當它到達第一條語句時,它可以輸出代碼來調整堆棧,在函數代碼正確啓動之前立即爲本地分配空間。

3

它可能從來沒有這樣實施,因爲它從來沒有需要。

假設你想要寫在純C是這樣的:

int myfunction(int value) 
    { 
    if (value==0) 
     return 0; 
    int result = value * 2; 
    return result; 
    } 

然後你就可以很容易地在合法的C重寫這個,像這樣:

int myfunction(int value) 
    { 
    int result; 
    if (value==0) 
     return 0; 
    result = value * 2; 
    return result; 
    } 

是絕對的性能沒有影響首先聲明變量,然後設置它的值。

但是,在C++中,情況不再是這樣。 在以下示例中,函數2將比功能1慢:

double function1(const Factory &factory) 
    { 
    if (!factory.isWorking()) 
     return 0; 
    Product product(factory.makeProduct()); 
    return product.getQuantity(); 
    } 

double function2(const Factory &factory) 
    { 
    Product product; 
    if (!factory.isWorking()) 
     return 0; 
    product = factory.makeProduct(); 
    return product.getQuantity(); 
    } 

在功能2的產品變量需要被構造,即使當工廠不工作。 之後,工廠生產產品,然後分配操作員需要複製產品(從makeProduct的返回值到產品變量)。在函數1中,只有在工廠正在工作時才構造產品,即使這樣,也會調用複製構造函數,而不是正常的構造函數和賦值運算符。然而,我期望現在一個好的C++編譯器會優化這段代碼,但在第一個C++編譯器中,這可能不是這種情況。

第二個例子是下列:

double function1(const Factory &factory) 
    { 
    if (!factory.isWorking()) 
     return 0; 
    Product &product = factory.getProduct(); 
    return product.getQuantity(); 
    } 

double function2(const Factory &factory) 
    { 
    Product &product; 
    if (!factory.isWorking()) 
     return 0; 
    product = factory.getProduct(); // Invalid. You can't assign to a reference. 
    return product.getQuantity(); 
    } 

在這個例子中,函數2是簡單地無效。引用只能在聲明時賦值,而不能晚於賦值。 這意味着在這個例子中,編寫有效代碼的唯一方法是在變量真正初始化的時刻寫入聲明。不早。

這兩個示例都說明了爲什麼在C++中真正需要允許在其他可執行語句之後使用變量聲明,而不是像C中那樣在塊的開始處使用變量聲明。這解釋了爲什麼將它添加到C++而不是C(和其他語言),它不是真的需要。

+0

同樣,大多數C89編譯器也會優化代碼,並且只在堆棧中分配變量(如果它們實際在函數中使用)。 – Lundin