C語言的位域賦值問題

系統:ubuntu 11.04  little-end

描述:這個問題是在研究TS包頭解析時遇到的,用兩種方式取TS包頭,但結果不同

耗時:2天

程序:

1. 使用正常順序方式定義TS包頭結構體,然後將TS包頭數據拷貝到包頭結構體中。

void bob_get_packet_header_mem_cpy_1(unsigned char *ts_package)
{
	printf("  [%s]:\n", __func__);
	printf("   Package Source Data: ");
	unsigned char *p = ts_package;
	while(p < (ts_package + sizeof(TS_packet_header))){
		printf("%02x ", *p);
		p++;
	}
	printf(" (0x%04x)\n", *(unsigned int *)ts_package);

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

	memcpy((void *)&header, ts_package, sizeof(header));

	printf("   sync_byte(8b):                         0x%02X (%u) %s\n", header.sync_byte, header.sync_byte, (header.sync_byte == 0x47 ? "" : "(sync error)"));
	printf("   transport_error_indicator(1b):         0x%02X (%u) %s\n", header.transport_error_indicator, header.transport_error_indicator, (header.transport_error_indicator == 0x00 ? "" : "(packet error)"));
	printf("   payload_unit_start_indicator(1b):      0x%02X (%u)\n", header.payload_unit_start_indicator, header.payload_unit_start_indicator);
	printf("   transport_priority(1b):                0x%02X (%u)\n", header.transport_priority, header.transport_priority);
	printf("   PID(13b):                              0x%04X (%u)\n", header.PID, header.PID);
	printf("   transport_scrambling_control(2b):      0x%02X (%u) %s\n", header.transport_scrambling_control, header.transport_scrambling_control, (header.transport_scrambling_control == 0x00 ? "(not scrambled)" : "(scrambled)"));
	printf("   adaptation_field_control(2b):          0x%02X (%u) %s\n", header.adaptation_field_control, header.adaptation_field_control, (header.adaptation_field_control == 0x00 ? "(no adap_field, no payload)" : header.adaptation_field_control == 0x01 ? "(no adap_field, payload only)" : header.adaptation_field_control == 0x10 ? "(adap_field only, no payload)" : "(adap_field followed by payload)"));
	printf("   continuity_counter(4b):                0x%02X (%u)\n", header.continuity_counter, header.continuity_counter);
	printf("\n");
}


2. 使用正常順序方式定義TS包頭結構體,使用buffer存儲TS包頭,然後通過位移過濾方式將值賦給TS包頭結構體的成員。

void bob_get_packet_header_bit_shift(unsigned char *ts_package)
{
	printf("  [%s]:\n", __func__);
	printf("   Package Source Data: ");
	unsigned char *p = ts_package;
	while(p < (ts_package + sizeof(TS_packet_header))){
		printf("%02x ", *p);
		p++;
	}
	printf(" (0x%04x)\n", *(unsigned int *)ts_package);

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

	unsigned char buf[4];
	memcpy(buf, ts_package, sizeof(header));

	int i;
	for(i=0; i<4; i++){
		printf("%x[%d] ", buf[i], i);
	}
	printf("\n");
	
	/* *
	 * buf:
	 *      Hex: 	0x47 0x40 0x00 0x10
	 *      Bin: 	0100 0111 0100 0000 0000 0000 0001 0000
	 *	 buf[0]: 	0100 0111|
	 *	 buf[1]: 	0|1|0|0
	 *	 buf[2]: 	0000 0000
	 *	 buf[3]: 	0000|
	 *	 buf[4]: 	00|01| 0000
	 */
	header.sync_byte 					   = buf[0];
	header.transport_error_indicator       = buf[1] >> 7;
	header.payload_unit_start_indicator    = buf[1] >> 6 & 0x01;
	header.transport_priority              = buf[1] >> 5 & 0x01;
	header.PID                             = (buf[1] & 0x1F) << 8 | buf[2];
	header.transport_scrambling_control    = buf[3] >> 6;
	header.adaptation_field_control        = buf[3] >> 4 & 0x03;
	header.continuity_counter              = buf[3] & 0x0F;

 	printf("   sync_byte(8b):                         0x%02X (%u) %s\n", header.sync_byte, header.sync_byte, (header.sync_byte == 0x47 ? "" : "(sync error)"));
	printf("   transport_error_indicator(1b):         0x%02X (%u) %s\n", header.transport_error_indicator, header.transport_error_indicator, (header.transport_error_indicator == 0x00 ? "" : "(packet error)"));
	printf("   payload_unit_start_indicator(1b):      0x%02X (%u)\n", header.payload_unit_start_indicator, header.payload_unit_start_indicator);
	printf("   transport_priority(1b):                0x%02X (%u)\n", header.transport_priority, header.transport_priority);
	printf("   PID(13b):                              0x%04X (%u)\n", header.PID, header.PID);
	printf("   transport_scrambling_control(2b):      0x%02X (%u) %s\n", header.transport_scrambling_control, header.transport_scrambling_control, (header.transport_scrambling_control == 0x00 ? "(not scrambled)" : "(scrambled)"));
	printf("   adaptation_field_control(2b):          0x%02X (%u) %s\n", header.adaptation_field_control, header.adaptation_field_control, (header.adaptation_field_control == 0x00 ? "(no adap_field, no payload)" : header.adaptation_field_control == 0x01 ? "(no adap_field, payload only)" : header.adaptation_field_control == 0x10 ? "(adap_field only, no payload)" : "(adap_field followed by payload)"));
	printf("   continuity_counter(4b):                0x%02X (%u)\n", header.continuity_counter, header.continuity_counter);
	printf("\n");
}


