【问题标题】:How can I declare a structure of an unknow size?如何声明一个未知大小的结构?
【发布时间】:2016-10-17 09:56:16
【问题描述】:

我有这个结构:

  struct          __attribute__((packed)) BabelPacket
  {
    unsigned      senderId;
    unsigned      dataLength;
    unsigned char data[0];
  };

我要声明它:

  BabelPacket *packet = reinterpret_cast<BabelPacket *>(new char[sizeof(BabelPacket) + 5]);
  packet->senderId = 1;
  packet->data = "kappa";
  packet->dataLength = 5;

但是当我编译时出现这个错误:

error: incompatible types in assignment of ‘const char [6]’ to ‘unsigned char [0]’
   packet->data = "kappa";

            ^

你知道我该怎么做吗? 而且我需要通过套接字发送这个结构,以便将对象返回到我的服务器中,所以我只能使用 C 类型。

【问题讨论】:

  • 你不能在标准 C++ 中。
  • 这适用于 C (like this) 但不适用于 C++。
  • 你可以在那里使用指针并单独分配字符串。
  • C++ 不支持数组赋值。您不能将数组设置为等于任何值。所以packet-&gt;data = "kappa"; 在 C 或 C++ 的规则下毫无意义。

标签: c++


【解决方案1】:

如果这是一个 C 程序,你得到的错误是因为你试图分配一个数组,这是不可能的。你只能复制到一个数组:

memcpy(packet->data, "kappa", 5);

还要注意,如果你希望数据是一个C字符串,你需要为字符串终止符'\0'分配一个额外的字符。然后你可以使用strcpy 而不是上面的memcpy。或strncpy 最多复制特定数量的字符,但您可能需要手动终止字符串。

但是,这根本不应该在 C++ 中工作,除非您的编译器将它作为扩展。

【讨论】:

  • 更好地为strcpy提供更安全的替代方案。
  • 我想你的意思是memcpy(packet-&gt;data, "kappa", 5);
【解决方案2】:

您不能以这种方式分配文字字符串。您需要为字符串分配额外的内存,然后复制到数据指针。

struct A {
    size_t datasize;
    char data[0]; // flexible member must appear last.
};

A* create_A(const char* str)
{
    size_t datasize = strlen(str) + 1; // null terminated (?)
    A* p = reinterpret_cast<A*>(new char[sizeof(A) + datasize]);
    memcpy(p->data, str, datasize);
    p->datasize = datasize;
    return p;
}

A* p = create_A("data string");

此解决方案仅适用于支持零长度或灵活数组的环境。事实上,更好的解决方案可能是用 C 编写套接字代码,然后导出该接口以在 C++ 中使用。

【讨论】:

  • char data[0]; 这在 C++ 中是不允许的。
  • @juanchopanza 考虑到他正在使用属性,可以安全地假设他正在使用支持它作为扩展的 GCC。
  • 这取决于编译器标志。
  • 没有人能阻止你写出完整、准确的答案。
  • @juanchopanza 这很有趣,因为这是唯一正确的答案。接受的答案只分配 5 个字节,然后使用 strcpy 复制 6 个字节。另一个答案是 memcpy 的操作数乱序。实际上,我缺少逗号和分号。如此精确。
【解决方案3】:

如果您愿意/允许将unsigned char 更改为常规char,您可以使用strcpy

#include <iostream>
#include <stdio.h>
#include <string.h>

struct          __attribute__((packed)) BabelPacket
{
  unsigned      senderId;
  unsigned      dataLength;
  char data[0]; // I changed this to char in order to use strcpy
};

int main(){
  BabelPacket *packet = reinterpret_cast<BabelPacket *>(new char[sizeof(BabelPacket) + 5]);
  packet->senderId = 1;
  // Copy the string. Add NULL character at the end of 
  // the string to indicate its end
  strcpy(packet->data, "kappa\0"); 
  packet->dataLength = 5;

  // Verify that the string is copied properly
  for (int i=0;i<packet->dataLength;++i){
    std::cout<<packet->data[i];
  }
  std::cout<<std::endl;

  return 0;
}

请注意,这仅在data 位于struct 的末尾时才有效,否则没有连续的内存可分配data。如果我将元素的顺序交换为:

struct          __attribute__((packed)) BabelPacket
{
  unsigned      senderId;
  char data[0]; // I changed this to char in order to use strcpy
  unsigned      dataLength;
};

上述代码的输出(而不是“kappa”)将是“a”。

如果您决定使用 C 数组,一种更可靠的方法是假设最大数量的元素并预分配数组,即:

#include <iostream>
#include <stdio.h>
#include <string.h>

#define MAX_NUMBER_OF_CHARACTERS 5 // Many ways to do this, I defined the macro for the purposes of this example

struct          __attribute__((packed)) BabelPacket
{
  unsigned      senderId;
  // I changed this to char in order to use strcpy. Allocate the
  // max number + 1 element for the termination string
  char data[MAX_NUMBER_OF_CHARACTERS+1]; 
  unsigned      dataLength;
};

int main(){
  BabelPacket *packet = reinterpret_cast<BabelPacket *>(new char[sizeof(BabelPacket) + 5]);
  packet->senderId = 1;
  packet->dataLength = 5;
  if (dataLength>MAX_NUMBER_OF_CHARACTERS){
    std::cout<<"String greater than the maximum number of characters"<<std::endl;
  }
  // Copy the string. Add NULL character at the end of 
  // the string to indicate its end
  strcpy(packet->data, "kappa\0");  

  // Verify that the string is copied properly
  for (int i=0;i<packet->dataLength;++i){
    std::cout<<packet->data[i];
  }
  std::cout<<std::endl;

  return 0;
}

此代码产生正确的输出,并保护您免受违规行为。如您所见,它很快就会变得一团糟,这就是为什么我建议为此使用std::vector。然后,dataLength 可能会被自动检索为向量的大小,并且您始终可以防止溢出。

【讨论】:

  • char data[0]; 这在 C++ 中是不允许的。
  • @juanchopanza 我使用 g++ 5.4 并且工作正常。
  • 同样,它也适用于我的 clang 3.8。
  • @DimitriDanilov 这并不意味着它是标准 C++。
  • 您应该解释分配被黑客攻击的方式是在结构的末尾附加可用空间,如果数组不是最后一个字段,则该技巧将失败。而且您忘记为字符串终止符分配一个额外的字节。
猜你喜欢
  • 2021-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多