首页 > 其他分享 >由字节对齐引发的一场“血案“

由字节对齐引发的一场“血案“

时间:2024-08-17 11:19:08浏览次数:3  
标签:字节 int 血案 UINT8 len pmsg frm 对齐

最近在搞个网络通信协议,

采用socket udp传输,

运行时,居然报段错误了,

经过debug,发现居然是因为字节对齐问题导致的。

这个问题在实现通信协议,是经常会遇到的问题,

为了方便读者理解,

我把内容做了简化,分享给大家。

1、协议说明

通信协议信令格式如下:

typedef struct protocol_msg_s{
	UINT8 msgType;
	UINT8 data1;
	UINT8 data2;
	UINT16 len;
	char data[100];
}PRO_MSG;

根据协议格式,我造了一个数据frm,代表我收到的某个信令,

	UCHAR frm[]={0x12,0x34,0x56,0x00,0x07,0x01,0x02,0x03,0x04,0x05,0x06,0x07};

根据协议,

信令的字段与原始帧对应关系如下

于是我实现了一个简单的解析代码【该代码有问题】

int main(int argc, char **argv)
{
	int ret;
	int frm_len = 0;
	UINT8 frm[]={0x12,0x34,0x56,0x00,0x07,0x01,0x02,0x03,0x04,0x05,0x06,0x07};


	PRO_MSG *pmsg = (PRO_MSG *)frm;

	printf("devType:%02x data1:%02x data2:%02x len:%04x \n",
			pmsg->msgType,
			pmsg->data1,
			pmsg->data2,
			pmsg->len);
}

编译运行后,其中len的值居然是0107,而不是0007

这其实就是因为编译器采用了字节对齐导致的,

在给pmsg->len赋值时,因为需要2个字节,

这两个字节是frm[3]、frm[4]
这正好分布在两个字里,

编译器忽略了frm[3],最终将frm[4]、frm[5]合在一起赋值给了pmsg->len

为什么有字节对齐?

简单的说内存对齐能够提高 cpu 读取数据的速度,减少 cpu 访问数据的出错性(有些 cpu 必须内存对齐,否则指针访问会出错)。

原因找到了,下面就是解决了。

2、解决办法

1. 方法1 #pragma pack()

该预处理指令用来改变对齐参数。在缺省情况下,C编译器为每一个变量或数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对齐参数:

使用伪指令#pragma pack (n),C编译器将按照n字节对齐。

使用伪指令#pragma pack (),取消自定义字节对齐方式。

完整代码

#include <stdio.h>
#include <string.h>
typedef unsigned char UINT8;
typedef unsigned short UINT16;

#define MAX_FRM_DATA_LEN 100
#pragma pack(1)
typedef struct protocol_msg_s{
	UINT8 msgType;
	UINT8 data1;
	UINT8 data2;
	UINT16 len;
	char data[MAX_FRM_DATA_LEN];
}PRO_MSG;
#pragma
int main(int argc, char **argv)
{
	int ret;
	int frm_len = 0;
	UINT8 frm[]={0x12,0x34,0x56,0x00,0x07,0x01,0x02,0x03,0x04,0x05,0x06,0x07};

	PRO_MSG *pmsg = (PRO_MSG *)frm;

	printf("devType:%02x data1:%02x data2:%02x len:%04x \n",
			pmsg->msgType,
			pmsg->data1,
			pmsg->data2,
			pmsg->len);
}

2. 方法2

老老实实将收到的数据帧逐字节解析,

并填充到struct protocol_msg_s

#include <stdio.h>
#include <string.h>
typedef unsigned char UINT8;
typedef unsigned short UINT16;

#define MAX_FRM_DATA_LEN 100

typedef struct protocol_msg_s{
	UINT8 msgType;
	UINT8 data1;
	UINT8 data2;
	UINT16 len;
	char data[MAX_FRM_DATA_LEN];
}PRO_MSG;

int frm_parse(PRO_MSG *pmsg,UINT8 buf[],int len)
{
	int pos=0;
	pmsg->msgType = buf[pos];
	pos++;
	pmsg->data1 = buf[pos];
	pos++;	
	pmsg->data2 = buf[pos];
	pos++;
	pmsg->len = buf[pos]<<8 | buf[pos+1]<<0;
	pos+=2;	

	if(pmsg->len>MAX_FRM_DATA_LEN)
	{
		printf("frm len is longer than 100\n");
		return -1;
	}
	memcpy(pmsg->data,&buf[pos],pmsg->len);
	return 0;
}
int maini(int argc, char **argv)
{
	int ret;
	int frm_len = 0;
	UINT8 frm[]={0x12,0x34,0x56,0x00,0x07,0x01,0x02,0x03,0x04,0x05,0x06,0x07};



	PRO_MSG msg;
	PRO_MSG *pmsg = &msg;

	frm_len = sizeof(frm);
	ret = frm_parse(pmsg,frm,frm_len);
	if(ret<0)
	{
		printf("frm_parse fail\n");
		return -1;
	}

	printf("devType:%02x data1:%02x data2:%02x len:%04x \n",
			pmsg->msgType,
			pmsg->data1,
			pmsg->data2,
			pmsg->len);
	return 0;
}


