9

我正在尝试将结构转换为 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;
}

这是各种 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

我是在转换过程中忘记做某事,还是忘记了有关整数表示的事情?

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

4

10 回答 10

11

您看到的是从 char 到 int 的符号保留转换。该行为是由于在您的系统上 char 已签名(注意: char 并非在所有系统上都已签名)。如果位模式对 char 产生负值,这将导致负值。将这样的 char 提升为 int 将保留符号,并且 int 也将是负数。请注意,即使您没有(int)显式放置 a,编译器也会在传递给 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 打印时不需要任何演员表。

于 2009-01-11T21:17:41.473 回答
8

char 是有符号类型;所以对于二进制补码,0x80 对于 8 位整数(即一个字节)是 -128

于 2009-01-11T21:02:43.313 回答
8

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

现在,您只传递了 a char,因此 barex告诉函数使用int传递的完整值来代替 - 由于 " ..." 参数的默认参数提升。尝试使用hh修饰符告诉函数将参数视为只是 a char

printf("%02hhx", b[i]);
于 2009-01-11T21:12:47.910 回答
5

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

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

于 2009-01-11T21:07:25.223 回答
2

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

  • 长期以来,在 POSIX/Unix 世界中,通过函数htonlhtons和. 例如,参见 FreeBSD 或 Linux 系统上的byteorder(3)手册页。ntohlntohs
  • 将数据与JSON等完全中立的表示形式相互转换也是完全可以接受的。与网络传输延迟相比,您的程序在 JSON 和原生形式之间转换数据所花费的时间可能相形见绌。
于 2009-01-11T21:41:02.037 回答
1

char 是有符号类型,所以你看到的是两个恭维表示,转换为 (unsigned char*) 将解决这个问题(罗兰打败了我)。

在旁注中,您可能想要更改

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

for (i=0; i<sizeof(x); i++) {
//...
}
于 2009-01-11T21:04:11.500 回答
1

char 数组的符号不是问题的根源!(这是一个问题,但不是唯一的问题。)

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

在当地大学参加机器架构入门课程。同时,正确序列化。切勿将结构视为 char 数组。

于 2010-02-26T05:24:35.933 回答
1

当您发送它时,只需使用:

(char*)&CustomPacket

转换。为我工作。

于 2010-04-11T10:04:01.603 回答
0

您可能希望转换为无符号字符数组。

于 2009-01-11T21:04:32.017 回答
-1

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

如果您确实必须具有二进制格式,那么仅将 struct 中的字节挤出来仍然不安全,因为字节顺序、基本大小或对齐约束可能因主机而异。您必须设计您的有线协议以使用明确定义的大小并使用明确定义的字节顺序。对于您的实现,请使用类似宏ntohl(3)或使用移位和掩码将字节放入流中。无论您做什么,请确保您的代码在大端和小端主机上产生相同的结果。

于 2009-01-13T02:06:17.443 回答