各位大神,小弟又有一个关于结构体指针强转的问题想要请教一下

ze_hit 2019-04-18 11:03:54
最近在看《UNIX网络编程》,发现一个问题,始终无法解决,请大神帮忙答疑解惑。

我们知道,通常情况下,结构体是有对齐的需求的,我在win32下使用gcc,int大小是4, char大小是1,默认采用自然对齐,定义了两个结构体和一个字符数组:

typedef struct
{
char a1;
char a2;
char a3;
}A;

typedef struct
{
int b1;
int b2;
}B;

char buff[1024] = {0};

很明显,char是1字节对齐,A是1字节对齐,sizeof(A) = 3, B是4字节对齐. sizeof(B) = 8;

在《UNIX网络编程》一书中,经常有类似于下面的这种做法:

A* a = (A*) buff;

XXXXXXXXX; //对a进行操作

B* b = (B*) (buff +sizeof(A));

b->b1......;
b->b2......; //对b进行操作

我之前对字节对齐的理解是这样的:char*强转成A*没有问题,因为他们都只要求起始地址是1的整数倍,但是强转成B*似乎会出问题。假设buff本身恰好处于4的整数倍的地址(貌似有些系统默认栈内存起始都是4对齐),那么这样一来buff +sizeof(A)就一定不能满足是4的整数倍,从而使得强转后的指针b无法满足B的对齐条件(B是4字节对齐所以B的首地址必须是4 的整数倍),这个时候使用b->b1或者b->b2这样的方式访问数据似乎就会有一些问题。
看到网上有些资料说,这种情况在x86上是允许的,只是会使存取效率降低,而在ARM上会导致错误。我本人之前在ARM上也踩过类似的坑,所以对这个特别关注。不过我感觉Steven大神的代码不应该有问题的,所以有些怀疑自己之前所理解的内容。不知为各位大神对此有什么见解呢?
...全文
107 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
ze_hit 2019-04-19
  • 打赏
  • 举报
回复
引用 7 楼 lin5161678 的回复:
[quote=引用 6 楼 ze_hit 的回复:] [quote=引用 5 楼 lin5161678 的回复:] 你的顾虑的确是合理的 的确可能存在因为不对齐造成错误的可能性 所以 那段代码 要么就是通过其他手法设置了对齐 比如pack(4) 而你没注意 要么就是只能部署到特定平台
谢谢您的回答,我还有一个问题想请教一下。如果上面的代码中,假设buff是发送缓冲区,我对B结构体指针不使用强制转换,而是采用以下方式: B bs; bs.b1... ; bs.b2...; //填写和修改bs memcpy(buff + sizeof(A), &bs, sizeof(B)); XXXXXXXX buff; //将buff发送给接收方 而在接收方,也不采用直接强制转换的方式取出结构体的内容,而是采用下面的方式: char recv[1024] = {0}; //定义接收缓冲区 XXXXXXXX recv; // 将数据接收到缓冲区中 B rs; memcpy(&B, recv + sizeof(A), sizeof(B)); rs.b1...; rs.b2...; //读取接收到的B结构体内容 使用这种方式是不是就可以从根本上解决我之前提出的问题,从而实现无需指定对齐方式的情况下跨平台移植呢?[/quote] 单纯讨论 写入缓冲区 和 从缓冲区读数据 的确从解决之前的问题(内存没对齐) 实现跨平台 但需要注意 因为跨平台 你发送和接受的环境中 sizeof(A) 不保证一样大 同理sizeof(B) 也不保证一样大 略有瑕疵[/quote] 谢谢您,是我表述有误,我本意是指发送和接收方具有相同平台,而这段代码在不同平台上跑。
lin5161678 2019-04-19
  • 打赏
  • 举报
