2017-01-26 26 views
3

是什麼我可以做分配的內存有沒有限制什麼(標準明智)我可以做我想做的與分配的內存

例如

#include <stdio.h> 
#include <stdlib.h> 

struct str{ 
    long long a; 
    long b; 
}; 

int main(void) 
{ 
    long *x = calloc(4,sizeof(long)); 
    x[0] = 2; 
    x[3] = 7; 
//is anything beyond here legal(if you would exclude possible illegal operations) 
    long long *y = x; 
    printf("%lld\n",y[0]); 
    y[0] = 2; 
    memset (x,0,16); 
    struct str *bar = x; 
    bar->b = 4; 
    printf("%lld\n",bar->a); 
    return 0; 
} 

總結:

  • 只要尺寸合適,我可以重新改寫指向其他數據類型和結構的指針嗎?
  • 我可以在寫之前閱讀嗎?
  • 如果不是我可以在我寫完後閱讀嗎?
  • 我可以在小於分配內存的結構中使用它嗎?

回答

4

y[0]讀取違反嚴格的別名法則。您可以使用long long類型的左值來讀取long有效類型的對象。

假設你省略該行;接下來的麻煩部分是memset(x,0,16);This answer認爲memset不會更新有效類型。標準不明確。假設memset的有效類型不變;下一個問題是bar->a的閱讀。 C標準也不清楚。有人說bar->a意味着(*bar).a,這是一個嚴格的鋸齒違規,因爲我們沒有先寫一個bar對象到位置。

其他人(包括我)說這樣很好:唯一用於訪問的左值是bar->a;即long long類型的左值,並訪問有效類型long long(由y[0] = 2;寫的那個)的對象。

有一個C2X工作組正致力於改進嚴格別名的規範來澄清這些問題。

+0

[6.5p6](http://port70.net/~nsz/c/c11/n1570.html#6.5p6)意味着'memove'和company修改了有效類型。 – StoryTeller

+0

@StoryTeller memmove和memcpy當然可以,但我不認爲memset屬於同一個保護傘。如果你說memset設置了有效類型(對於char - 還有什麼?),那麼通常使用的成語memset(x,0,n);來初始化一些int將導致UB,所以我認爲這不會做一個實際的解釋 –

+0

因此,在bar-> b上讀或寫會沒問題,因爲它會是同一個別名? –

3

只要大小合適,我可以重新指向其他數據類型的指針嗎?

您可以將重寫爲最多與您分配的內存一樣大的任何數據類型。您必須編寫一個值,但是要根據6.5p6更改所有塗層物體的有效類型

然後我可以讀取嗎?
如果不是我可以在我寫完後閱讀嗎?

號時另有規定除外(calloc是其他方式),在存儲器中的值是不確定的。它可能包含陷阱值。一投,以重新解釋值作爲另一種類型是UB,並且違反嚴格別名(6.5p7

我可以用一個結構比分配的內存較小的使用呢?

是的,但這是一種浪費。


你需要轉換爲void*第一。否則,你會得到編譯器有關不兼容指針類型的正確投訴。
即使有些類型可能陷入完全0位模式,所以它依賴。

+0

好了,第一個答案剩下的就完成了,因爲他們依賴於這個操作。 –

+1

@KamiKaze - 編輯來反映M.M對我生鏽的標準回憶的評論。 – StoryTeller

0

大多數編譯器都提供了一種模式,在這種模式下,指針的讀寫操作將根據執行的順序對底層存儲執行操作,而不管所涉及的數據類型如何。該標準不要求編譯器提供這種模式,但據我所知,所有質量編譯器都這樣做。

根據其公佈的理由,標準的作者增加走樣限制,以避免編譯器的既定目的的語言,使悲觀的別名假定時給出類似的代碼:在本例中

float f; 
float test(int *p) 
{ 
    f=1.0f; 
    *p = 2; 
    return f; 
} 

注意基本原理[非常類似於上述],即使通過指針p修改f使用的存儲是合法的,一個合理的人在查看代碼時也沒有理由認爲它可能會發生這樣的事情。在另一方面,許多編譯器的編寫者認爲,如果給這樣的:

float f; 
float test(float *p) 
{ 
    f=1.0f; 
    *(int*)p = 2; 
    return f; 
} 

一個必須是故意裝傻認爲該代碼將不可能修改由float使用的存儲,並有結果沒有理由說明質量編譯器不應將寫入*(int*)p視爲潛在寫入float

不幸的是,在這段時間裏,編譯器編寫者已經越來越積極地使用基於類型的別名「優化」,有時候這種方式無疑會超出標準允許的範圍。除非程序永遠不需要在不同時間訪問任何不同類型的存儲,否則我建議在支持它的編譯器上使用-fno-strict-aliasing選項。否則,可能會有符合標準並且今天​​有效的代碼,但未來版本的編譯器會失敗,因爲它的「優化」更加激進。 PS-禁用基於類型的別名可能會影響代碼在某些情況下的性能,但正確使用restrict限定的變量和參數應避免悲觀別名假設的代價。稍加小心,使用這些限定符可以實現與積極混疊可以完成的相同的優化,但更安全。

+0

我想我必須做一些閱讀才能完全理解你的答案。感謝您的詳細描述。 –

+0

@KamiKaze:閱讀'restrict';即使不禁用基於類型的別名,它也可以提供性能優勢,但如果這樣做會變得尤爲重要。 – supercat

相關問題