编辑存储在字节数组中的数据

Posted

技术标签:

【中文标题】编辑存储在字节数组中的数据【英文标题】:Edit data stored in a byte-array 【发布时间】:2014-10-01 19:39:24 【问题描述】:

我一直在尝试将经常被操作的数据存储到一个无符号字符的大数组中(因为 C++ 没有字节类型)。因此,如果我将浮点数存储到 char 数组中,它将占用 4 个无符号字符。很简单,除非现在我想在数组中编辑该数据,否则我需要同时访问所有 4 个元素,据我所知这是不可能的。 那么,有没有一种方法可以将 4 个无符号字符编辑为我想要的浮点值,而无需使用 memcpy()?

例子:

#include <iostream>
#include <string.h>
#include <stdint.h>

using namespace std;

struct testEntity 

    float density;
    uint16_t xLoc, yLoc;
    uint16_t xVel, yVel;
    uint16_t xForce, yForce;
    uint16_t mass;
    uint8_t UId;
    uint8_t damage; 
    uint8_t damageMultiplier;
    uint8_t health;
    uint8_t damageTaken;
;


int main()


    testEntity goblin  1.0, 4, 5, 5, 0, 0, 0, 10, 1, 2, 1, 10, 0 ;
    testEntity goblin2;
    unsigned char datastream[24];
    unsigned char* dataPointer = datastream;

    memcpy(&datastream, &goblin, sizeof(testEntity));


    //I know that datastream[0..3] contains information on goblin.density
    //How would I edit goblin.density without memcpy into another testEntity structure?

    memcpy(&goblin2, &datastream, sizeof(testEntity));

return 0;

【问题讨论】:

如果我理解正确,您可以将datastream 转换为testEntity *,然后访问浮点值。 @Deduplicator 我知道这一点。我从来没有说过我没有,也没有试图混淆这两者。要么回答我的问题,如果你认为我需要澄清,那就给它,或者不要。 @user2233706 我会试试这个,等一下。 @Deduplicator 它有 24 个字节长。注意density之后,同一行有两个uint16_t 【参考方案1】:

这就是我所做的:

#include <iostream>
#include <string.h>
#include <stdint.h>

using namespace std;

struct testEntity 

    float density;
    uint16_t xLoc, yLoc;
    uint16_t xVel, yVel;
    uint16_t xForce, yForce;
    uint16_t mass;
    uint8_t UId;
    uint8_t damage; 
    uint8_t damageMultiplier;
    uint8_t health;
    uint8_t damageTaken;
;


int main()


    testEntity goblin =  1.0, 4, 5, 5, 0, 0, 0, 10, 1, 2, 1, 10, 0 ;
    testEntity goblin2;
    unsigned char datastream[24];
    unsigned char* dataPointer = datastream;
    testEntity *goblinP;

    memcpy(datastream, &goblin, sizeof(testEntity));


    goblinP = (testEntity *) datastream;

    cout << goblinP->density << endl;

return 0;

【讨论】:

现在,如果我将数据流的大小加倍,现在是 48 字节,我将如何插入第二个 testEntity 呢?我在想 memcpy(datastream+sizeof(testEntity), &goblin2, sizeof(testEntity)),然后分配 goblinP =(testEntity *) dataStream[24],但这似乎失败了。有什么想法吗? 你需要做goblinP = (testEntity *) (datastream + 24)goblinP = (testEntity *) &amp;datastream[24]。您的表达式将dataStream 的第一个字节转换为一个地址,该地址不是有效地址。 为什么不goblinP = 1+(testEntity*)datastream?无论如何,请避免使用幻数。 @Deduplicator 或goblinP[1] 后做goblinP = (testEntity *) datastream 您可以通过编写unsigned char datastream[sizeof goblin]; ... memcpy(datastream, &amp;goblin, sizeof goblin);来改进此代码【参考方案2】:

您的计划:

goblinP = (testEntity *)datastream;

违反了严格的别名规则。释义,规则是只能通过具有该对象类型的表达式来访问对象;除了少数例外。您可以将对象作为无符号字符访问,但不能将无符号字符作为对象访问。

此代码可能似乎可以工作,但您实际上是在玩火,因为编译器可能会决定优化您读取对象的尝试,因为标准说您正在有效地读取未初始化的变量。

要做你想做的事,声明你的缓冲线:

testEntity goblin;

然后您可以通过执行以下操作将其别名为字节:

unsigned char *datastream = reinterpret_cast<unsigned char *>(&goblin);

您可以通过datastream 写入字节,然后访问goblin 以查看您得到了什么。 (当然,这仍然取决于您写入的字节实际上是 testEntity 的有效对象表示形式。

【讨论】:

【参考方案3】:

通过使用联合,您可以通过多种方式访问​​内存位置,也许这​​就是您正在寻找的(如果我理解正确的话):

typedef union 

  struct testEntity 
  
    float density;
    uint16_t xLoc, yLoc;
    uint16_t xVel, yVel;
    uint16_t xForce, yForce;
    uint16_t mass;
    uint8_t UId;
    uint8_t damage; 
    uint8_t damageMultiplier;
    uint8_t health;
    uint8_t damageTaken;
   te;
  char datastream[24];
 myunion;


...
myunion goblin =  1.0, 4, 5, 5, 0, 0, 0, 10, 1, 2, 1, 10, 0 ; 

char* goblinP = goblin.datastream;

or e.g. goblin.te.health

编辑:

最好为您的结构创建一个序列化/反序列化函数,以便将其从/转换为结构。

例如

ostream& operator<<(ostream& o, const testEntity &in)

  typedef union  float f; char bytes[4];  floatunion;
  floatunion fu = in.density;
  o.write( fu.bytes, 4 );
  ...
  return o;  

【讨论】:

我相信这将是朝着正确方向迈出的一步。我完全忘记了工会!实际上,数据流(在您的示例中为 goblinP)将能够容纳数百个实体。例如,unsigned char datastream[48] 来保存联合的 2 个实例。我可以将 myunion.datastream 存储到正确的位置,但是如何将 goblinP 数据以某个间隔*24 重新解释到联合数据流中?只是 myunion.datastream = &goblinP[24/48/72...n*24]? 在 C++ 中,causes undefined behaviour 访问不是最后一个分配的联合成员。 @DanielMartin 我错过了 c++ 标签,我认为在 c++ 中以不同的方式做会更好,例如为您的结构创建一个 > 重载,然后使用它们进行写入/读取,因为我猜这就是您最终想要实现的目标,不是吗? @Claptrap 有点,是的。最终目标是拥有一块连续的内存,各种实体将存在于其中,并使用一个单独的数组来跟踪每个实体的开始和结束位置。 @DanielMartin 在不确切知道您的要求/约束​​的情况下,听起来有些 STL 容器比摆弄字节要好。结构向量确保它们在内存中是连续的

以上是关于编辑存储在字节数组中的数据的主要内容,如果未能解决你的问题,请参考以下文章

将 FILE 中的二进制字节值存储到 C 数组中

如何将字节数组中的图像文件数据转换为位图?

用于 ScintillaNET 的字节数组到文本

字节数组到短数组并在java中再次返回

Java - 将字节数组作为字符串存储在数据库中,并使用字符串值创建字节数组

将字节变量添加到字节数组中的正确方法