国产毛片a精品毛-国产毛片黄片-国产毛片久久国产-国产毛片久久精品-青娱乐极品在线-青娱乐精品

C語言中可移植且可靠的指針運算

發(fā)布時間:2015-7-7 10:32    發(fā)布者:designapp
關(guān)鍵詞: C語言 , 指針運算 , 編譯器
C語言中,指針變量是強大且實用的功能。指針變量使程序員不僅可以間接引用數(shù)據(jù)和函數(shù),還可以結(jié)合數(shù)組下標來選擇、讀取和寫入數(shù)組項。但首先需要了解什么是指針和地址以及編譯器如何使用它們。不理解指針和地址會很快導致代碼故障。利用指針,我們可以編寫出許多語法正確的C語言代碼,來編譯和實現(xiàn)某種功能,但這種功能在不同的C編譯器實現(xiàn)中以及不同的目標器件上可能有所不同。甚至可能與我們的期望不同。

指針不是整數(shù)

指針變量包含 C 語言數(shù)據(jù)的地址。例如,查看以下幾行代碼。

int a, *p;
/* 為指針賦予某個目標的地址 */
p = &a;
/* 解除引用指針以間接訪問目標 */
*p = 0;

上面的代碼將變量a 的值設(shè)置為0。應用到a 的&運算符返回一個表示該變量位置的值(地址)。如果將該值復制到一個指針變量,然后對指針解除引用(使用*運算符),則該表達式表示原始變量a。這很容易讓人認為該地址在數(shù)值上等于變量a 所在的計算機存儲器地址,但在C 語言中并沒有此類要求。

以下示例可清楚地說明最后一點:考慮具有多個獨立存儲區(qū)的PIC 器件。對位于數(shù)據(jù)存儲器中器件地址100h 的變量使用地址運算符時應返回什么值?而對位于程序存儲器中器件地址100h 的另一個變量使用地址運算符時又應返回什么值?

如果在兩種情況下都回答 100h,那么在運行時如何得知100h 是數(shù)據(jù)存儲器中的地址還是程序存儲器中的地址呢?顯然,在這種情況下,如果稍后要解除引用地址,則需要其他方式來確定應訪問哪個存儲器。

“其他方式”可以是對地址運算符返回的值進行特殊編碼(與MPLAB XC8 編譯器配合使用的技術(shù)),也可以使用傳達相同信息的特殊指針類型限定符(MPLAB XC16 和XC32 編譯器使用該方法)。

為保持代碼的可移植性,不應假設(shè)將整數(shù)賦給指針就會使指針能訪問任何對象,即使該整數(shù)的值與某個對象的器件地址相同。因此對于上面的示例,為指針賦值立即數(shù)100h(或者保留此值的整數(shù)變量)并不意味著該指針指向變量a。

/* 我們發(fā)現(xiàn)“a”被分配到地址100h
*/int a, *p;
/* 注:這涉及整數(shù)到指針的隱式轉(zhuǎn)換 */
p = 0x100;
/* 沒人知道會發(fā)生什么!*/
*p = 0;

請記住,一種地址空間中的取指和存儲可能不像另一種地址空間中的取指和存儲一樣簡單——編譯器可能需要使用不同的寄存器和指令才能執(zhí)行訪問。

基于同樣的原因,在定義指針時,必須使用適當?shù)闹羔橆愋拖薅ǚS捎?MPLAB XC8 對地址進行編碼,因此它不使用特殊地址空間限定符,而MPLAB XC16 和XC32 則使用。但是,兩種情況下都必須適時使用通常的const 和volatile 限定符。限定符在數(shù)據(jù)定義中指定,如果想要可靠地訪問該數(shù)據(jù),則需要使用與引用該數(shù)據(jù)的指針相匹配的限定符。例如,使用MPLABXC16 時:

__psv__ char buffer[8] __attribute__((space(psv)))

在閃存程序存儲器中放置一個字符數(shù)組buffer,可通過“psv”(程序空間可視性)窗口進行訪問。直接訪問buffer 將使編譯器生成可確保psv 窗口(位于處理器地址空間中的特定位置)映射到閃存(包含“buffer”)中適當位置的代碼。buffer 的“地址”是所需窗口設(shè)置與“buffer”在整個窗口中的可視區(qū)域內(nèi)的偏移量的組合。

通過指針引用“buffer”中的項時,必須使用如下指針:

__psv__ char *bp;

才能使編譯器生成正確的代碼。不帶__psv__限定符的“普通”指針不起作用。

因此指針不僅僅是一個寬到可以保存“地址”的整數(shù),它還具有關(guān)聯(lián)的目標類型;C 語言數(shù)據(jù)地址不僅僅是一個計算機存儲器地址,它可由編譯器修改或優(yōu)化。C 編譯器還會考慮其他一些事項。

出問題的位置

