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

时间:2009-01-11 20:58:57

标签: c pointers struct char

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

#include <stdio.h>

struct x
{
   int x;
} __attribute__((packed));


int main()
{
   struct x a;
   a.x=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;
}

这是a.x的各种值的输出(在使用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。
*编辑:格式化

10 个答案:

答案 0 :(得分:11)

您看到的是保留从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打印时不需要任何演员表。

答案 1 :(得分:8)

char是签名类型;因此,对于2位补码,对于8位整数(即一个字节),0x80为-128

答案 2 :(得分:8)

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

现在,您只传递char,因此裸x告诉函数使用已传递的完整int - 由于默认参数提升为“{ {1}}“参数。尝试...修饰符告诉函数将参数视为hh而不是:

char

答案 3 :(得分:5)

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

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

答案 4 :(得分:2)

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

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

答案 5 :(得分:1)

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

在旁注中您可能想要更改

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

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

答案 6 :(得分:1)

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

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

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

答案 7 :(得分:1)

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

(字符*)及CustomPacket

转换。适合我。

答案 8 :(得分:0)

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

答案 9 :(得分:-1)

除非您非常令人信服的测量结果显示每个八位字节都很珍贵,否则不要这样做。使用可读的ASCII协议,如SMTPNNTP,或IETF编制的许多其他精细互联网协议之一。

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

相关问题