回复
引用 6 楼 ze_hit 的回复:
[quote=引用 5 楼 lin5161678 的回复:] 你的顾虑的确是合理的 的确可能存在因为不对齐造成错误的可能性 所以 那段代码 要么就是通过其他手法设置了对齐 比如pack(4) 而你没注意 要么就是只能部署到特定平台
谢谢您的回答,我还有一个问题想请教一下。如果上面的代码中,假设buff是发送缓冲区,我对B结构体指针不使用强制转换,而是采用以下方式: B bs; bs.b1... ; bs.b2...; //填写和修改bs memcpy(buff + sizeof(A), &bs, sizeof(B)); XXXXXXXX buff; //将buff发送给接收方 而在接收方,也不采用直接强制转换的方式取出结构体的内容,而是采用下面的方式: char recv[1024] = {0}; //定义接收缓冲区 XXXXXXXX recv; // 将数据接收到缓冲区中 B rs; memcpy(&B, recv + sizeof(A), sizeof(B)); rs.b1...; rs.b2...; //读取接收到的B结构体内容 使用这种方式是不是就可以从根本上解决我之前提出的问题,从而实现无需指定对齐方式的情况下跨平台移植呢?[/quote] 单纯讨论 写入缓冲区 和 从缓冲区读数据 的确从解决之前的问题(内存没对齐) 实现跨平台 但需要注意 因为跨平台 你发送和接受的环境中 sizeof(A) 不保证一样大 同理sizeof(B) 也不保证一样大 略有瑕疵
ze_hit 2019-04-19
  • 打赏
  • 举报
回复
引用 5 楼 lin5161678 的回复:
你的顾虑的确是合理的 的确可能存在因为不对齐造成错误的可能性 所以 那段代码 要么就是通过其他手法设置了对齐 比如pack(4) 而你没注意 要么就是只能部署到特定平台
谢谢您的回答,我还有一个问题想请教一下。如果上面的代码中,假设buff是发送缓冲区,我对B结构体指针不使用强制转换,而是采用以下方式: B bs; bs.b1... ; bs.b2...; //填写和修改bs memcpy(buff + sizeof(A), &bs, sizeof(B)); XXXXXXXX buff; //将buff发送给接收方 而在接收方,也不采用直接强制转换的方式取出结构体的内容,而是采用下面的方式: char recv[1024] = {0}; //定义接收缓冲区 XXXXXXXX recv; // 将数据接收到缓冲区中 B rs; memcpy(&B, recv + sizeof(A), sizeof(B)); rs.b1...; rs.b2...; //读取接收到的B结构体内容 使用这种方式是不是就可以从根本上解决我之前提出的问题,从而实现无需指定对齐方式的情况下跨平台移植呢?
lin5161678 2019-04-19
  • 打赏
  • 举报
回复
你的顾虑的确是合理的 的确可能存在因为不对齐造成错误的可能性 所以 那段代码 要么就是通过其他手法设置了对齐 比如pack(4) 而你没注意 要么就是只能部署到特定平台
AlbertS 2019-04-19
  • 打赏
  • 举报
回复
`B* b = (B*) (buff +sizeof(A));`感觉这种写法与`B* b = p`这个写法并没有什么不同,个人理解是4字节对齐可能是快一点,但是这种告诉你一个不是4字节对齐的地址,也会按照字节序得到想要数据的
ze_hit 2019-04-19
  • 打赏
  • 举报
回复
引用 1 楼 636f6c696e 的回复:
这样访问根本不会有问题,只是这么写的话对性能会有点影响罢了。 如果要避免这种问题,直接用4字节对齐的方式不就好了?
在x86上我试过,确实不会出错,但是好像ARM和SPARC非对齐访问是不允许的吧!
自信男孩 2019-04-19
  • 打赏
  • 举报
回复
可以对A强制4字节对齐,pack(4)
636f6c696e 2019-04-19
  • 打赏
  • 举报
回复
这样访问根本不会有问题,只是这么写的话对性能会有点影响罢了。 如果要避免这种问题,直接用4字节对齐的方式不就好了?

69,377

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