【问题标题】:Converting bit field to byte array将位字段转换为字节数组
【发布时间】:2015-02-26 10:12:53
【问题描述】:

我有一些 mpeg ts 位域,例如传输流包:

struct ts_package_header_s {
    unsigned int continuity_counter :4;
    unsigned int adaptation_field_control :2;
    unsigned int transport_scrambling_control :2;
    unsigned int PID :13;
    unsigned int transport_priority :1;
    unsigned int payload_unit_start_indicator :1;
    unsigned int transport_error_indicator :1;
    unsigned int sync_byte :8;
};

struct ts_package_s {
    struct ts_package_header_s ts_header;
    unsigned char ts_body[TS_BODY];
};

union ts_package_u {
    struct ts_package_s ts_package;
    unsigned char bytes[TS_PACKAGE];
};

在我的源代码中,我初始化了标头结构:

pat_package_header.sync_byte = 0x47;
pat_package_header.transport_error_indicator = 0;
pat_package_header.payload_unit_start_indicator = 1;
pat_package_header.transport_priority = 0;
pat_package_header.PID = PAT_PID;
pat_package_header.transport_scrambling_control = 0;
pat_package_header.adaptation_field_control = 1;
pat_package_header.continuity_counter = 0;

比我创建 ts_packag 联合

union ts_package_u package;
package.ts_package.ts_header = pat_package_header;

比我填充 ts_body 数组。 当我将此包写入文件时。我得到了向后数组:

10 00 40 47 XX XX XX.. instead of 47 40 10 00 XX XX XX.. 

我尝试将我的结构转换为 char* 而不是使用 union,但得到了相同的结果。

错在哪里?谢谢。

【问题讨论】:

  • 您已将sync_byte 声明为结构中的最后一个成员,现在您想知道它不是第一个。
  • @mch 如果我向后移动这个结构,我将得到 '47 02 00 04' 而不是 '47 40 10 00'
  • 位域没有可移植的表示形式,您的编译器可以随意布置它们。如果您需要匹配现有的数据布局,请不要使用位域。

标签: c arrays struct unions


【解决方案1】:

在磁盘上直接使用这样的 try 和序列化结构是很危险的,因为格式必须在不同的体系结构之间是已知的,或者使用不同的编译器。

编译器存储位域的方式不同,架构的底层字节序也会改变数据的存储方式

例如,编译器可能选择在字节、字或其他边界上对齐位域。这是编译器的决定。它也可以选择以任何顺序保存位,这通常取决于您机器的字节序。

为了安全地将此标头写入磁盘,您需要自己序列化数据。根据Wikipedia.,标头为 32 位和 Big Endian

例如:

#include <stdio.h>

#define TS_BODY 1024
#define PAT_PID 0x40

struct ts_package_header_s {
    unsigned int continuity_counter :4;
    unsigned int adaptation_field_control :2;
    unsigned int transport_scrambling_control :2;
    unsigned int PID :13;
    unsigned int transport_priority :1;
    unsigned int payload_unit_start_indicator :1;
    unsigned int transport_error_indicator :1;
    unsigned int sync_byte :8;
};

struct ts_package_s {
    struct ts_package_header_s ts_header;
    unsigned char ts_body[TS_BODY];
};

static void write_ts( struct ts_package_s pat_package )
{
    FILE* f = fopen( "test.ts", "wb+" );
    unsigned int header = 0;

    if( f == NULL )
        return;

    header = pat_package.ts_header.sync_byte << 24;
    header |= ( pat_package.ts_header.transport_error_indicator << 23 );
    header |= ( pat_package.ts_header.payload_unit_start_indicator << 22 );
    header |= ( pat_package.ts_header.transport_priority << 21 );
    header |= ( pat_package.ts_header.PID << 8 );
    header |= ( pat_package.ts_header.transport_scrambling_control << 6 );
    header |= ( pat_package.ts_header.adaptation_field_control << 4 );
    header |= ( pat_package.ts_header.continuity_counter );

    /* Write the 32-bit header as big-endian */
    unsigned char byte = header >> 24;
    fwrite( &byte, 1, 1, f );

    byte = ( header >> 16 ) & 0xFF;
    fwrite( &byte, 1, 1, f );

    byte = ( header >> 8 ) & 0xFF;
    fwrite( &byte, 1, 1, f );

    byte = header & 0xFF;
    fwrite( &byte, 1, 1, f );

    fclose( f );
}

int main( int argc, char* argv[] )
{
    struct ts_package_s pat_package;
    pat_package.ts_header.sync_byte = 0x47;
    pat_package.ts_header.transport_error_indicator = 0;
    pat_package.ts_header.payload_unit_start_indicator = 1;
    pat_package.ts_header.transport_priority = 0;
    pat_package.ts_header.PID = PAT_PID;
    pat_package.ts_header.transport_scrambling_control = 0;
    pat_package.ts_header.adaptation_field_control = 1;
    pat_package.ts_header.continuity_counter = 0;

    write_ts( pat_package );

    return 0;
}

写入具有以下标头的文件:

0x47 0x40 0x01 0x10

根据您使用的值,这似乎是正确的。

【讨论】:

  • 我们不能使用htonl() 将标头写为big-endian 吗?
猜你喜欢
  • 2019-12-13
  • 2011-02-02
  • 2010-09-26
  • 2010-10-17
  • 2018-07-16
  • 1970-01-01
  • 1970-01-01
  • 2018-05-06
相关资源
最近更新 更多