1. 程式人生 > >__attribute__((packed)) 指標傳遞,賦值錯誤問題

__attribute__((packed)) 指標傳遞,賦值錯誤問題

啊,網路真強大,果然讓我找到答案了!

 在ARM core的平臺(StrongARM, 2410, XScale)上,如果企圖通過一個獨立的指標對某個數
據結構的內部成員域進行定位和訪問時,會遇到下面的問題(注,在IA-32 X86平臺不會出現
這樣的問題)。

定義一個數據結構:
struct __attribute__((packed)) test {
char c; // 1個位元組
short s; // 2個位元組
long l; // 4個位元組
} st;

packed屬性,對應於gcc編譯選項 -fpack-struct;

test的記憶體佈局在該屬性的制約下是緊湊的,也就意味著第2、3個成員s、l的起始地址將是
奇地址。

如果另行定義一個獨立指標: unsigned long t;
通過“t+偏移位元組數”的方式來定位(訪問)結構內成員域,則會在強制型別轉換t指標來賦值
或者讀取相應成員域時,出現下面的問題:
(假定該結構的起始地址為A0, 則c、s、l的地址各為A0、A1、A3)

1. 編譯器版本(arm version):GCC 2.95.2 / 3.2 / 3.3.2
t = (unsigned long) &st;
*((short*)(t+1)) = 0x0201;
期望: 0x01賦於A1位元組, 0x02賦於A2位元組
實際: 0x01賦於A0位元組, 0x02賦於A1位元組,與期望不吻合

*((long*)(t+3)) = 0x08070605;
期望: 0x05 - 0x08 逐次賦於 A3 - A6 位元組
實際: 0x05 - 0x08 逐次賦於 A0 - A3 位元組,與期望不吻合

結論: 基於這些GCC版本,如果對奇地址進行強制型別轉換,企圖獲得"字、雙字"的資料型別,
則強制轉換後所獲得的資料型別,其地址將被自動"優化"為最近的可被相應對齊模數
(字型資料的對齊模數是2,雙字的是4)整除的低位偶地址!

2. 編譯器版本(arm version):GCC 2.95.3 / 3.0
t = (unsigned long) &st;
*((short*)(t+1)) = 0x0201;
期望: 0x01賦於A1位元組, 0x02賦於A2位元組
實際: 0x01賦於A1位元組, 0x02賦於A2位元組,與期望吻合

*((long*)(t+3)) = 0x08070605;
期望: 0x05 - 0x08 逐次賦於 A3 - A6 位元組
實際: 0x05 - 0x08 逐次賦於 A0 - A3 位元組,與期望不吻合

結論: 基於這些GCC版本,如果對奇地址進行強制型別轉換,企圖獲得"雙字"的資料型別,
則強制轉換後所獲得的資料型別,其地址將被自動"優化"為最近的可被相應對齊模數
4整除的低位偶地址!但對字型資料的此類操作,卻不會引起這種指標優化。


附,有資料稱:
某些架構上訪問資料時有對齊的要求,比如只能從4位元組邊界上讀取一個4位元組的資料型別。
IA-32架構沒有硬性要求對齊,儘管未對齊的訪問降低執行效率。另外一些架構,比如MIPS、
SPARC、m68k、ARM(<V3),要求對齊訪問,否則向當前程序分發SIGBUS訊號,主要是相對早
期的RISC CPU存在此問題。

那麼有沒有一個辦法(比如gcc的編譯選項,或者一個__attribute__),能制約上述的這種指標優化,
使其不自動發生!從而使實際操作與期望操作相吻合?

進一步地問,為何不同版本的arm-linux-gcc所編譯出的執行碼的在這個問題上的表現不一樣!?
這後面有更深層的理論背景嗎?

================
如果endian是確定的,我喜歡這樣的形式:((unsigned char*)&s)[0] * 256 + ((unsigned char*)&s)[1] 
=============================
en,這樣把操作分解成基於位元組的處理確實是一個變通的辦法,和本人的考慮是一致的;在找不出更好解決方法的情況下,我認為可以有下面3種辦法來回避這樣的問題:
1. 改變資料來源的結構佈局,放棄緊湊佈局;
代價:增加並浪費了 padding 位元組,且既有應用需要重寫,以配合結構體記憶體佈局的變化;
2. 改變對結構成員的訪問方式,放棄通過獨立指標來定位/讀寫成員域,而直接採用成員名的方式訪問;
代價:既有應用程式的改動範圍較大;
3. 維持目前的資料來源和資料訪問方式,而新增一個過渡層,表現形式可以是一組巨集,或者行內函數,或
者函式,對結構體內的一切字或雙字型資料的訪問,通過這個過渡邏輯被分拆為以位元組為基礎的操作,
從而回避指標強制型別轉換時發生優化;這也同樣是nxin的思路!
代價:既有應用程式在訪問結構體成員時的程式碼形式發生變化,但個人認為這種方法對系統整體程式碼的
影響是最小的。

可是變通歸變通,歡迎大家繼續對可能根本解決此問題的方法進行討論!謝謝!

相關推薦

__attribute__((packed)) 指標傳遞錯誤問題

啊,網路真強大,果然讓我找到答案了!  在ARM core的平臺(StrongARM, 2410, XScale)上,如果企圖通過一個獨立的指標對某個數 據結構的內部成員域進行定位和訪問時,會遇到下面的問題(注,在IA-32 X86平臺不會出現 這樣的問題)。 定義一個數據

