【问题标题】:Copy struct with different type members to buffer将具有不同类型成员的结构复制到缓冲区
【发布时间】:2017-03-10 22:55:16
【问题描述】:

我有一个带有多个不同类型成员的结构。我想将此结构复制到缓冲区,然后连接一条消息以供以后使用(将它们分开并读取结构和消息,我在这里没有这样做)。这是我的代码。

#define DAT "d"
#define ACK "a"
#define SYN "s"
#define FIN "f"
#define RST "r"

typedef struct Headers {        //total 20 bytes
    unsigned char _magic_[7];   
    unsigned char _type_[1];                        
    union {
        unsigned int _seq_;     //4 bytes
        unsigned int _ack_;     //4 bytes
    } no;
    unsigned int _length_;      //4 bytes
    unsigned short _size_;      //2 bytes
} Header;

int main() {
   Header receiver_header;
   char buffer[1024];

   strcpy(receiver_header._magic_, "ABCDEF");
   strcpy(receiver_header._type_, DAT);
   receiver_header.no._ack_ = 0;
   receiver_header._length_ = 900;
   receiver_header._size_ = 10240;
   char foo[] = "A random message";

   memcpy(buffer, &receiver_header, sizeof(Header));
   strcat(buffer, foo);
   printf("%s\n", buffer);
}

输出是

ABCDEFA random message

我的问题是

  1. 是否需要将成员强制转换为相同类型才能将它们复制到缓冲区中?

  2. 为什么我已经声明了正确的源指针和长度,缓冲区中的其余成员却消失了?

【问题讨论】:

  • 即使假设您对shortint 的字节大小的所有假设都是正确的,并且该结构没有填充,您是如何计算20 个字节的?我数了18。
  • 不要依赖于通信协议的实现细节。定义与机器无关的消息格式,并使用正确的编组和位移/屏蔽来序列化数据。其他任何事情都只会带来麻烦。你应该回顾一下 C 书籍中关于 C 字符串和数组的章节。另外:为什么要绕道使用宏来获取字符串文字?为什么不直接使用const 限定变量呢?
  • @StoryTeller 它在填充之后,它发生在 _magic_[7] 和 _type_[1] 之后。它会在每种情况下填充 1 个字节,因此总共是 20 个字节。
  • @Olaf 感谢您的推荐,我会研究一下,看看我如何使用数据序列化,看看我是否可以做到这一点。我没有考虑使用const,因为我不熟悉结构中的使用。我想将 struct 和 string 组合在一起的原因是为了让另一端(接收方或发送方)识别标头。它当然可以在所有字符串中完成,但我想让它更优雅,更易于使用。
  • 我不建议对成员使用const,而是使用原始字符串而不是宏。如果核心语言结构可以做得更好甚至更好,就不要使用宏!

标签: c struct buffer memcpy


【解决方案1】:

Header 的第一个成员 _magic_ 拥有 7 个 chars。您复制了 6 个chars ("ABCDEF"),后跟一个空字符。 strcat 在遇到的第一个空字符处将源字符串复制到目标字符串中。因此,它会在Header 的前六个字节之后附加“随机消息”。如果要在整个结构之后附加消息,则需要指定要复制到的偏移量,如下所示。

strcpy(buffer + sizeof(Header), foo);

但是,它不会打印整个结构,因为空字符仍然在“ABCDEF”之后,即使foo 已附加到正确的偏移量。如果您想打印结构中的所有内容,只需显式打印出每个成员,就像这样。

printf("%s %c %u %u %hu %s",
    receiver_header._magic_,
    receiver_header._type[0],
    reveiver_header.no._ack_,
    receiver_header._length_,
    receiver_header._size_,
    foo);

【讨论】:

  • 感谢strcat行为的解释。所以这意味着如果我去掉最后的空字符,我会在缓冲区内看到d 作为ABCDEF 之后的值?
  • @SpaceWalker 是的,如果您使用_magic_ 6 chars 而不是 7,您会看到“ABCDEFd”打印出来,但仅此而已,因为结构的下一个字节是空字符,因为下一个成员是值为 0 的 int。这将阻止 printf 在此之后打印任何内容。这就是为什么最好单独打印每个成员,这样空字符字节的存在就不会影响输出。
【解决方案2】:
  1. 是否需要将成员强制转换为相同类型才能将它们复制到缓冲区中?

没有。 memcpy() 可以复制任何对象的整个表示。但是您确实还有其他一些明显的误解和陌生感:

  • 如果Header._magic_ 用于保存一个字符串,那么它的元素类型应该是char,而不是unsigned char。另一方面,如果它打算保存二进制数据,那么您不应该使用strcpy() 将数据复制到其中。

  • Header._type_ 有一个字节的空间,但你 strcpy() 一个字符的字符串。计算终止符时,字符串需要两个字节;这些都不适合_type_。可能您应该改为执行单个(无符号)char 的普通分配。

  • 复制到缓冲区的结构表示不是 C 字符串的内容,如果只是因为具有内部空字节。因此,您不能安全地使用strcat() 附加到它。更一般地说,您不应该使用字符串函数来处理一般数据。

  • 尝试像字符串一样打印结构表示也是不合理的。即使没有内部空字节,但有一个终止空字节,你的结构也有数字字段。它们的表示是二进制的,而不是文本的,并且像文本一样打印它们不会产生任何有用的东西。

  1. 为什么我已经声明了正确的源指针和长度,但缓冲区中的其余成员却消失了?

因为当您将消息strcat()ed 到缓冲区时,您覆盖了它们。

【讨论】:

  • 详细信息:A C string 未指定为 char 元素。所有字符串函数都像处理 unsigned char C11 §7.24.1 3 一样处理元素,所以除了向函数传递数据的机制之外,我认为 unsigned char_strings_` 没有其他问题。
  • @chux,字符串函数被定义为将字符串元素视为unsigned char 类型,这确实是一个有趣的细节,但我坚持我的断言。 按照惯例,更不用说函数调用机制了,一个专门用来保存字符串的数组应该被分配元素类型char。该标准没有要求,但这样做会使意图更加清晰。
  • 同意char 更好地传达了 OP 的意图,但 `unsigned char 本身并不是错误的,很好的答案。
猜你喜欢
  • 1970-01-01
  • 2021-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-17
  • 2019-02-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多