【问题标题】:How to convert struct to char array in C如何在C中将struct转换为char数组
【发布时间】:2010-09-30 19:49:46
【问题描述】:

我正在尝试将 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("%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

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

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

【问题讨论】:

    标签: c pointers struct char


    【解决方案1】:

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

    (char*)&自定义包

    转换。对我有用。

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

        • 投反对票并标记,这不是他问题的答案;甚至没有接近答案。如果人们问如何正确地做某事,你真的应该避免灌输他们是否这样做是个好主意,特别是我非常不同意你在第一段中的建议,特别是如果你不知道程序员是什么真的在这里。
        • 我们无法阻止人们朝自己的脚开枪。但是,这里提出了关于字节顺序的一点 ,这就是我拒绝标记的原因。虽然这个答案没有给 OP 他们想要的东西,但保留它是有用的。可能部分不正确不保证删除。如果您认为这是错误的,请使用您的选票。这里没有主持人干预的理由。
        【解决方案4】:

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

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

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

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

            【解决方案6】:

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

            【讨论】:

              【解决方案7】:

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

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

              【讨论】:

                【解决方案8】:

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

                【讨论】:

                  【解决方案9】:

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

                  顺便说一句,您可能想要更改

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

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

                  【讨论】:

                  • char 并不总是签名。 signed char 已签名。 char 的符号取决于编译器。无论如何,char、signed char 和 unsigned char 是三种不同的类型。
                  • "char" 在此上下文中显然是有符号的,因为在将参数传递给堆栈上的 printf 时会发生符号扩展。
                  • dreamlax,确实他的回答很好:) 只是想告诉他们,在另一个系统上,输出很可能是其他的(非负数),因为 char 也可能是无符号的。这取决于编译器。
                  【解决方案10】:

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

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2020-10-11
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2022-11-10
                    相关资源
                    最近更新 更多