C++获取缓冲区的二进制值

7
很抱歉问题的措辞有点混乱。
假设我有一个缓冲区:
char buffer[4] = {0, 0, 2, 0};

如果我们将所有内容转换为二进制,我们可以得到一个32位无符号整数: 00000000 00000000 00000010 00000000 它的值是512。
我的问题是,如何在c++中使用一些库函数来获得512的答案?
感谢您的任何答案,如果此前已经有人问过这个问题而我没有找到,请谅解。

你意识到你将512表示为大端序了吗? - ppetraki
3个回答

8
您可以执行一些位运算操作:
unsigned int converter(const char buffer[4]) {
  return (buffer[0] << 24) |
         (buffer[1] << 16) |
         (buffer[2] << 8) |
         (buffer[3]);
}

这里有一个例子。


2
唯一正确的答案!reinterpret_cast和联合体在其他答案中被传播,但它们在标准中使用的方式是不允许的。 - Anton F.
2
@AntonF,请向我们展示规范中禁止使用联合体将一种类型转换为另一种类型的地方。 - Chimera
我在另一个答案中的评论: 它适用于大多数编译器,但不安全且不可移植。请参阅en.cppreference.com/w/cpp/language/union部分成员生命周期:“联合成员的生命周期始于该成员被激活时。如果先前有另一个成员处于活动状态,则其生命周期结束。”考虑优化/寄存器存储等。 - Anton F.

0

如果你是大端字节序,那只有512。你将二进制格式写成了MSB:LSB,但这不是你在数组缓冲区中表示字节的方式。数组中的“LSB”是buffer [0],小端表示如下。

一种可移植的类型转换方法是实际上使用memcpy。根据此编译器资源管理器比较,甚至函数调用都被优化掉了。

#include <iostream>                                                             
#include <cstdint>                                                              
#include <array>                                                                
#include <cstring>                                                              

std::array<unsigned char, 4> buffer = {0, 2, 0, 0};                             

template <std::size_t N>                                                        
std::uint32_t char_buf_to_int( std::array<unsigned char, N>& b )                
{                                                                               
    std::uint32_t i;                                                            
    std::memcpy( &i, b.data(), b.size() );                                      
    return i;                                                                   
}                                                                               

int main()                                                                      
{                                                                               
    std::cout << char_buf_to_int( buffer ) << std::endl;                        
    return 0;                                                                   
}    

1
reinterpret_cast 不允许这样使用。标准禁止在不相关的指针类型之间进行转换!最近我才学会了这一点(以前我不相信)。 - Anton F.
2
@AntonF。static_cast适用于相关类型,reinterpret_cast适用于不相关类型。在此示例中进行静态转换将在编译时失败,http://www.cplusplus.com/doc/tutorial/typecasting/。“reinterpret_cast将任何指针类型转换为任何其他指针类型,甚至是不相关的类。操作结果是从一个指针到另一个指针的简单二进制复制值。允许所有指针转换:既不检查指向的内容也不检查指针类型本身。” - ppetraki
1
@ppetraki 不行,你不能这样做。你的代码是未定义行为和有风险的。你可以“安全地”重新解释Tchar,但反过来不成立。你不能将一个char数组强制转换为unsigned int。架构表示可能不同(内存对齐)。 - BiagioF
1
@AntonF。我想我被说服了。我抵制使用memcpy的原因是因为它会带来额外的开销,但事实证明编译器“知道memcpy”,并将其优化掉了。即使使用函数的开销也被优化掉了 https://blog.regehr.org/archives/959 ,并且生成的代码与我的原始示例完全相同 https://godbolt.org/z/nVXagN 。 - ppetraki
1
@ppetraki:不用谢。现在你编辑过的答案是正确的,而且经过优化后可能是转换值的最快方式。 - Anton F.
显示剩余8条评论

-1

编辑:这种方法“有效”,但根据一些人的说法,它依赖于编译器允许它,因为从技术上讲,它会产生未定义的行为。所以使用时需自担风险。

还有另一种方法是使用联合体。根据大小端,您将不得不正确地排序缓冲区。请参见下面的示例。

#include <stdio.h>

union u{ 
    unsigned char buffer[4]; 
    unsigned int x; 
} u; 



int main()
{

    u.buffer[0] = 0;
    u.buffer[1] = 2;
    u.buffer[2] = 0;
    u.buffer[3] = 0;


    printf("%d\n", u.x);

    return 0;
}

如评论中所提到的,您也可以使用联合初始化。请参见下面:

#include <stdio.h>
#include <stdint.h>

union u{ 
    uint8_t buffer[4]; 
    uint32_t x; 
}; 

int main()
{
    union u u1 = {.buffer = {0,2,0,0} };
    printf("%d\n", u1.x);

    return 0;
}

联合初始化会更有效率。https://zh.cppreference.com/w/c/language/struct_initialization - ppetraki
2
@ppetraki 标准不允许这样做。你只能使用“活动字段”。只能从你最近写入的那个字段中读取! - Anton F.
2
@AntonF. 你能否提供一个来源?因为这个代码可以正常工作。 - ppetraki
它适用于大多数编译器,但不安全且不可移植。请参见https://en.cppreference.com/w/cpp/language/union节成员生存期:“联合成员的生存期始于成员被激活时。如果先前有另一个成员处于活动状态,则其生存期结束。”考虑优化/寄存器存储等因素。 - Anton F.
1
这段代码是未定义行为。正如@AntonF.所说,语句u1.x访问了"ended-lifetime"成员。 - BiagioF
@BiagioFesta 感谢您提供的信息。我一直听说过使用这种技术,但从未知道它在技术上是未定义行为。 - Chimera

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接