我正在使用 C 17。
假設我有兩個變數a和b。這些變數的型別為uint8_t。我希望能夠以 uint8_t 和 uint16_t 的形式訪問它們。
例如 :
#include <memory>
int main()
{
uint8_t a = 0xFF;
uint8_t b = 0x00;
uint16_t ab; // Should be 0xFF00
}
我認為使用陣列將是一個很好的解決方案,因為這兩個變數應該在記憶體中彼此相鄰。所以我這樣做了:
#include <memory>
int main()
{
uint8_t data[] = {0xFF, 0x00};
uint8_t * a = data;
uint8_t * b = data sizeof(uint8_t);
uint16_t * ab = reinterpret_cast<uint16_t*>(data);
std::cout << std::hex << (int) *a << "\n";
std::cout << std::hex << (int) *b << "\n";
std::cout << std::hex << (int) *ab << "\n";
}
輸出:
ff
0
ff
但我希望:
ff
0
ff00
你能解釋一下我在這里做錯了什么,任何危險信號或更好的方法嗎?
謝謝 !
uj5u.com熱心網友回復:
還有其他幾種方法可以在兩個 8 位和一個 16 位值之間進行轉換。
但請注意,直接尋址 16 位值中的單個位元組的每個解決方案的結果取決于執行它的機器的位元組順序。例如,英特爾使用“小端”,其中首先存盤最低有效位。其他機器可能使用“大端”并首先存盤最重要的位。
使用 bitshift 和 or 計算 16 位值
const uint8_t a = 0xff;
const uint8_t b = 0x00;
const uint16_t ab = a | (b << 8); // works because b is promoted to int before being shifted
使用 bitshift 并and計算 8 位值
const uint16_t ab = 0xff;
const uint8_t a = ab & 0xff;
const uint8_t b = ab >> 8;
直接尋址字的位元組
uint16_t ab;
auto& a = reinterpret_cast<uint8_t*>(&ab)[0];
auto& b = reinterpret_cast<uint8_t*>(&ab)[1];
使用聯合
這是標準明確不允許的(但也無處不在)
宣告以下聯合:
union conv
{
struct {
uint8_t a, b;
};
uint16_t ab;
};
您現在可以使用它來將兩個 8 位值組合成一個 16 位值:
conv c;
c.a = 0xFF;
c.b = 0x00;
std::cout << c.ab << std::endl;
在 Intel 機器上,這將輸出 255 (0xff),因為 Intel 使用“little endian”,其中最低有效位首先存盤。所以a是ab的低位元組,b是高位元組。
如果您將聯合重新定義為
union conv
{
struct {
uint8_t b, a;
};
uint16_t ab;
};
上面的示例將在 Intel 機器上輸出 65280 (0xff00),因為現在 b 代表 a 的最低有效位 8 位,而 a 代表最高有效位。
結合聯合和位域,您還可以訪問 16 位值的每一位:
union bitconv
{
struct {
uint16_t
b0 : 1, b1 : 1, b2 : 1, b3 : 1, b4 : 1, b5 : 1, b6 : 1, b7 : 1,
b8 : 1, b9 : 1, b10 : 1, b11 : 1, b12 : 1, b13 : 1, b14 : 1, b15 : 1;
};
uint16_t word;
};
uj5u.com熱心網友回復:
這是因為位元組順序wiki。
正如你所看到的,這段代碼決定了記憶體中的位元組順序
uint16_t x = 0x0001;
std::cout << (*((uint8_t*)&x) ? "little" : "big") << "-endian\n";
嘗試交換號碼
#include <memory>
#include <iostream>
int main()
{
uint16_t x = 0x0001;
std::cout << (*((uint8_t*)&x) ? "little" : "big") << "-endian\n";
uint8_t data[] = { 0x00, 0xFF };
uint8_t* a = data;
uint8_t* b = data sizeof(uint8_t);
uint16_t* ab = reinterpret_cast<uint16_t*>(data);
std::cout << std::hex << (int)*a << "\n";
std::cout << std::hex << (int)*b << "\n";
std::cout << std::hex << (int)*ab << "\n";
}
結果
little-endian
0
ff
ff00
uj5u.com熱心網友回復:
沒有未定義行為可移植的方式打包2uint8_t成uint16_t,后:
int main() {
uint8_t a = 0xFF;
uint8_t b = 0x00;
// from a and b to ab
uint16_t ab = a * 0x100 b;
// from ab to a and b
a = ab / 0x100 & 0xff;
b = ab & 0xff;
}
需要注意的是依賴于鑄造的所有方法uint16_t來uint8_t*,因為只有發生在作業uint8_t是一個型別別名unsigned char和char型別,因為它們可以別名任何其他型別比較特殊。當轉換為大于 的任何其他型別uint8_t時,例如轉換uint64_t為uint32_t*或時,此方法會破壞嚴格的別名并導致未定義的行為uint16_t*。
請參閱什么是嚴格別名規則以及我們為什么關心?更多細節。
uj5u.com熱心網友回復:
在您的情況下,使用 union 和 struct 更好也更容易理解。下面是一個例子:
#include <iostream>
struct mystruct {
uint8_t a;
uint8_t b;
} ;
union my_unino{
mystruct x;
uint16_t ab ;
}data;
int main(){
data.x.a=0x1;
data.x.b=0xff;
std::cout<<std::hex<<(int)data.x.a<<std::endl;
std::cout<<std::hex<<data.ab<<std::endl;
return 0;
}
結果:
1
ff01
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/386150.html
