2

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.

Dimitri Danilov
  • 1,338
  • 1
  • 17
  • 45
  • 6
    You can't in standard C++. – juanchopanza Oct 17 '16 at 09:56
  • PD: https://stackoverflow.com/questions/688471/variable-sized-struct-c Looking for a better one though. – Baum mit Augen Oct 17 '16 at 09:58
  • This works in C ([like this](http://stackoverflow.com/a/19293671/335858)) but not in C++. – Sergey Kalinichenko Oct 17 '16 at 09:59
  • You could use pointer there and allocate the string separately. – Kami Kaze Oct 17 '16 at 10:02
  • 1
    C++ does not support array assignment. You cannot set an array equal to anything. So `packet->data = "kappa";` makes no sense under the rules of C or C++. – David Schwartz Oct 17 '16 at 10:06
  • What the hell!!! what's the intention in writting `reinterpret_cast(new char [sizeof(BabelPacket)])` ???? – Luis Colorado Oct 18 '16 at 09:22
  • @LuisColorado I need to allocate more memory than the size of the structure, because I use a little hack that allows to have an unknown size array at the end of my structure. – Dimitri Danilov Oct 18 '16 at 21:11
  • @DimitriDanilov, then you lead to undefined behaviour, as new is going to call the `char[]` constructor (it doesn't exist but it should call the one for the object type you use in `new`) instead of the one you could define for your `struct`. And no way to call it after. The best approach is to define two structs, one with the hack and other without it (one can be a subclass of the other, so you don't need to reimplement things) and call `new` with the proper argument. Don't think like in C when you are programming C++. – Luis Colorado Oct 19 '16 at 05:31

3 Answers3

3

If this was a C program, the error you get is because you try to assign to an array, which is not possible. You can only copy to an array:

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

Also note that if you want the data to be a C string, you need to allocate an extra character for the string terminator '\0'. Then you can use strcpy instead of memcpy above. Or strncpy to copy at most a specific amount of characters, but then you might need to manually terminate the string.

However, this should not work in C++ at all, unless your compiler have it as an extension.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
0

You can't assign a literal string that way. You'll need to allocate additional memory for the string, then copy to the data pointer.

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");

This solution is only applicable in environments supporting zero-length or flexible arrays. In fact, a better solution may be to write the sockets code in C and export that interface for use in C++.

John Q.
  • 177
  • 4
-1

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.

Nikos Kazazakis
  • 792
  • 5
  • 19