如果我們認為指針只是一個值為(計算機存儲器)地址的整數(shù),并且認為我們已了解地址的含義以及該存儲器中排列數(shù)據(jù)的方式,我們可能會想要在所編寫的C 語言代碼中顯式執(zhí)行各種各樣的地址運算,進而在程序中嵌入底層運行時環(huán)境的特定于實現(xiàn)的詳細信息。這樣一來,即使現(xiàn)在程序可以運行,但如果針對其他處理器進行編譯,可能就無法正常工作,或者可能在看起來無關(guān)緊要的更改后莫名停止工作。我們該如何避免這類問題呢?

1. 使用正確的指針類型。根據(jù)引用的數(shù)據(jù)選擇適用的指針類型。盡管在你添加一系列轉(zhuǎn)換后程序會進行編譯,但不要據(jù)此認為程序會實際按照你的期望工作。它會按照你告訴它的方式工作,這可能與你的期望有很大不同。

2. 根據(jù)你將用來訪問數(shù)據(jù)的結(jié)構(gòu)來分配數(shù)據(jù)

3. 不要猜測數(shù)據(jù)類型的布局

例如,可以分配一個字符緩沖區(qū),然后將該緩沖區(qū)的地址轉(zhuǎn)換為指向更大類型數(shù)據(jù)數(shù)組或結(jié)構(gòu)數(shù)組的指針。隨后你可能會通過不同類型的指針,有時訪問字符型數(shù)據(jù),有時訪問其他類型的數(shù)據(jù)。為此,必須知道更大類型的數(shù)據(jù)在字符數(shù)據(jù)上以及彼此之間的排列方式。這非常危險而且容易出錯。如果需要通過多類型“視圖”訪問數(shù)據(jù),請將數(shù)據(jù)分配成聯(lián)合數(shù)組,然后通過聯(lián)合訪問數(shù)據(jù)。編譯器將清楚你的意圖并幫助你正確實現(xiàn)。

示例

下面的 C 程序建立了一個初始化結(jié)構(gòu)數(shù)組,顯示該數(shù)組,修改數(shù)組的一個元素,最后顯示更新的結(jié)果。代碼中針對選擇和更新要更改的元素提供了幾種備選方法。其中一些是常用方法,但實際上是不安全的代碼模式:

1: /* 用于演示指針運算問題的測試程序 */
2: #include
3: #include
4:
5: struct twoints {
6: uint8_t a;
7: uint32_t b;
8: };
9:
10: static struct twoints twointbuf[4] = {
11: {1, 5}, {2, 6}, {3, 7}, {4, 8}
12: };
13:
14: int main(int argc, char *argv[])
15: {
16: struct twoints *p;
17: size_t i;
18:
19: /* 輸出結(jié)構(gòu)數(shù)組 */
20: printf(“Before:\n\n”);
21: i = 0;
22: p = twointbuf;
23: while (i a, (*p).b);
25: ++p;
26: ++i;
27: }
28: printf(“\n”);
29:
30: /* 選擇下標為2 的元素的正確方法 */
31: p = twointbuf + 2;
32:
33: /* 等效且同樣好的方法 */
34: #ifdef ALSORIGHT
35: p = &twointbuf[2];
36: #endif
37:
38: /* 正確,但沒有必要采用的方法 */
39: #ifdef CORRECTBUTWHY
40: p = (struct twoints *)((char *)twointbuf + 2*sizeof(struct twoints));
41: #endif
42:
43: /* 以下是常見錯誤 */
44: #ifdef REALLYWRONG
45: p = (struct twoints *)((char *)twointbuf + 2*(sizeof(uint8_t) + sizeof(uint32_t)));
46: #endif
47: #ifdef NOTSAFE
48: p = (struct twoints *)((size_t)twointbuf + 2*sizeof(struct twoints));
49: #endif
50:
51: /* 修改元素2 */
52: p->b = 0xffffffff;
53:
54: /* 顯示更新的數(shù)組 */
55: printf(“After:\n\n”);
56: i = 0;
57: p = &twointbuf[0];
58: while (i a,(p[ i]).b);
60: ++i;
61: }
62: printf(“\n”);
63:
64: return 0;
65: }

我們討論一下如何訪問要修改的第二個結(jié)構(gòu)元素。在第10 行中聲明的twointbuf 是一個結(jié)構(gòu)數(shù)組,相當于指向該數(shù)組首地址的指針。我們可以通過數(shù)組或指針語法來訪問該數(shù)組中的元素,這兩種編碼風格表示同一個意思。第31 行和第35 行中給出的備選方法均是獲取指向數(shù)組中元素2 的指針的安全方法。編譯器不會將“2”解讀成兩個字節(jié)或兩個“字”,而是解讀成元素0 和元素1 后面的元素的編號2。

