2012-12-05 46 views
2

,我有以下結構分配結構值:它是安全的使用指針

struct foo 
{ 
    int a; 
    char b; 
}; 

它存儲到內存和指針,它可以是未對齊(奇地址)。

所以,這是安全的?:

const struct foo a = *((struct foo*)char_ptr); 

我是令人擔憂的,因爲源結構的整數成員可以在奇地址。在一些系統中,從奇數地址讀取(多字節)整數會導致問題。

編輯: 爲了避免偏離主題的評論關於const使用,我也從代碼中刪除const。 (我從來沒有投常量指針到非const指針,即使在這種情況下它不應該引起任何問題)

更多的問題背景: 這種結構是協議框架的一部分。它可以在框架內的任何偏移量。在真正的代碼中,該結構具有__attribute__((packed))屬性。但是這可能不會改變答案?

無論如何,我可以使用memcopy和非const a來解決問題。但我想使用assingment,因爲它似乎是更優雅的方式(如果它是安全的)。

+0

編譯器應該照顧它。如果一個變量可以正常訪問,它可以通過指針訪問。 –

+2

@ User1 - 我會回到你的教科書並閱讀關鍵字'const' –

+0

^^正確地指出...... LHS上的const不允許RHS上的變量。 – anishsane

回答

1

不,這是不安全的,除非你知道指針具有結構所需的對齊。

這裏有一些方法來知道指針有必要的對齊方式:

  • 指針本來是一個指向此結構類型,然後將其轉換爲字符指針。
  • 指針是內存分配例程(例如malloc)的結果,該例程保證返回的地址被適當地分配給任何用途(其中malloc確實保證)。
  • 指針已經從上面的一個計算,保留了所需的對齊方式。
  • 您將指針轉換爲uintptr_t並測試其對齊方式,前提是您的C實現支持該對齊方式。
  • 您的C實現(特別是操作系統和處理器)允許對結構中類型的對象進行非對齊訪問。

如果您不知道指針有必要的對齊方式,那麼您不應該通過轉換的指針訪問結構。相反,您可以定義一個新結構並使用memcpy將char指針複製到新結構中。

+0

好吧,所以編譯器使結構賦值struct foo a ='field by field? (而不是逐字節拷貝) – SKi

+1

@ User1:編譯器可以以不同的方式實現分配。它可能會在某些情況下處理每個字段,或者可能在某些情況下複製整個字段。但是,即使編譯器選擇複製整個結構,而不考慮單個元素,這並不意味着它會調用memcpy並執行逐字節的副本。如果根據語言規則,結構應該是四字節對齊的,則編譯器可以使用四字節加載和存儲指令來執行復制,即使結構包含一些不同大小的元素。 –

+0

聽起來很清楚。您的評論對我來說是詳盡的答案。 – SKi

2

參見Data structure padding。在大多數系統中,結構將對齊&內部成員地址將與其各自的大小對齊,而malloc自己。

如果你手動創建一個內存池&使用內部的某個部分來存儲你的struct對象,那麼編譯器不會保證你對齊,但是如果你使用malloc來創建struct對象,那一般是編譯器的頭痛,給你對齊地址,如果需要在結構元素之間填充。

+0

他的具體問題是關於源結構,意味着可能未對齊的指針char_ptr(我認爲它是一個char *)。解析文件或網絡中的二進制數據時,這是一個常見問題。 – Art

+0

^^因此,免責聲明:「如果您手動創建內存池並使用其中的某個部分來存儲您的結構對象,那麼編譯器不會保證您對齊」 – anishsane

+0

我認爲這不應該是一個免責聲明;我認爲這應該是重點。將一個指向char的指針指向結構指針非常指示訪問已被某些外部需求佈置的緩衝區或其他結構。它可能**是指向字符的指針只是通過內存分配和地址操作創建的,但它具有必要的對齊方式,但提問者應該對此提醒注意。 –

1

如果您從指向數據的指針不一定對齊的位置獲取數據,則唯一可以安全使用的方法是memcpy,並希望編譯器不「智能」,並假裝錯位指針實際上是對齊的,memcpy可以被優化(有幾個舊版本的gcc有這個bug,要求你編寫一個不叫memcpy的自定義memcpy函數)。根據你的體系結構,你可以通過不正確的對齊訪問來獲得,但它幾乎肯定會變慢,有時甚至通過內核陷阱來模擬。

一面軼事:在操作系統中的網絡堆棧中,這通常是一個問題,在這種操作系統中,以太網頭部的奇數大小使得IP頭部未對齊,並且如果硬件不具有未對齊接收到的數據包的能力(某些DMA引擎只能在4字節的邊界上寫入數據),這就要求整個頭文件(或整個數據包)在軟件中被複制一次。