2014-04-19 70 views
1

因此,訪問類型上的Ada編譯器,原來是2005年的Ada玩的時候,我嘗試以下經典的例子:2005阿達,訪問類型和局部變量逃逸分析

type Node is record 
    next: access Node; 
end record; 

function reverselist(input: access Node) return access Node is 
    result: access Node := null; 
    this: access Node := input; 
    next: access Node := null; 
begin 
    while this /= null loop 
    next := this.next; 
    this.next := result; -- [*] 
    result := this; 
    this := next; 
    end loop; 
    return result; -- [*] 
end; 

兩個主演線產生編譯錯誤,因爲Ada 2005的分析認爲我可能泄漏了一個本地指針。 (我知道Ada 2012有更好的分析,並且會接受這個,但是我沒有Ada 2012編譯器)

好的,所以這可以通過使用命名池訪問而不是匿名access Node ;通過使用池訪問,我告訴編譯器指針不能指向堆棧對象。這工作正常。

然而,然後我嘗試使用一個名爲一般訪問:

type Node; 
type List is access Node; 
type Node is record 
    next: List; 
end record; 

function reverselist(input: List) return access Node is 
    result: List := null; 
    this: List := input; 
    next: List := null; 
begin 
    while this /= null loop 
     next := this.next; 
     this.next := result; 
     result := this; 
     this := next; 
    end loop; 
    return result; 
end; 

常規訪問類型可以在堆棧點對象以及堆對象;那麼爲什麼編譯器會拒絕第二個例子,因爲它拒絕了第一個例子呢?

(我還略顯驚訝的是,匿名access integer變量似乎與一般的訪問語義就結了。我爲什麼不必須使用access all integer匿名訪問一般?)

+0

只是想知道:什麼是Ada編譯器?獲取Ada 2012編譯器非常簡單,只需從[AdaCore Libre](https://libre2.adacore.com/download)網站下載GNAT GPL 2013。 –

+1

我實際上在http://ideone.com上使用在線代碼片段執行環境,所以我無法控制它使用的編譯器。我知道自己做一件很容易的事,但現在,爲了我在做的事,在線的更容易。 –

回答

3

雖然一般通用接入類型可以指向非堆對象(全局和堆棧),但有一些規則會阻止List指向堆棧。在這種情況下,由於List沒有任何子程序中聲明,它不能進行指向一個堆棧變量:

function reverselist(input: List) return access Node is 
    result: List := null; 
    this: List := input; 
    next: List := null; 
    Dummy : aliased Node; 
begin 
    result := Dummy'Access; -- a compile-time error would occur at this point 
    while this /= null loop 
     next := this.next; 
     this.next := result; 
     result := this; 
     this := next; 
    end loop; 
    return result; 
end; 

由於規則「確保」一個List永遠不能指向一個堆棧變量,沒有編譯器需要拒絕任何其他語句。 (我把「確保」加上引號,因爲你可以使用'Unchecked_Access讓他們周圍,以及Unchecked_Conversion或一些其他的方式來解決該類型的系統。)

如果接入類型是子程序內聲明,它可以指向該子程序中的堆棧變量。這是因爲沒有該訪問類型的對象可以被聲明爲以外的子程序,因此在子程序完成後,這種類型的任何對象都不能生存(禁止未經檢查的技巧)。它也可以指向全局變量或堆對象。但它不能指向嵌套子程序中的堆棧變量。

procedure Proc is 
    type Node is ... 
    type Acc is access all Node; 
    N1 : aliased Node; 

    procedure Nested_Proc is 
     N2 : aliased Node; 
     X : Acc; 
    begin 
     X := N1'Access; -- legal 
     X := N2'Access; -- illegal. N2 is too deep for this access type. 
    end; 

所以它在哪裏'Access使用編譯器可以確保到不再視爲現有變量懸空引用無法創建點。

所有這些都受可訪問性規則的約束,這些規則在RM 3.10.2中有詳細說明。 (我在開玩笑3.10.2是很難理解的,而且往往產生在那些誰嘗試讀它偏頭痛和精神錯亂邊緣。)

編輯:爲了您的評論跟進:基本上,我不認爲編譯器正在做任何「強大的分析」。大部分規則實際上比我發出聲音簡單。在Ada 2005中,大多數訪問類型(包括大多數匿名訪問類型)都有一個「級別」,這取決於聲明類型的位置。如果一個訪問對象的類型是L級,那麼它就不能接受一個訪問級別爲「更深」(嵌套更多)的訪問值。每個匿名訪問都會聲明一個新類型,它的級別通常取決於它聲明的位置。在使用匿名訪問類型的示例中,next記錄字段的類型是庫級別,因爲它位於任何過程之外; result的類型比它更深,因爲它在一個過程中。 this.next := result是非法的,因爲result,更深,可能指向一個堆棧變量,並且this.next具有較淺的水平,因此這是不允許的。對於指定訪問類型的情況,this.next := result是合法的,因爲resultnext字段一樣淺,而result不允許指向堆棧變量。在Ada 2012中,我認爲它是不同的,因爲匿名訪問對象必須跟蹤在運行時他們指向的東西的級別。因此this.next := result是合法的,但是(我認爲)在result指向堆棧變量時會在運行時引發異常。在Ada 2012中,這些訪問對象將需要一些額外的數據來允許他們進行檢查;在Ada 2005中,他們都可能只是簡單的指針。 (我對Ada 2012的一些變化可能是錯誤的。)但希望這不是太複雜。 3.10.2中的複雜性與匿名訪問參數,匿名訪問判別式以及其他一些與您的示例沒有任何關係的特殊情況有關。

+0

啊 - 因爲兩個等價的命名訪問類型是不同的和不兼容的,所以編譯器能夠比匿名訪問類型做更強大的分析,這是無法區分的? –

+0

(順便說一下,在你的代碼片段中輸入錯誤 - 你正在分配給'List',這是一個類型名稱.Ada RM不擔心我:我曾經試過閱讀C++規範!他們告訴我腦損傷將有一天可以修復...) –

+0

@DavidGiven謝謝你的錯誤,修正。 – ajb

1

一個解決方案是稍微重構代碼;使用擴展收益可能會爲你做到這一點:

type Node is record 
    next: access Node; 
end record; 

function reverse_list(input: access Node) return access Node is 
    this: access Node := input; 
    next: access Node := null; 
begin 
    Return result: access Node := null do 
    while this /= null loop 
     next := this.next; 
     this.next := result; 
     result := this; 
     this := next; 
    end loop; 
    end return; 
end reverse_list; 

在輔助功能檢查可以得到令人沮喪的,我有他們的總體好感:我們做希望懸擺指針。