在第 40 行,我們看到了根據(jù)數(shù)組的字節(jié)地址以及前面元素的長度(字節(jié))來計算結(jié)構(gòu)元素地址的示例。如果(char *)上的限定符與數(shù)組上的限定符(本示例中沒有)匹配,則這種方法可行——只要字符指針和數(shù)組均聲明為引用相同的地址空間,地址和增量映射到底層存儲的規(guī)則就會相同,且該代碼有效。但為什么要這樣做呢?使用C 語言提供的簡潔明了的語法,編譯器將生成同樣正確或更有效的代碼。

在第 45 行,此代碼假設(shè)結(jié)構(gòu)元素的長度(字節(jié))是兩個成員的長度之和。這是不安全的假設(shè),因為編譯器可能必須對結(jié)構(gòu)進行填充才能使兩個成員在自然字邊界上對齊。是否使用結(jié)構(gòu)填充將取決于目標器件。

第 48 行上的語句一開始沒有將數(shù)組指針轉(zhuǎn)換為字符指針,而是轉(zhuǎn)換為大到足以保存指針的整數(shù),從而向編譯器隱藏了該值是特定地址空間中的地址的事實。隨后執(zhí)行與第40 行相同的地址運算,并將結(jié)果轉(zhuǎn)換回指向結(jié)構(gòu)數(shù)組的指針。在這種情況下,編譯器沒有機會對添加為特定空間中的指針和下標的數(shù)字進行解讀,且無法應用任何映射規(guī)則。因此轉(zhuǎn)換回結(jié)構(gòu)指針的值可能是錯誤的。

結(jié)論

使用C 語言的功能時,應依據(jù)功能在語言中的含義:

使用地址運算符來獲取要賦給指針的地址。

確保所定義的指針類型在程序執(zhí)行期間與其可引用的數(shù)據(jù)相匹配。

決不要假設(shè)對象分配到存儲器的方式。

不要假設(shè)或回避規(guī)則來使 C語言代碼更“直接”和“有效”,此類代碼不會具有可移植性、可靠性或更有效。
本文地址:http://m.qingdxww.cn/thread-151189-1-1.html     【打印本頁】

本站部分文章為轉(zhuǎn)載或網(wǎng)友發(fā)布,目的在于傳遞和分享信息,并不代表本網(wǎng)贊同其觀點和對其真實性負責;文章版權(quán)歸原作者及原出處所有,如涉及作品內(nèi)容、版權(quán)和其它問題,我們將根據(jù)著作權(quán)人的要求,第一時間更正或刪除。
您需要登錄后才可以發(fā)表評論 登錄 | 立即注冊

廠商推薦

  • Microchip視頻專區(qū)
  • Dev Tool Bits——使用MPLAB® Discover瀏覽資源
  • Dev Tool Bits——使用條件軟件斷點宏來節(jié)省時間和空間
  • Dev Tool Bits——使用DVRT協(xié)議查看項目中的數(shù)據(jù)
  • Dev Tool Bits——使用MPLAB® Data Visualizer進行功率監(jiān)視
  • 貿(mào)澤電子(Mouser)專區(qū)

相關(guān)視頻

關(guān)于我們  -  服務條款  -  使用指南  -  站點地圖  -  友情鏈接  -  聯(lián)系我們
電子工程網(wǎng) © 版權(quán)所有   京ICP備16069177號 | 京公網(wǎng)安備11010502021702
快速回復 返回頂部 返回列表
主站蜘蛛池模板: 99re这里只有精品国产精品 | 色综合天天色综合 | va在线播放 | 丰满美女视频 | 免费在线观看污视频网站 | 国产热久久精 | 国产精彩对白综合视频 | 日本不卡高清中文字幕免费 | 精品国产区一区二区三区在线观看 | 丁香伊人五月综合激激激 | 天天天操操操 | 在线观看视频色 | 欧美人与性动交α欧美精品 | 日韩一级片播放 | 欧美大片日韩精品四虎影视 | 日韩毛片一级 | 九月丁香十月婷婷在线观看 | 精品久久一区二区三区 | 日韩在线视频免费 | 国产性夜夜夜春夜夜爽 | 亚洲国产欧美日韩一区二区三区 | 日本天堂在线播放 | 国产成视频 | 欧美成人精品手机在线观看 | 欧美视频一级 | 日韩性视频网站 | 国产欧美成人免费观看 | 日本!日本! 在线播放 | 久久婷婷色 | 视频一区二区在线播放 | 色播艾小青国产专区在线播放 | 国产在线播放网址 | 天天视频免费观看高清影视 | 色综合天天综合网看在线影院 | 亚洲一区二区三区精品国产 | 青青国产成人久久91网站站 | 曰本人一级毛片免费完整视频 | 日本在线视频二区 | 亚洲欧洲精品视频在线观看 | 一卡2卡三卡4卡在线不卡 | 欧美91 |