3.  使用倒序方式定義TS包頭結構體,並將原始的TS包頭數據顛倒4->1, 3->2, 2->3, 1->4,然後將顛倒後的TS包頭數據拷貝到包頭結構體中。

struct _TS_packet_header//defined in reverse order
{
	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 ;
};
typedef struct _TS_packet_header TS_packet_header;

void TS_header_decode(TS_packet_header *p_header,byte* ts_package)
{
	*(byte *)p_header = *(ts_package + 3);
	*((byte *)(p_header) + 1) = *(ts_package + 2);
	*((byte *)(p_header) + 2) = *(ts_package + 1);
	*((byte *)(p_header) + 3) = *(ts_package);
	
	printf("   Package Source Data: ");
	unsigned char *p = (unsigned char *)p_header;
	while(p < ((unsigned char *)p_header + sizeof(TS_packet_header))){
		printf("%02x ", *p);
		p++;
	}
	printf(" (0x%04x)\n", *(unsigned int *)p_header);

	
	printf("PACKET HEADER:\n");
	printf("   sync_byte(8b):                         0x%02X (%u) %s\n", p_header->sync_byte, p_header->sync_byte, (p_header->sync_byte == 0x47 ? "" : "(sync error)"));
	printf("   transport_error_indicator(1b):         0x%02X (%u) %s\n", p_header->transport_error_indicator, p_header->transport_error_indicator, (p_header->transport_error_indicator == 0x00 ? "" : "(packet error)"));
	printf("   payload_unit_start_indicator(1b):      0x%02X (%u)\n", p_header->payload_unit_start_indicator, p_header->payload_unit_start_indicator);
	printf("   transport_priority(1b):                0x%02X (%u)\n", p_header->transport_priority, p_header->transport_priority);
	printf("   PID(13b):                              0x%04X (%u)\n", p_header->PID, p_header->PID);
	printf("   transport_scrambling_control(2b):      0x%02X (%u) %s\n", p_header->transport_scrambling_control, p_header->transport_scrambling_control, (p_header->transport_scrambling_control == 0x00 ? "(not scrambled)" : "(scrambled)"));
	printf("   adaptation_field_control(2b):          0x%02X (%u) %s\n", p_header->adaptation_field_control, p_header->adaptation_field_control, (p_header->adaptation_field_control == 0x00 ? "(no adap_field, no payload)" : p_header->adaptation_field_control == 0x01 ? "(no adap_field, payload only)" : p_header->adaptation_field_control == 0x10 ? "(adap_field only, no payload)" : "(adap_field followed by payload)"));
	printf("   continuity_counter(4b):                0x%02X (%u)\n", p_header->continuity_counter, p_header->continuity_counter);
}

結果:程序1:錯誤,程序2和3:正確。

原因分析:

主要分析點是程序1和程序3的區別,而程序2是一種繁瑣但能保證正確性的實現方式,所以沒有仔細分析程序2。

1.首先,考慮到可能是由於大小端的造成的,但是仔細分析後發現這是明顯的單字節內數據錯誤問題,而大小端影響的是多字節的排列順序。

2.其次,仔細分析了兩種數據的結構的數據內存佈局,發現是由於處理器定義字節的順序是從右向左的,也就是說低位在最右邊。如下圖所示:

程序1的內存佈局:當結構體位域成員不是字節對齊的時候,就出現了錯誤。


程序3的內存佈局:使用倒序,巧妙地解決了處理器從右向左的訪問順序對程序造成的影響


總結:通過這次的問題,搞明白了3個概念,C語言的位域,大小端,處理器數據訪問順序。也掌握了一種更快速地解析數據包的方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章