如何在C中将struct转换为char数组

我正在尝试将结构转换为char数组以通过网络发送。 但是,当我这样做时,我从char数组得到了一些奇怪的输出。

#include  struct x { int x; } __attribute__((packed)); int main() { struct xa; ax=127; char *b = (char *)&a; int i; for (i=0; i<4; i++) printf("%02x ", b[i]); printf("\n"); for (i=0; i<4; i++) printf("%d ", b[i]); printf("\n"); return 0; } 

这是各种ax值的输出(在使用gcc的X86上):
127:
7f 00 00 00
127 0 0 0

128:
ffffff80 00 00 00
-128 0 0 0

255:
ffffffff 00 00 00
-1 0 0 0

256:
00 01 00 00
0 1 0 0

我理解127和256的值,但为什么数字在转到128时会改变? 为什么不会这样:80 00 00 00 128 0 0 0

我忘记在转换过程中做某事或者我忘记了整数表示的某些事情?

*注意:这只是一个小测试程序。 在一个真正的程序中,我在结构中有更多,更好的变量名,我转换为little-endian。
*编辑:格式化

x格式说明符本身表示参数是一个int ,并且由于该数字是负数,因此printf需要八个字符来显示int -sized值的所有四个非零字节。 0修饰符告诉用零填充输出, 2修饰符表示最小输出应该是两个字符长。 据我所知, printf没有提供指定最大宽度的方法,除了字符串。

现在,你只传递一个char ,所以裸x告诉函数使用传递的完整int – 由于“ ... ”参数的默认参数提升。 尝试使用hh修饰符告诉函数将参数视为char而不是:

 printf("%02hhx", b[i]); 

你看到的是保持从char转换为int的符号。 该行为是由于在您的系统上char已签名( 注意: char未在所有系统上签名)。 如果位模式产生char的负值,那将导致负值。 将这样的char提升为int将保留符号,而int也将是负数。 请注意,即使您没有显式地放置(int) ,编译器也会在传递给printf时自动将字符提升为int。 解决方案是首先将您的值转换为unsigned char

 for (i=0; i<4; i++) printf("%02x ", (unsigned char)b[i]); 

或者,您可以从一开始就使用unsigned char*

 unsigned char *b = (unsigned char *)&a; 

然后在使用printf打印时不需要任何演员表。

char是签名类型; 所以使用二进制补码,对于一个8位整数(即一个字节),0x80为-128

将结构视为char数组是未定义的行为。 要通过网络发送,请使用正确的序列化。 这是C ++的痛苦,在C中更是如此,但它是你的应用程序独立于机器读写的唯一方式。

http://en.wikipedia.org/wiki/Serialization#C

以您的方式将结构转换为字符或字节,当您尝试使其网络中立时,将导致问题。 为什么不现在解决这个问题呢? 您可以使用各种不同的技术,所有这些技术都可能比您尝试的更“便携”。 例如:

  • 在POSIX / Unix世界中,通过函数htonlhtonsntohlntohs长期以来以机器中立的方式在网络上发送数字数据。 例如,请参阅FreeBSD或Linux系统上的byteorder(3)手册页。
  • 将数据转换为完全中性的表示forms(如JSON )也是完全可以接受的。 与网络传输延迟相比,您的程序在JSON和本机表单之间转换数据所花费的时间可能会很少。

char是一个签名类型,所以你看到的是两个赞美表示,转换为(unsigned char *)将解决这个问题(Rowland只是打败了我)。

另外,您可能想要更改

 for (i=0; i<4; i++) { //... } 

 for (i=0; i 

char数组的签名不是问题的根源! (这是问题,但不是唯一的问题。)

对准! 这是关键词。 这就是为什么你永远不应该试图像原始记忆一样对待结构。 编译器(和各种优化标志),操作系统和月亮阶段都对结构中“相邻”字段的存储器中的实际位置做了奇怪和令人兴奋的事情。 例如,如果你有一个带有char后跟一个int的结构,那么整个结构将是内存中的EIGHT字节 – char,3个空白,无用的字节,然后是int的4个字节。 机器喜欢做这样的事情,所以结构可以很好地适应内存页面等等。

在当地大学学习机械建筑的入门课程。 同时,正确序列化。 永远不要像char数组那样处理结构。

当你去发送它时,只需使用:

(字符*)&CustomPacket

转换。 适合我。

您可能希望转换为unsigned char数组。

除非你有非常令人信服的测量表明每个八位字节都是珍贵的, 否则不要这样做 。 使用可读的ASCII协议,如SMTP , NNTP或IETF编写的许多其他精细Internet协议之一。

如果你真的必须有二进制格式,那么仅仅推断结构中的字节仍然是不安全的,因为字节顺序,基本大小或对齐约束可能因主机而异。 您必须将wire protcol设计为使用定义良好的大小并使用定义良好的字节顺序。 对于您的实现,要么使用像ntohl(3)这样的宏,要么使用移位和屏蔽将字节放入流中。 无论您做什么,请确保您的代码在big-endian和little-endian主机上产生相同的结果。