Dimitri Danilov Dimitri Danilov - 1 month ago 17
C++ Question

How can I declare a structure of an unknow size?

I have this structure :

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


And to declare it I do :

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


But when I compile I have this error :

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

^


Have you an idea how I can do that ?
And I need to send this structure through a socket, to get the object back in my server, so I can use only C types.

Answer

If you are willing/allowed to change the unsigned char to a regular char, you can use 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;
}

Note that this will only work if data is at the end of the struct, otherwise there is no contiguous memory to allocate data. If I swap the order of the elements to:

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

the output of the code above (instead of "kappa"), would be "a".

A more reliable way if you are determined to use C-arrays would be to assume a maximum number of elements and preallocate the array, i.e.:

#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;
}

This code produces the correct output, and protects you against violations. As you can see, it can get messy pretty quickly, which is why I would recommend to use std::vector for this. The dataLength may then be retrieved automatically as the size of the vector, and you are always protected against overflows.

Comments