如何在C语言中将结构转换为字符数组

如何在C语言中将结构转换为字符数组,c,pointers,struct,char,C,Pointers,Struct,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("

我正在尝试将结构转换为字符数组,以便通过网络发送。然而,当我这样做时,我从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;
}
#包括
结构x
{
int x;
}_uuu属性_uuu((压缩));
int main()
{
结构xa;
a、 x=127;
字符*b=(字符*)&a;
int i;

对于(i=0;ichar是一种有符号类型;因此对于2的补码,0x80是-128,表示8位整数(即一个字节)

char是一种有符号类型,因此您看到的是两个互补表示,强制转换为(unsigned char*)将解决这个问题(Rowland刚刚击败了我)

在旁注上,您可能需要更改

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

<代码> >(i=0;i您可能想转换为一个无符号字符数组。

将结构视为字符数组是未定义行为。要通过网络发送它,使用适当的序列化。这是C++中的一种痛苦,甚至在C中更是如此,但它是唯一的方法,您的应用程序将独立于机器读写。
格式说明符本身表示参数是一个
int
,由于数字是负数,
printf
需要八个字符来显示
int
大小的值的所有四个非零字节。
0
修饰符告诉用零填充输出,而
2
修饰符则告诉输出我建议最小输出长度应为两个字符。据我所知,
printf
没有提供指定最大宽度的方法,字符串除外

现在,您只传递了一个
char
,因此bare
x
告诉函数使用传递的完整
int
——这是由于“
”参数的默认参数提升。请尝试
hh
修饰符,告诉函数将参数视为一个
char

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

您看到的是从char到int的保符号转换。这种行为是由于在您的系统上,char是有符号的(注意:并非所有系统上都有char的符号)。如果位模式使字符的值为负值,则会导致负值。将这样的字符提升为int将保留符号,并且int也将为负值。请注意,即使不放置
(int)
明确地说,编译器将在传递到printf时自动将字符升级为int。解决方案是首先将您的值转换为
无符号字符

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

然后,在使用printf打印时,您不需要任何强制转换。

当您尝试使其与网络无关时,将结构转换为字符或字节会导致问题。为什么不现在解决这个问题呢?您可以使用多种不同的技术,所有这些技术都很可能是非常有用的您的“便携性”比您正在尝试的要高。例如:

  • 在POSIX/Unix世界中,通过函数
    htonl
    htons
    ntohl
    ntohs
    以与机器无关的方式在网络上发送数字数据早已得到处理。例如,请参阅FreeBSD或Linux系统上的手册页
  • 在JSON和本机表单之间转换数据时,与网络传输延迟相比,程序在JSON和本机表单之间转换数据所花费的时间可能微不足道
除非你有非常令人信服的测量结果表明每一个八位元都是宝贵的,不要这样做。使用可读的ASCII协议,如,或IETF编纂的许多其他优秀互联网协议之一


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

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

对齐!这是这里的关键词。这就是为什么你永远不应该尝试将结构当作原始内存。编译器(和各种优化标志)、操作系统和月相都会对“相邻”内存中的实际位置做出奇怪和令人兴奋的事情结构中的字段。例如,如果您有一个结构,其中一个char后跟一个int,那么整个结构将在内存中包含八个字节——char,3个空白的无用字节,然后是int的4个字节。机器喜欢这样做,以便结构可以干净地放在内存页上,诸如此类


在当地大学学习机器体系结构入门课程。同时,正确序列化。不要像对待字符数组一样对待结构。

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

(字符*)和自定义数据包


转换。对我有效。

字符不总是有符号的。有符号字符是有符号的。字符的符号大小取决于编译器。在任何情况下,字符、有符号字符和无符号字符是三种不同的类型。“字符”显然,在这个上下文中是有符号的,因为符号扩展是在参数传递给stack.dreamlax上的printf时发生的,实际上他的答案很好:)只是想告诉他们,在另一个系统上,输出很可能是其他的(非负的),因为char也可能是未签名的。这取决于编译器。Downvote和flagged,这不是他的问题的答案;甚至不接近答案。如果有人问如何正确地做某件事,你真的应该避免灌输他们,如果这样做是好主意还是不好,尤其是我非常不同意你在第一段,特别是如果
for (i=0; i<4; i++)
   printf("%02x ", (unsigned char)b[i]);
unsigned char *b = (unsigned char *)&a;