我想把一個大型別的東西分成幾個小型別的東西。例如,將一個uint32_t變數更改為四char件事。下面是我的方法:
uint32_t big = 0x01234567; // little endian
char* ptr = (char*)&big 1; // points to 0x45
*(ptr 1) = 0xff;
printf("x", big); //01ff4567
這是一個明確的行為嗎?或者有更合適的方法嗎?相反,我知道
int a = 20, b = 30, c = 40;
int* d = &b;
*(d 1)=60; // some compiler will pass; while some will error
for(int i=0;i<5;i )
printf("%d, ",*(d i));
是一個未定義的行為,因為我們訪問了指標指向的范圍之外的記憶體。如果第一種情況是合法的,那么我想知道有什么區別?也就是說,如果程式安全地擁有整個區域,就像
- 宣告一個大型別變數
uint32_t big = 0x01234567并執行上述操作,或者 - 比如宣告一個陣列(所以我們確定有我們擁有的連續記憶體),例如,
int apple[4] = {10,20,30,40}; int* ptr = &apple[1]; *(ptr 1) = 666; // success to move or edit the value, // even originally ptr is only an int type pointer for (int k = 0; k < 15; k ) { printf("%d\n", *(ptr k)); }
那么訪問指標宣告的型別的原始范圍之外的記憶體是否合法?這很令人困惑。
uj5u.com熱心網友回復:
讓我們舉個例子
int apple[4] = {10,20,30,40};
int* ptr = &apple[1];
如果我們現在“繪制”出陣列和指標,它將看起來像這樣:
---------- ---------- ---------- ----------
| 蘋果[0] | 蘋果[1] | 蘋果[2] | 蘋果[3] |
---------- ---------- ---------- ----------
^
|
指標
從中很容易看出ptr 1將指向apple[2]. 它仍然在原始陣列內,因此沒有越界并且是一個有效的指標。
還記得,對于任何陣列或指標ptr和索引i,所述表達*(ptr i)是恰好等于ptr[i]。后者 ( ptr[i]) 通常更容易閱讀和理解。也少寫了。
uj5u.com熱心網友回復:
在您的第二個示例中,變數a、和不構成陣列,因此您無法保證它們會占用記憶體中的連續區域。編譯器不需要以任何特定的順序分配它們,它們甚至不需要在記憶體中分配它們——例如,一個變數可以保存在處理器的暫存器中,或者如果不使用甚至完全丟棄。bcd
因此,指標算術沒有定義為將您從其中一個帶到另一個。
uj5u.com熱心網友回復:
uint32_t big = 0x01234567; // little endian char* ptr = (char*)&big 1; // points to 0x45 *(ptr 1) = 0xff; printf("x", big); //01ff4567這是一個明確的行為嗎?
允許通過指向字符型別的指標訪問任何物件的表示。這是字符型別的特殊屬性,例如charand unsigned char。您用來完成此任務的方法已明確定義。
但是,型別表示的許多細節都留給實作來決定。其中特別值得注意的是各種整數型別中的位元組順序。// points to 0x45因此,您的評論只是一種可能性,您將在所謂的小端機器上觀察到這種可能性,例如基于 Intel 的機器。在今天可用的其他機器上,您可能會發現ptr最初指向值為 value 的位元組0x23。
歷史上,也有一些機器展示了 32 位整數的其他位元組順序。
或者有更合適的方法嗎?
通過指向的指標訪問物件的表示char是一回事,但使用類似的方法來訪問它,因為其他型別具有未定義的行為。如果您想在示例中就地執行此操作,則可以通過指向適當聯合型別的指標來執行此操作。例如:
uint32_t big = 0x01234567;
union parts {
uint32_t as_u32; // this member is required, even if it goes unreferenced
uint16_t as_u16[2];
uint8_t as_u8[4];
};
union parts *ptr = (union parts *) &big;
ptr->as_u8[3] = 0xba;
ptr->as_u16[0] = 0xfedc;
printf("x", big); // on a little-endian machine, prints ba23fedc
“適當”意味著聯合成員之一具有與您嘗試訪問的物件型別兼容的型別,而其他成員具有表示您希望通過其訪問它的視圖的型別。然后訪問應該明確地通過聯合,如圖所示。
可能對“正確”的程度存在一些分歧,但 C 允許它,前提&big是它與 a 適當對齊union parts *。
另一種主要的替代方法是用于memcpy()將物件的部分表示復制到不同的物件或從不同的物件復制。例如,
uint32_t big = 0x01234567;
uint16_t medium;
memcpy(&medium, sizeof(medium) (char *) &big, sizeof(medium));
printf("" PRIx16 "\n", medium); // prints 0123 on a little-endian machine
medium = 0xfedc;
memcpy(&big, &medium, sizeof(medium));
printf("x\n", big); // prints 0123fedc on a little-endian machine
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/314978.html
