How can I declare a structure of an unknow size? - c++

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.

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.

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++.

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.

Related

A struct that contains an array of char arrays

I'm trying to define an instance of a struct, and am having particular trouble with setting this one variable. It's an array of char arrays.
Here is my struct in my header file...
struct widget_t {
char *name;
uint8_t numberOfNicknames;
char *nicknames[];
};
And here's me attempting to set up the instance of the widget_t struct...
widget_t SomeWidget;
void setUpFunction () {
SomeWidget.name = (char *)"Lawn Mower";
SomeWidget.numberOfNicknames = 2;
SomeWidget.nicknames = {
"Choppie McGrasschopper",
"Really Noisy"
};
}
So, the error is happening when I try to put the nicknames into SomeWidget.nicknames. I'm not sure if I need to do something funky like I'm doing with name being a pointer...?
The tricky bit is that the number of nicknames is variable. So each instance will want to set up a different number of them.
The problem you have, is that c++ does not support variable arrays. Instead you will have to allocate memory dynamically using new or in your case new[].
First you need to change your data type to char**, almost equaliant to the one before. Then you can allocate as many strings you want like this nicknames = new char*[number_of_nicknames].
Important is that with this method you will have to delte your nicknames manually like this: delete[] nicknames;. The best way to accomplish this is using RAII (delete your nicknames in your deconstructor)
When you have dynamic strings then you would use the following structure
struct widget_t {
// optional constructor to allocate nicknames
~widget_t()
{
for (int i = 0; i < numberOfNicknames; ++i)
{
char* nickname = nicknames[i];
if (nickname)
delete[] nickname;
}
delete[] nicknames;
}
char *name;
uint8_t numberOfNicknames;
char **nicknames = NULL;
};
and with constant string the next
struct widget_t {
// optional constructor to allocate nicknames
// allocate nicknames like
// -> nicknames = new const char*[numberOfNicknames];
~widget_t()
{
if (nicknames) delete[] nicknames;
}
char *name;
uint8_t numberOfNicknames;
const char **nicknames = NULL;
};
One option would be:
struct widget_t {
char const *name;
uint8_t numberOfNicknames;
char const * const *nicknames;
};
static char const *mower_nicknames[] = { "Choppie", "Bob" };
widget_t SomeWidget = { "Lawn Mower", 2, mower_nicknames };
static char const *bus_nicknames[] = { "Wheels", "Go", "Round" };
widget_t OtherWidget = { "Bus", 3, bus_nicknames };
// no setup function needed
What you are trying to do is to assign a string literal to a char * pointer -- that is bad (see more at How to get rid of deprecated conversion from string constant to ‘char*’ warnings in GCC?). Here is an possible approach:
#define MAX_NAME_LEN 128
#define MAX_NICK_NAMES 10
struct widget_t {
char name[MAX_NAME_LEN];
uint8_t numberOfNicknames;
char nicknames[MAX_NICK_NAMES][MAX_NAME_LEN];
};
widget_t SomeWidget;
void setUpFunction () {
strcpy(SomeWidget.name, "Lawn Mower");
SomeWidget.numberOfNicknames = 2;
strcpy(SomeWidget.nicknames[0], "Choppie McGrasschopper");
strcpy(SomeWidget.nicknames[1], "Really Noisy");
}
Anyway, since you tag your question with C++ I would suggest you to use std::string and std::vector instead.
There are different problems here.
First your last member in of an incomplete type because it is an array of undeclared dimension. This is not allowed in C++ but most compilers allows it as an extension with same semantics as C. This is rather tricky to use anyway, because it can only be used with allocated structs, where you allocate memory for the struct itself and the incomplete array.
Next, you are trying to assign to a array. You cannot. Arrays are not first class objects in C++ (nor in C). You can initialize an array as a whole, but can only assign to an array element.
And last, you are assigning a C litteral string to a char *. This is bad, because the standard declares that litteral strings are const so using later the pointer to change a char would be Undefined Behaviour. It will work if you do not, but the pointers should at least be declared as const.
Here is how you could use all that:
widget_t* setUpFunction () {
// allocates a widget_t with 2 slots in nicknames
widget_t *someWidget = (widget_t *) malloc(sizeof(widget_t) + 2 * sizeof(char *));
someWidget.name = (char *)"Lawn Mower"; // VERY DANGEROUS: pointer should be const
someWidget.numberOfNicknames = 2;
someWidget.nicknames[0] = (char *) "Choppie McGrasschopper"; // SAME DANGER
someWidget.nicknames[1] = (char *) "Really Noisy" // Still same danger
return widget_t;
}
But all this is rather C-ish and should be avoided in C++. In addition, it still requires allocated memory, which may not be what you want for Arduino

