I want to write a a mix of int, char, real in void *data.
I am using a file pointer to run through the data block.
Now my question is that since the data type is void, I have to typecast it to int while writing integer and char for writing string.
While typecasting I used the following sample code:
*((int *)data+0) = 14; //writing int
*((int *)data+4) = 5; //writing int, left a space of 4 bytes for int
*((char *)data+8) = 'a'; //writing char
*((char *)data+9) = 'f'; //writing char
But then while reading the values back it didnt give the correct value.
cout<<*((int *)data+0);
cout<<*((int *)data+3);
cout<<*((char *)data+8);
Is the way my code is written correct? I am doubtful about it as data is void.
*((int *)data+4) = 5; // writing 4th int
cout<<*((int *)data+3); // but reading third one
And just in case, ((int *)data+4) points to 4th integer (that is, 16th byte given int size = 4), not to 4th byte. That is, you code overwrites bytes 0-3, then 16-19, then 8th, then 9th. What you probably meant is: *(int *)( (char*)data + X )
Edited to correct mistake pointed out by MSalters
Apart from the typo that others have mentioned (data+3 instead of data+4), you also need to change e.g.
*((int *)data+4)
to
*((int *)data+1)
because adding 4 to an int * doesn't add 4 to the address, it adds 4 * sizeof (int).
If you need to write to an offset that is not a multiple of sizeof(int) (say, 7), you need:
*(int *)((char *)data+7)
For this reason, it might be better to make data a char * to start with, so you can just say
*(int *)(data+7)
Use a class or a struct.
Here, pointer arithmetics is misleading you. When you add 4 to an int * you are adding actually four times sizeof int.
If your data has a constant layout, why don't you just use a struct such as
struct MemoryLayout {
int _first;
int _second;
char _c1;
char _c2;
};
?
You're writing the second one with:
*((int *)data+4) = 5; // offset = 4
and reading it back with:
cout<<*((int *)data+3); // offset = 3
In addition, the (int*) cast is binding to data, not data+4 so that your 4 is scaled up by the size of an int.
If you really want to do this (and a struct is not a possibility due to variances in the data formats), you should cast data to a char*, then add the number of bytes to get the char offset, then cast that to your desired type.
That would be something like:
*((int*)((char*)(data + 0)) = 14; //writing int
*((int*)((char*)(data + 4)) = 5; //writing int
*((char*)data + 8) = 'a'; //writing char
*((char*)data + 9) = 'f'; //writing char
int Data;
//char Data;
//float Data;
FILE *File = fopen("File.txt","wb");
fwrite((char *)&Data,sizeof(Data),1,File);
fclose(File);
File = fopen("File.txt","rb");
fread((char *)&Data,sizeof(Data),1,File);
fclose(File);
...
int DataInt1 = 200;
char DataChar1 = 'N';
FILE *File = fopen("File.txt","wb");
fwrite((char *)&DataInt1,sizeof(DataInt1),1,File);
fwrite((char *)&DataChar1,sizeof(DataChar1),1,File);
fclose(File);
int DataInt2 = 0;
char DataChar2 = 0;
File = fopen("File.txt","rb");
fread((char *)&DataInt2,sizeof(DataInt2),1,File);
fread((char *)&DataChar2,sizeof(DataChar2),1,File);
fclose(File);
printf("%d %d!\n",DataInt2,DataChar2);
Related
I want to dump chars from a Lora transmitter using Arduino Nano. With this line I assume it can be store the chars into an array:
char* dump = (char)LoRa.read();
char in[255];
strcpy(in, dump);
char str[] = in;
But unfortunately I get this compiler error:
exit status 1
initializer fails to determine size of 'str'
How I fix it?
UPDATE
I give my entire code. I used shox96 shox96 from siara-cc for my objective to compress the data from Lora.read().
void print_compressed(char *in, int len) {
int l;
byte bit;
//Serial.write("\nCompressed bits:");
for (l=0; l<len*8; l++) {
bit = (in[l/8]>>(7-l%8))&0x01;
//Serial.print((int)bit);
//if (l%8 == 7) Serial.print(" ");
}
}
void loop() {
char* dump = (char)LoRa.read();
char in[255];
strcpy(in, dump);
char str[] = in;
char cbuf[300];
char dbuf[300];
int len = sizeof(str);
if (len > 0) {
memset(cbuf, 0, sizeof(cbuf));
int ctot = shox96_0_2_compress(str, len, cbuf, NULL);
print_compressed(cbuf, ctot);
memset(dbuf, 0, sizeof(dbuf));
int dlen = shox96_0_2_decompress(cbuf, ctot, dbuf, NULL);
dbuf[dlen] = 0;
float perc = (dlen-ctot);
perc /= dlen;
perc *= 100;
Serial.print(ctot);
Serial.write(",");
Serial.println(dlen);
}
delay(1000);
}
The compiler can only supply the size of an array for you on creation if it has a brace enclosed initializer list. Like this:
int array[] = {1, 2, 3, 4, 5};
If you're doing anything other than that then you need to put a number inside those braces. Since you're making a copy of the array in and that array is 255 char then this one needs to be 255 char as well in order to accommodate.
char str[255] = in;
My comment on your question still stands though. This answer clears your compiler error, but I don't think it is really a solution to your larger problem. But without seeing more of your code and knowing more about it I can't tell much about it. You already have two copies of this data by the time you get to that line. I'm not sure why you think you need to have a third.
It seems to me that the buffer is being modified. Does it put 6 integers and then 5 floats into the buffer? It's also strange that they set size to 44 instead of 1024*sizeof(char). Perhaps the whole buffer is passed to write() but write() writes only the first 44 bytes to the client.
Could you please explain line by line? I have no experience with c++.
char buf[1024];
int* pInt = reinterpret_cast<int*>(buf);
*pInt = 5;
*(pInt+1) = 2;
*(pInt+2) = 3;
*(pInt+3) = 4;
*(pInt+4) = 5;
*(pInt+5) = 6;
float* pFloat = reinterpret_cast<float*>(pInt+6);
*pFloat = 111;
*(pFloat+1) = 222;
*(pFloat+2) = 333;
*(pFloat+3) = 444;
*(pFloat+4) = 555;
int n;
int size = (1+2*5)*4;
n = write(buf, size);
Does it put 6 integers and then 5 floats into the buffer?
Yes.
It's also strange that they set size to 11 instead of 1024*sizeof(char)
They don't want to write the entire buffer. Thy want to write just the ints and floats that were written to the buffer.
FWIW, that is poorly written code. It assumes that sizeof(int) and sizeof(float) are both equal to 4. A more portable method would use:
int size = 6*sizeof(int) + 5*sizeof(float);
Caution
Even though the posted code might work under some, perhaps most, circumstances, use of
int* pInt = reinterpret_cast<int*>(buf);
*pInt = 5;
is cause for undefined behavior by the standard. It violates the strict aliasing rule. There wasn't an int to begin with at that location.
Had you used:
int array[5] = {};
char* cp = reinterpret_cast<char*>(array);
// ...
int* iptr = reinterpret_cast<int*>(cp);
*iptr = 10;
there would be no problem since cp points to a place where an int was there to begin with.
For your use case, it will be better to use:
char buf[1024];
int intArray[] = {5, 2, 3, 4, 5, 6};
std::memcpy(buff, intArray, sizeof(intArray));
float floatArray = {111, 222, 333, 444, 555};
std::memcpy(buff+sizeof(intArray), floatArray, sizeof(floatArray));
int n;
int size = sizeof(intArray) + sizeof(floatArray);
n = write(buf, size);
Further reading:
reinterpret_cast creating a trivially default-constructible object
Unions and type-punning
I have attached my code below. I do not see what I am doing wrong. I have a struct that I am trying to serialize into a byte array. I have wrote some some simple code to test it. It all appears to work during runtime when I print out the values of objects, but once I hit return 0 it throws the error:
Run-Time Check Failure #2 - Stack around the variable 'command' was corrupted.
I do not see the issue. I appreciate all help.
namespace CommIO
{
enum Direction {READ, WRITE};
struct CommCommand
{
int command;
Direction dir;
int rwSize;
BYTE* wData;
CommCommand(BYTE* bytes)
{
int offset = 0;
int intsize = sizeof(int);
command = 0;
dir = READ;
rwSize = 0;
memcpy(&command, bytes + offset, intsize);
offset += intsize;
memcpy(&dir, bytes + offset, intsize);
offset += intsize;
memcpy(&rwSize, bytes + offset, intsize);
offset += intsize;
wData = new BYTE[rwSize];
if (dir == WRITE)
{
memcpy(&wData, bytes + offset, rwSize);
}
}
CommCommand() {}
}
int main()
{
CommIO::CommCommand command;
command.command = 0x6AEA6BEB;
command.dir = CommIO::WRITE;
command.rwSize = 128;
command.wData = new BYTE[command.rwSize];
for (int i = 0; i < command.rwSize; i++)
{
command.wData[i] = i;
}
command.print();
CommIO::CommCommand command2(reinterpret_cast<BYTE*>(&command));
command2.print();
cin.get();
return 0;
}
The following points mentioned in comments are most likely the causes of your problem.
You seem to be assuming that the size of Direction is the same as the size of an int. That may indeed be the case, but C++ does not guarantee it.
You also seem to be assuming that the members of CommIO::CommCommand will be laid out in memory without any padding between, which again may happen to be the case, but is not guaranteed.
There are couple of ways to fix the that.
Make sure that you fill up the BYTE array in the calling function with matching objects, or
Simply cast the BYTE* to CommCommand* and access the members directly.
For (1), you can use:
int command = 0x6AEA6BEB;
int dir = CommIO::WRITE;
int rwSize = 128;
totatlSize = rwSize + 3*sizeof(int);
BYTE* data = new BYTE[totalSize];
int offset = 0;
memcpy(data + offset, &comand, sizeof(int));
offset += sizeof(int);
memcpy(data + offset, &dir, sizeof(int));
offset += sizeof(int);
memcpy(data + offset, &rwSize, sizeof(int));
offset += sizeof(int);
for (int i = 0; i < rwSize; i++)
{
data[i + offset] = i;
}
CommIO::CommCommand command2(data);
For (2), you can use:
CommCommand(BYTE* bytes)
{
CommCommand* in = reinterpret_cast<CommCommand*>(bytes);
command = in->command;
dir = in->dir;
rwSize = in->size;
wData = new BYTE[rwSize];
if (dir == WRITE)
{
memcpy(wData, in->wData, rwSize);
}
}
The other error is that you are using
memcpy(&wData, bytes + offset, rwSize);
That is incorrect since you are treating the address of the variable as though it can hold the data. It cannot.
You need to use:
memcpy(wData, bytes + offset, rwSize);
The memory for your struct is laid out without padding, this can be rectified by adding the macro #pragma pack(1) at the start of the struct and #pragma pop() at the end of the struct - check its syntax though.
For your struct to byte conversion, I would use something simple as:
template<typename T, typename IteratorForBytes>
void ConvertToBytes(const T& t, IteratorForBytes bytes, std::size_t pos = 0)
{
std::advance(bytes, pos);
const std::size_t length = sizeof(t);
const uint8_t* temp = reinterpret_cast<const uint8_t*>(&t);
for (std::size_t i = 0; i < length; ++i)
{
(*bytes) = (*temp);
++temp;
++bytes;
}
}
Where T is the is the struct in your case your Command struct and bytes would be the array.
CommIO::CommCommand command;
command.wData = new BYTE[command.rwSize];
ConvertToBytes(command, command.wData);
The resulting array would contain the expected bytes You could specify the offset as well as an extra parameter if you want to start filling your byte array from a particular location
The main problem is here:
memcpy(&wData, bytes + offset, rwSize);
Member wData is a BYTE *, and you seem to mean to copy bytes into the space to which it points. Instead, you are copying data into the memory where the pointer value itself is stored. Therefore, if you copy more bytes than the size of the pointer then you will overrun its bounds and produce undefined behavior. In any case, you are trashing the original pointer value. You probably want this, instead:
memcpy(wData, bytes + offset, rwSize);
Additionally, although the rest of the deserialization code may be right for your actual serialization format, it is not safe to assume that it is right for the byte sequence you present to it in your test program via
CommIO::CommCommand command2(reinterpret_cast<BYTE*>(&command));
As detailed in comments, you are making assumptions about the layout in memory of a CommIO::CommCommand that C++ does not guarantee will hold.
At
memcpy(&wData, bytes + offset, rwSize);
you copy from the location of the wData pointer and to the location of the wData pointer of the new CommCommand. But you want to copy from and to the location that the pointer points to. You need to dereference. You corrupt the heap, because you have only sizeof(BYTE*) space (plus some extra, because heap blocks cannot be arbitrarily small), but you copy rwSize bytes, which is 128 bytes. What you probably meant to write is:
memcpy(wData, *(BYTE*)(bytes + offset), rwSize);
which would take use the pointer stored at bytes + offset, rather than the value of bytes + offset itself.
You also assume that your struct is tightly packed. However, C++ does not guarantee that. Is there a reason why you do not override the default copy constructor rather than write this function?
If I have
void *temp = malloc(128);
memset(temp, 0 , 128);
And I want to read the first byte alone, following is what I'm doing.
char a[2];
strncpy(a, (char*)temp, 1);
int p = a[0];
//p will be zero in this case
Q1. I'm sure there is a more elegant way to achieve the same. If so, what would it be?
Q2. Is there a way I can alter the value of that single byte alone?
Say I want the first byte to have the value equivalent to the int value 48 (i.e. 00110000)
How would I do that?
I was able to make no progress with the write.
you can cast it to char * then access the memory
char *buff = temp;
char p = buff[0]; // read first byte
I am trying to place 3 integers(byte size is 4) into a character string byte by byte using c. I then need to "extract" the integers out of the character array so I can do integer operations on them. I have looked around and could not find any solutions to this. I think this will require some type of pointer use or shifting, but I cannot figure out how to write it.
char str[12]="";
int a;
int b;
int c;
int x;
int y;
int z;
a=5;
b=7;
c=12;
I know that an int is 4 bytes. I would like to make it so the str char array has the following data in it.
str = |a1|a2|a3|a4|b1|b2|b3|b4|c1|c2|c3|c4|
*I do not want it like this. str=|'5'|'7'|'12'|
I then need to "extract" the integers out of the character array.
x=str[0-3]; //extracting a
y=str[4-7]; //extracting b
z=str[8-11]; //extracting c
After this, I should be able to write x=y+z and x will be equal to 19.
One way is to treat str as an int array instead:
int* istr = reinterpret_cast<int*>(str)
Then you can use e.g.
istr[0] = a;
istr[1] = b;
istr[2] = c;
and
x = istr[0];
y = istr[1];
z = istr[2];
The question is not well posed so you are getting different answers which may or may not be solving your problem. In my interpretation, here's what you need:
int i1, i2, i3;
char arr[sizeof(i1)+sizeof(i2)+sizeof(i3)];
memcpy(arr, &i1, sizeof(i1));
memcpy(arr+sizeof(i1), &i2, sizeof(i2));
memcpy(arr+sizeof(i1)+sizeof(i2), &i3, sizeof(i3));
Note that I'm being deliberately explicit with using sizeof(i) instead of just "4". It is fairly safe that integers will be 32-bit in whatever environment you are using, but this is safer and strictly speaking more correct.
The easiest solution is to use memcpy:
int nums[sizeof str / sizeof(int)];
std::memcpy(nums, str, sizeof nums);
// Do work on nums here...
The reinterpret_cast approach is undefined behaviour.
Use (void *) to get a pointer to x, byte by byte
for (int i = 0; i < sizeof(int); ++i) {
str[i] = (void *)(&x)[i];
}
This will copy the 4 bytes of x into str, one by one. (void )(&x) casts x as a char array (or void*, same thing), and [i] accesses the i_th byte of the array
then access elements of str the same way.
Do the same with y and z, and don't forget the offset