傳遞指標傳遞引用傳遞

    c++中傳遞引數的方式有三種:傳引數的值(稱為值傳遞,簡稱傳值),傳引數的地址(稱為地址傳遞,簡稱為傳址),和引用傳遞(簡稱為傳引用),相應的函式也就是傳值呼叫,傳址呼叫和傳引用呼叫     函式定義時引數表中的引數稱為形式引數,簡

指標指標指標

bug中最可怕的是野指標,那麼問題來了,野指標是什麼?是如何產生的?為什麼程式設計師會“忘記”? 昨天除錯時,我發現了一個野指標,由此我明白了。 a的值成了亂碼,而且gcc編譯器並沒有報錯,那麼請

c++裡面的傳遞指標傳遞地址傳遞詳解

c++裡面的值傳遞: //將i和j的值傳給a和b,實參傳給形參,但由於形參不會回傳給實參,故輸出i仍為38,j仍為45 void main(){void swap(int, int);int i =

C++中函式引數傳遞傳遞指標傳遞引用傳遞

今天想寫一個函式,從函式中把我需要的兩個值傳出來,由於傳出來的值比較多,所以不考慮用return來返回,需要通過引數把修改後的值拉出來供我使用,很當然的就想到了用指標,但是值就是傳不出來;使我對原有的大腦中指標的思維產生混沌感,今天一上午才把函式傳遞又走了

JS陣列操作指向同一指標

錯誤的實現: var array1 = new Array("1","2","3"); var array2; array2 = array1; array1.length = 0; alert(array2); //返回為空 這種做法是錯的,因為javascrip

C++11六大函數(構造函數移動構造函數移動操作符復制構造函數操作符析構函數)

his 類對象 多個 post highlight water iss 重獲新生 amp 在C++中,有三大函數復制控制(復制構造函數,賦值操作符,析構函數),而在C++11中,加入了移動構造函數,移動賦值操作符。我就鬥膽將他們命名為六大函數好了。 一、構造函數 c++

C++中的構造函數拷貝構造函數函數

cpp 區域 操作 兩個 16px size 取值 基於 lan C++中一般創建對象,拷貝或賦值的方式有構造函數,拷貝構造函數,賦值函數這三種方法。下面就詳細比較下三者之間的區別以及它們的具體實現 1.構造函數 構造函數是一種特殊的類成員函數,是當創建一個類的對象時,它被

引用數據類型的傳遞那個先輸出後面的也同樣是同一個

string 結果 變量 rgs student 數據類型 ati print 傳遞 public class TestReferenceType { public static void main(String[] args) { Student student = n

5.4.3 條件定義

5.4.3 條件,賦值,定義 作為元迴圈的直譯器,通過選擇表示式的解釋的片段來處理特殊的形式。對於一個條件的 表示式,我們必須解釋判斷式和決定,基於判斷式的值,解釋真值的語句或者是假值時的 語句。 在解釋判斷式之前,我們儲存條件表示式本身,為了我們能在稍後的時候,抽取到 真值時的語句或者是假值時

Sql sever 宣告變數變數

語句: --宣告變數DECLARE @idcard nvarchar (50) , @rowid nvarchar (50) --給變數賦值SELECT @idcard = '{0}', @rowid = '{1}' SELECT COUNT (1) FRO

《21天學通C#》變數使用前需要宣告和後可以重新新的

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks; namespace 變數宣告賦值使用{ class Program { static

Java-註解-屬性為value時的三種情況

1.自定義註解只有一個屬性時,且屬性名為value時,賦值時value可省略。 註解定義: public @interface Table { public String value(); } 註解使用: @Table("student") public class Stud

[轉]jquery設定select選中等操作

一、基礎取值問題 例如<select class="selector"></select> 1、設定value為pxx的項選中      $(".selector").val("pxx"); 2、設定text為pxx的項選中  

建構函式拷貝建構函式解構函式函式

例一: class CGoods {     //行為,成員方法 public:     CGoods(char *name = NULL, int amount = 0, double price = 0.0) //建構函式 &nbs

算術運算運算if語句whilecontinue語句

算術運算  print(10 / 3)=3.333333333333  print(10 // 3)=3  print(10 ** 2)=100 賦值運算增量賦值  age=18  age+=1  print(age)交叉賦值  x=10  y=20  x,y=y,x  print(x,y)鏈式賦值  x=

[C]關於函式指標引數的

問題 在有一次嘗試用stat()函式獲取檔案屬性的時候,發現如果直接宣告一個指標,然後把這個指標作為引數傳給函式,會導致函式執行失敗,原始碼: #include <sys/stat.h> #include <unistd.h> #include <stdio.h>

golang map 宣告

// 先宣告mapvar m1 map[string]string// 再使用make函式建立一個非nil的map,nil map不能賦值m1 = make(map[string]string)// 最後給已宣告的map賦值m1["a"] = "aa"m1["b"] = "bb" // 直接建立

2.構造析構運算--條款09-12

條款09:絕不在構造和析構過程中呼叫virtual函式 為什麼? 作者用了一段簡單的買賣訂單程式碼來輔助解釋: //交易的base class class Transaction { public: Transaction(); virtual void logTransaction()

線段樹維護區間(平方和立方和)修改區間(加乘)

題目地址 /* * @Author: hannibal * @Date: 2018-08-07 10:42:26 * @Last Modified by: hannibal * @Last Modified time: 2018-08-07 17:08: