C++ memory allocation (constructor) - c++

I need to write class which contains char pointer to text line and constructor which retrieves text line from the list of arguments, dynamically allocates memory and copies the text of the line to the component class.
Writed programm doesn't works correctly. Example Error.
What is wrong? Please help!
class A
{
char* text;
public:
A(char *line);
};
A::A(char *line) {
int length = strlen(line);
text = new char[length];
if (strlen(line) <= sizeof(text))
strcpy_s(text, length, line);
else
{
cout << text << endl;
cout << "Too long string" << endl;
}
}
int main()
{
A ob("aaaaaa");
system("PAUSE");
return 0;
}

The sizeof operator returns the size of the object (not the size of a string). So in this case
sizeof(text)
It returns the size of the object text. You declare text as
char* text;
So it returns the size of a char*. The exact size of this will depend on the system, but lets guess its 4. So any string that has a length greater than 4 will result in the output of:
Too long string
If we look at the string: "aaaaaa" is longer than 4 so you get the expected output.
I expect you are trying to check that the previous line succeded.
text = new char[length];
But in C++ the new will either work or throw an exception (causing program termination for this program). So either that line works or the program will exit. So there is no need to check the result of new (unlike C where you should check the result of malloc())
Also note: You should check the result of strcpy_s() as it will return an error on failure. Since you do not provide enough space in the destination it will indicate an error (you don't provide space for the null terminator).

Related

How to convert a SINGLE char to system string C++?

I'm working on a program that displays the following chars/integers in a console app.
The code that I wrote works in console app, but doesn't work in form...
I also want to display these values in my form( textBox->text).
My myfunctions.h file:
typedef struct{
char Header [23]; //the char I want to display is 23 characters long
int Version [4]; //4 characters...
char Projectname [21];
char Developer [8];
char email [16];
char Description [132];
char Unknown [1];
}PackedContent;
void BinaryReader(){
system("CLS");
PackedContent t;
fstream myFile;
myFile.open("PackedContent.opf");
if(!myFile){
cout<<"An unknown error occured! Cannot read .opf file... exiting now... \n"<<endl;
}else{
cout <<"Reading packed data content... done!\n"<<endl;
myFile.read((char *)&t, sizeof(PackedContent));
cout<<"\nHeader :" <<t.Header <<endl; // Header info [ok]
//cout<<"\nVersion :" <<t.Version <<endl; // Project Version [err]
cout<<"\nProject name:" <<t.Projectname <<endl; // Project name
cout<<"\nDeveloper name:" <<t.Developer<<endl;
cout<<"\nEmail :" <<t.email <<endl; // Developer email
cout<<"\nDescription :" <<t.Description <<endl; // Project description [ok]
cout<<"Unknown" <<t.Unknown <<endl;
}
Form:
Binary Reader.H (form)
PackedContent t;
BinaryReader();
textBox1->Text = t.Header; // doesnt work...
I've aslo tried:
textBox1->Text = Convert::ToString(t.Header); //doesn't work...
If your char array was null-terminated, like C-strings, you could have passed it as is to an std::string c'tor:
textBox1->Text = std::string(t.Header)
Your char array is not null terminated, so you should also provide the size, like this:
int headerSize = 1; // This variable is just for the example. Instead you can pass the size right in the function below
textBox1->Text = std::string(t.Header, headerSize)
Or:
textBox1->Text = std::string(t.Header, std::size(t.Header))
You have forgot about terminating zero and this is why you have problems.
If you what to store text in c-array which can have length of 23 you need char[24] (23 + 1) to include tailing zero too.
Anyway using this c-array to store text is C-style code not C++ and this should be avoided.
If you do not include terminating zero then for example this call extBox1->Text = t.Header; will try to finds it to perform conversion to String. This will lead to undefined behavior, result string will contain some trash at the end or it will end with a crash.
If your code records terminating zero in this structure, but reaches size limit, then you will have buffer overrun error.

How to convert a std::string which contains '\0' to a char* array?

I have a string like,
string str="aaa\0bbb";
and I want to copy the value of this string to a char* variable. I tried the following methods but none of them worked.
char *c=new char[7];
memcpy(c,&str[0],7); // c="aaa"
memcpy(c,str.data(),7); // c="aaa"
strcpy(c,str.data()); // c="aaa"
str.copy(c,7); // c="aaa"
How can I copy that string to a char* variable without loosing any data?.
You can do it the following way
#include <iostream>
#include <string>
#include <cstring>
int main()
{
std::string s( "aaa\0bbb", 7 );
char *p = new char[s.size() + 1];
std::memcpy( p, s.c_str(), s.size() );
p[s.size()] = '\0';
size_t n = std::strlen( p );
std::cout << p << std::endl;
std::cout << p + n + 1 << std::endl;
}
The program output is
aaa
bbb
You need to keep somewhere in the program the allocated memory size for the character array equal to s.size() + 1.
If there is no need to keep the "second part" of the object as a string then you may allocate memory of the size s.size() and not append it with the terminating zero.
In fact these methods used by you
memcpy(c,&str[0],7); // c="aaa"
memcpy(c,str.data(),7); // c="aaa"
str.copy(c,7); // c="aaa"
are correct. They copy exactly 7 characters provided that you are not going to append the resulted array with the terminating zero. The problem is that you are trying to output the resulted character array as a string and the used operators output only the characters before the embedded zero character.
Your string consists of 3 characters. You may try to use
using namespace std::literals;
string str="aaa\0bbb"s;
to create string with \0 inside, it will consist of 7 characters
It's still won't help if you will use it as c-string ((const) char*). c-strings can't contain zero character.
There are two things to consider: (1) make sure that str already contains the complete literal (the constructor taking only a char* parameter might truncate at the string terminator char). (2) Provided that str actually contains the complete literal, statement memcpy(c,str.data(),7) should work. The only thing then is how you "view" the result, because if you pass c to printf or cout, then they will stop printing once the first string terminating character is reached.
So: To make sure that your string literal "aaa\0bbb" gets completely copied into str, use std::string str("aaa\0bbb",7); Then, try to print the contents of c in a loop, for example:
std::string str("aaa\0bbb",7);
const char *c = str.data();
for (int i=0; i<7; i++) {
printf("%c", c[i] ? c[i] : '0');
}
You already did (not really, see edit below). The problem however, is that whatever you are using to print the string (printf?), is using the c string convention of ending strings with a '\0'. So it starts reading your data, but when it gets to the 0 it will assume it is done (because it has no other way).
If you want to simply write the buffer to the output, you will have to do this with something like
write(stdout, c, 7);
Now write has information about where the data ends, so it can write all of it.
Note however that your terminal cannot really show a \0 character, so it might show some weird symbol or nothing at all. If you are on linux you can pipe into hexdump to see what the binary output is.
EDIT:
Just realized, that your string also initalizes from const char* by reading until the zero. So you will also have to use a constructor to tell it to read past the zero:
std::string("data\0afterzero", 14);
(there are prettier solutions probably)

Crash after string concatenation in C++

I have this program that reads data from a serial port. For each line, I'm trying to concatenate the current time with the line of data. For some reason, it crashes when around the second print (it seems like at the end of the brackets?). The weird part is, that if I comment the print out, it'll still crash
char * cdata;
{
if( BINARY_ASCII == 1 ) //right now this is just set to 0, please ignore
{
cdata = convertBSTRToByteArray(data , numChars);
}
else
{
cdata = convertBSTRToString(data);
//prints the original output
cout << "before timestamp concat is: " << cdata << "\n";
//this is supposed to concatenate each output line with the associated time
std::stringstream ss;
ss << currentDateTime() << "," << cdata;
std::string s = ss.str();
std::strcpy(cdata,s.c_str());
cout << "after timestamp concat is: " << cdata << "\n"; //around here it crashes
}
cout << "after the thing" << "\n"; //does not even get here
I thought that the char * data would be the issue, but I've tried initializing it like
char *cdata = 0;
and
char *cdata = new char [100];
to no change...
That makes me think that I did something wrong in the concatenation?
I think it's important to highlight the difference between arrays and pointers, here.
char * cdata;
This creates a pointer named cdata. It's uninitialized, so it contains some useless memory address. A pointer is just a memory address, which means it takes up 32 (or 64) bits, and that's it.
char *cdata = 0;
This creates a pointer named cdata, and initializes it to all zeros, which means it points to the 0th location in memory. This is usually used to indicate that you should not follow this pointer.
char *cdata = new char [100];
This creates a block (array) of 100 characters, but gives that array no name. Then it creates a pointer named cdata and sets it to the memory address of the unnamed 100-byte block. I.e.:
cdata [ 0x3Ad783B2 ] --------\
\
\
|
V
[ unnamed 100-byte block ]
The reason I'm stressing this distinction is that the next line obliterates it all:
cdata = convertBSTRToString(data);
That line sets cdata to point to whatever memory address is returned by convertBSTRToString. It does not matter what value cdata had before this line -- uninitialized, null, pointing to an unnamed block of memory -- now it is pointing to the block of memory created by convertBSTRToString.
Abusing more ASCII-art:
cdata [ 0x64ADB7C8 ] --------\
\
\
|
V
[ unknown size, created by convertBSTRToString ]
// hey, look over here! it still exists,
// but we just don't point to it anymore.
[ unnamed 100-byte block ]
Now that that's covered, here's why it matters. This line:
std::strcpy(cdata,s.c_str());
strcpy will take the data pointed to by the second parameter, and copy it, byte-by-byte, to the location pointed to by the first parameter. It does not pay attention to buffer size. It's a really stupid copy. No safety whatsoever - that's up to you to provide.
I'm not sure what you're trying to accomplish with this line anyway, because s holds the full string data you wanted to concatenate:
cout << "after timestamp concat is: " << s << "\n";
convertBSTRToString probably allocates a new buffer that's sized exactly right to hold the BSTR you passed in. That means you cannot expand its size.
In your code, you are trying to add currentDateTime()'s result into that buffer (in addition to its existing content). The data won't fit. Thus, bad things happen.
You would need to first allocate a buffer big enough to contain both the convertBSTRToString plus the currentDateTime then strcpy the convertBSTRToString and then strcat the currentDateTime. strcpy won't append, strcat does.

strcat error "Unhandled exception.."

My goal with my constructor is to:
open a file
read into everything that exists between a particular string ("%%%%%")
put together each read row to a variable (history)
add the final variable to a double pointer of type char (_stories)
close the file.
However, the program crashes when I'm using strcat. But I can't understand why, I have tried for many hours without result. :/
Here is the constructor code:
Texthandler::Texthandler(string fileName, int number)
: _fileName(fileName), _number(number)
{
char* history = new char[50];
_stories = new char*[_number + 1]; // rows
for (int j = 0; j < _number + 1; j++)
{
_stories[j] = new char [50];
}
_readBuf = new char[10000];
ifstream file;
int controlIndex = 0, whileIndex = 0, charCounter = 0;
_storieIndex = 0;
file.open("Historier.txt"); // filename
while (file.getline(_readBuf, 10000))
{
// The "%%%%%" shouldnt be added to my variables
if (strcmp(_readBuf, "%%%%%") == 0)
{
controlIndex++;
if (controlIndex < 2)
{
continue;
}
}
if (controlIndex == 1)
{
// Concatenate every line (_readBuf) to a complete history
strcat(history, _readBuf);
whileIndex++;
}
if (controlIndex == 2)
{
strcpy(_stories[_storieIndex], history);
_storieIndex++;
controlIndex = 1;
whileIndex = 0;
// Reset history variable
history = new char[50];
}
}
file.close();
}
I have also tried with stringstream without results..
Edit: Forgot to post the error message:
"Unhandled exception at 0x6b6dd2e9 (msvcr100d.dll) in Step3_1.exe: 0xC00000005: Access violation writing location 0c20202d20."
Then a file named "strcat.asm" opens..
Best regards
Robert
You've had a buffer overflow somewhere on the stack, as evidenced by the fact one of your pointers is 0c20202d20 (a few spaces and a - sign).
It's probably because:
char* history = new char[50];
is not big enough for what you're trying to put in there (or it's otherwise not set up correctly as a C string, terminated with a \0 character).
I'm not entirely certain why you think multiple buffers of up to 10K each can be concatenated into a 50-byte string :-)
strcat operates on null terminated char arrays. In the line
strcat(history, _readBuf);
history is uninitialised so isn't guaranteed to have a null terminator. Your program may read beyond the memory allocated looking for a '\0' byte and will try to copy _readBuf at this point. Writing beyond the memory allocated for history invokes undefined behaviour and a crash is very possible.
Even if you added a null terminator, the history buffer is much shorter than _readBuf. This makes memory over-writes very likely - you need to make history at least as big as _readBuf.
Alternatively, since this is C++, why don't you use std::string instead of C-style char arrays?

C++ Char pointer to char array

None of the posted answers I've read work, so I'm asking again.
I'm trying to copy the string data pointed to by a char pointer into a char array.
I have a function that reads from a ifstream into a char array
char* FileReader::getNextBytes(int numberOfBytes) {
char *buf = new char[numberOfBytes];
file.read(buf, numberOfBytes);
return buf;
}
I then have a struct :
struct Packet {
char data[MAX_DATA_SIZE]; // can hold file name or data
} packet;
I want to copy what is returned from getNextBytes(MAX_DATA_SIZE) into packet.data;
EDIT: Let me show you what I'm getting with all the answers gotten below (memcpy, strcpy, passing as parameter). I'm thinking the error comes from somewhere else. I'm reading a file as binary (it's a png). I'll loop while the fstream is good() and read from the fstream into the buf (which might be the data array). I want to see the length of what I've read :
cout << strlen(packet.data) << endl;
This returns different sizes every time:
8
529
60
46
358
66
156
After that, apparently there are no bytes left to read although the file is 13K + bytes long.
This can be done using standard library function memcpy, which is declared in / :
strcpy(packet.data, buf);
This requires file.read returns proper char series that ends with '\0'. You might also want to ensure numberOfBytes is big enough to accommodate the whole string. Otherwise you could possibly get segmentation fault.
//if buf not properly null terminated added a null char at the end
buf[numberofbytes] = "\0"
//copy the string from buf to struc
strcpy(packet.data, buf);
//or
strncpy(packet.data, buf);
Edit:
Whether or not this is being handled as a string is a very important distinction. In your question, you referred to it as a "string", which is what got us all confused.
Without any library assistance:
char result = reader.getNextBytes(MAX_DATA_SIZE);
for (int i = 0; i < MAX_DATA_SIZE; ++MAX_DATA_SIZE) {
packet.data[i] = result[i];
}
delete [] result;
Using #include <cstring>:
memcpy(packet.data, result, MAX_DATA_SIZE);
Or for extra credit, rewrite getNextBytes so it has an output parameter:
char* FileReader::getNextBytes(int numberOfBytes, char* buf) {
file.read(buf, numberOfBytes);
return buf;
}
Then it's just:
reader.getNextBytes(MAX_DATA_SIZE, packet.data);
Edit 2:
To get the length of a file:
file.seekg (0, ios::end);
int length = file.tellg();
file.seekg (0, ios::beg);
And with that in hand...
char* buffer = new char[length];
file.read(buffer, length);
Now you have the entire file in buffer.
strlen is not a valid way to determine the amount of binary data. strlen just reads until it finds '\0', nothing more. If you want to read a chunk of binary data, just use a std::vector, resize it to the amount of bytes you read from the file, and return it as value. Problem solved.