标签:字节,int,血案,UINT8,len,pmsg,frm,对齐
From: https://www.cnblogs.com/yikoulinux/p/18364150

相关文章

  • 字节缓冲输出流BufferedOutputStream day17
    packagecom.shujia.day17.ketang;importjava.io.BufferedOutputStream;importjava.io.FileOutputStream;/*java针对字节输入流和字节输出流都提供了相应的缓冲流来提高读写的速度。字节流:输入流:InputStream-FileInput......
  • 字节缓冲输入流BufferedInputStream day17
    packagecom.shujia.day17.ketang;importjava.io.BufferedInputStream;importjava.io.FileInputStream;/*字节缓冲输入流:BufferedInputStream构造方法:BufferedInputStream(InputStreamin)创建一个BufferedInputStream并保存其参数,输入流in......
  • C++八股文——内存管理(堆和栈的区别? C++内存分区? 内存泄漏?如何避免?什么是智能指针?有哪
    文章目录C++内存管理堆和栈的区别C++内存分区内存泄漏?如何避免?1、什么是内存泄露?2、内存泄漏的分类3、什么操作会导致内存泄露?4、如何防⽌内存泄露?5、智能指针有了解哪些?6、构造函数,析构函数要设为虚函数吗,为什么?什么是智能指针?有哪些种类?new和malloc有什么区别?d......
  • 字节输入流FileInputStream
    packagecom.shujia.day16.ketang;importjava.io.File;importjava.io.FileInputStream;/*字节输入流:FileInputStream构造方法:FileInputStream(Filefile)通过打开与实际文件的连接创建一个FileInputStream,该文件由文件系统中的File对象file命......
  • C语言 ——— 结构体内存对齐
    目录发现问题 偏移量宏:offsetof()结构体内存的对齐规则小结 发现问题有以下两个结构体:结构体1:structS1{ charc1;//1字节 inti;//4字节 charc2;//1字节};结构体2:structS2{ charc1;//1字节 charc2;//1字节 inti;//4字节};通常情况下......
  • HexView 刷写文件脚本处理工具-命令行介绍(一)-数据对齐(/Adxx或/AD:yy)
    数据对齐(/Adxx或/AD:yy)每个块的起始地址将被对齐到给定参数xx的倍数。如果省略分隔符‘:’或‘=’,则参数xx被解释为十六进制值。如果使用了分隔符,则值xx以C风格进行解释,例如/AD:0xFF与/AD:255或/AD:11111111b相同。这个值只能是无符号字符值。示例说明......
  • HexView 刷写文件脚本处理工具-命令行介绍(二)-对齐长度(/AL[:length])
    对齐长度(/AL[:length])这个选项与/AD参数结合使用非常有用。它也将所有块的长度对齐,使其成为/Adxx选项中给定参数的倍数。示例说明:/AD4/AL如果有一个地址范围从0xE432到0xE47E的块,它将被对齐到0xE430到0xE47F。所有的字符将被填充为0xFF,或者被/Afxx指......
  • C语言结构体内存对齐
    结构体或许小伙伴们都知道,或许也能够做到熟悉的去运用结构体,但你们有没有想过:整型数组存放的数据都是整型,字符数组存放的数据都是字符,它们类型相同,所以也都能够做到在内存中紧密的存储,而结构体中存放的数据各种各样,它们的存储是否能做到在内存中紧密排列呢?又或者说,结构体的内存......
  • 科普文:Java基础系列之【java框架基础:字节码增强技术框架ASM】
    ,之前的文章我们介绍了字节码的基础知识,今天我们将介绍字节码相关的应用场景,首先要介绍的是如何对字节码做解析和修改,本文将会详细给大家介绍一个工业级字节码操作框架ASM。ASM当我们需要对一个class文件做修改时,我们可以选择自己解析这个class文件,在符合Java字节码规......
  • 科普文:Java基础系列之【java框架基础:字节码增强技术框架ASM#ClassReader实现原理及源
    1概述ASM是Java中比较流行的用来读写字节码的类库,用来基于字节码层面对代码进行分析和转换。在读写的过程中可以加入自定义的逻辑以增强或修改原来已编译好的字节码,比如CGLIB用它来实现动态代理。ASM被设计用于在运行时对Java类进行生成和转换,当然也包括离线处理。ASM短小精......