Copying struct with bitfields & dynamic data into a Char array buffer

I have a struct like the following
struct Struct {
int length; //dynamicTest length
unsigned int b: 1;
unsigned int a: 1;
unsigned int padding: 10;
int* dynamicTest;
int flag;
}
I want to copy this into a char array buffer (to send over a socket). I'm curious how I would do that.
To be precise, you do this with memcpy, e.g.:
#include <string.h>
/* ... */
Struct s = /*... */;
char buf[1024]
memcpy(buf, &s, sizeof(s));
/* now [buf, buf + sizeof(s)) holds the needed data */
Alternatively you can avoid copying at all and view an instance of struct as an array of char (since everything in computer memory is sequence of bytes, this approach works).
Struct s = /* ... */;
const char* buf = (char*)(&s);
/* now [buf, buf + sizeof(s)) holds the needed data */
If you are going to send it over the network, you need to care of byte order, int size and many other details.
Copying bit fields present no problem, but for dynamic fields, such as your char* this naive approach won't work. The more general solution, that works with any other types is serialization.

Function to copy data in a reference char array

How can I write in C++ a function who put the data in a char array, I don't how to explain well but I'll write an example
void functionCopyData(char& buf){
// get data from a char variable
char data1 = "textdata1";
char data2 = "textdata2"
// copy data
copy(&buf,data1);
copy(&buf,data2);
}
char variable[20];
functionCopyData(&variable);
printf(variable);
// return: textdata1textdata2
So summarizing, I need to copy the data from two variables and put in a char array. Sorry if I was not clear enough.
Why this way?
because I need to get the data from and a variable and send it in a function request a reference data sendFunction(&data,sizeVar);
how can I do that?
How can I write in C++ a function who put the data in a char array
Change the argument type.
void functionCopyData(char& buf){
will be good if you want to update just one character, not an array of characters. To update an array of characters, use:
void functionCopyData(char buf[]){
or
void functionCopyData(char* buf){
That could easily lead to problems if buf does not have enough memory to store the data. To make function more robust, pass a number that indicates the capacity of buf.
void functionCopyData(char buf[], size_t capacity){
Fix the definitions of data1 and data2.
char data1 = "textdata1";
char data2 = "textdata2"
are not syntactically valid. Change them to:
char data1[] = "textdata1";
char data2[] = "textdata2";
or
char const* data1 = "textdata1";
char const* data2 = "textdata2";
Make sure that buf can hold data1 and data2.
// You need the additional 1 for the terminating null
// character.
size_t capacityNeeded = strlen(data1) + strlen(data2) + 1;
if ( capacityNeeded > capacity )
{
// Deal with the problem.
}
else
{
// Now copy the contents of data1 and data2 to buf
}
Copy the contents data1 and data2 to buf using standard library functions.
strcpy(buf, data1);
strcat(buf, data2);
Fix the call to the function.
functionCopyData(&variable);
is not the right syntax to use to call the function, given the function declaration. Use:
functionCopyData(variable, 20);
An easy solution is to write a function to copy one array, and then call it twice with different pointers pointing to different positions in the result array.
For example, if you have char data[20] to hold the result, and want to copy two arrays char a[10] and char b[10], then write a function
void copy(char* destination, char* source, int length)
That copies length bytes from source to destination, then call it with
copy(data, a, 10);
copy(data+10, b, 10);
The functions strcpy and strlen can help you:
#include <string.h>
void functionCopyData(char buf[]){
char data1[] = "textdata1";
char data2[] = "textdata2";
strcpy(buf, data1);
strcpy(buf+strlen(buf), data2);
}
...
functionCopyData(variable);
...
void functionCopyData(char* buf){
// get data from a char variable
char* data1 = "textdata1";
char* data2 = "textdata2"
// copy data
copy(buf,data1);
copy(buf,data2);
}
char variable[20];
functionCopyData(variable);
printf(variable);
I think it's more clear use pointer but not reference.

Simple serialization example in c++

I have the following struct:
typedef struct{
int test;
std::string name;
} test_struct;
Then, I have the following code in the main function:
int main(int argc, char *argv[]){
test_struct tstruct;
tstruct.test = 1;
tstruct.name = "asdfasdf";
char *testout;
int len;
testout = new char[sizeof(test_struct)];
memcpy(testout, &tstruct, sizeof(test_struct) );
std::cout<< testout;
}
However, nothing gets printed. What's wrong?
sizeof(std::string) yeilds same value always. It will not give you the runtime length of the string. To serialize using memcpy, either change the struct to contain char arrray such as char buffer[20] or compute the size of the required serialized buffer by defining a method on the struct which gives the runtime length of the bytes.
If you want to use members like std::string, you need to go through each member of the struct and serialize.
memcpy(testout, (void *)&tstruct.test, sizeof(int) );
memcpy(testout+sizeof(int), tstruct.name.c_str(),tstruct.name.length() );
memcpy against the entire struct will not work in such scenarios.
Try NULL-terminating the string and also emitting a newline:
testout = new char[sizeof(test_struct) + 1];
memcpy(testout, &tstruct, sizeof(test_struct));
testout[sizeof(test_struct)] = '\0';
std::cout<< testout << std::endl;
However, as user3543576 points out, the serialization you get from this process won't be too useful, as it will contain a memory address of a character buffer, and not the actual string itself.

c++: write a char at a given char* causes segfault

I want to copy a char to an address where a given char* points to.
it's in a function which is called by main:
char data = " ";
myfunction(data, somethingelse);
...
inside the function i have something like
void myfunction(char* data, short somethingelse) {
...
char byte = 0;
inputfilestream.read(&byte, 1);
*data = byte; // here i get the segfault
data++;
...
}
the segfault also comes when i to the copy using strncpy:
strncpy(data, byte, 1);
why is there a segfault? data isn't const and the address where i actually write to is exactly the same as the one where i allocated the data-array. i've tested that multiple times.
thanks in advance.
String literals are readonly. If you want a modifyable string, you must use an array, e.g.:
char data[10];
Or:
char *data = new char[10];
To elaborate a bit more: the type of a string literal is actually const char*. Assigning a string literal to a non-const char* is therefore technically invalid, but most compilers allow it anyway for legacy reasons. Many modern compilers will at least issue a warning when you try to do that.
data is assigned a string literal. String literals are ready only, and writing to them will cause segfaults.
Try this:
char data[10]; // or whatever size you want.
instead.
why is there a segfault? data isn't const and the address where i actually write to is exactly the same as the one where i allocated the data-array.
You didn't allocate anything. char *data = " "; shouldn't even compile in C++. You are assigning a constant string to a non-constant.
char byte = 0;
inputfilestream.read(&byte, 1);
*data = byte; // here i get the segfault
data++; // << How many times?
No problem
#include <stdio.h>
int main(int argc, char **argv)
{
char *data = "Yello"; // or char data[] = "Yello";
*data = 'H';
puts(data); // Hello
}