Issues with converting from string to char** while creating a command prompt - c++

I am creating a command prompt and I take input from the user as a string and convert it to a char** using a parse function I have created. I can not see any issues with it but after running a few commands in my prompt I begin to run into the error of "corrupted size vs prev_size 0x00c06738. Here is my parse function. Any help would be appreciated.
char** parse(string s){
vector<string> commandvector;
istringstream iss(s);
for(string s; iss>>s;)
commandvector.push_back(s);
char** argv = new char*[commandvector.size() + 1];
for(int i = 0; i<commandvector.size();i++){
argv[i] = new char[commandvector[i].size()];
strcpy(argv[i],commandvector[i].c_str());
}
argv[commandvector.size() + 1] = NULL;
return argv;
}

If you want to use C-style strings, you have to take into account the null-terminator char.
While std::basic_string::c_str returns a pointer to a null-terminated (since C++11) array of char, std::basic_string::size doesn't count the null terminator, so in order to allocate enough memory, you have to add 1 to returned value.
Besides, as UnholySheep already noticed, you are accessing the argv array outside its bounds, beeing argv[commandvector.size()] its last element.

Related

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)

Dynamic memory allocation to char array

I have given the array size manually as below:
int main(int argc, char *argv[] )
{
char buffer[1024];
strcpy(buffer,argv[1]);
...
}
But if the data passed in the argument exceeds this size, it may will create problems.
Is this the correct way to allocate memory dynamically?
int main(int argc, char *argv[] )
{
int length;
char *buffer;
length = sizeof(argv[1]); //or strlen(argv[1])?
buffer = (char*)malloc(length*sizeof(char *));
...
}
sizeof tells you the size of char*. You want strlen instead
if (argc < 2) {
printf("Error - insufficient arguments\n");
return 1;
}
length=strlen(argv[1]);
buffer = (char*)malloc(length+1); // cast required for C++ only
I've suggested a few other changes here
you need to add an extra byte to buffer for the null terminator
you should check that the user passed in an argv[1]
sizeof(char *) is incorrect when calculating storage required for a string. A C string is an array of chars so you need sizeof(char), which is guaranteed to be 1 so you don't need to multiply by it
Alternatively, if you're running on a Posix-compatible system, you could simplify things and use strdup instead:
buffer = strdup(argv[1]);
Finally, make sure to free this memory when you're finished with it
free(buffer);
The correct way is to use std::string and let C++ do the work for you
#include <string>
int main()
{
std::string buffer = argv[1];
}
but if you want to do it the hard way then this is correct
int main()
{
int length = strlen(argv[1]);
char* buffer = (char*)malloc(length + 1);
}
Don't forget to +1 for the null terminator used in C style strings.
In C++, you can do this to get your arguements in a nice data structure.
const std::vector<std::string>(argv, argv + argc)
length= strlen(argv[1]) //not sizeof(argv[1]);
and
//extra byte of space is to store Null character.
buffer = (char*)malloc((length+1) * sizeof(char));
Since sizeof(char) is always one, you can also use this:
buffer = (char*)malloc(length+1);
Firstly, if you use C++ I think it's better to use new instead of malloc.
Secondly, you're malloc size is false : buffer = malloc(sizeof(char) * length); because you allocate a char buffer not a char* buffer.
thirdly, you must allocate 1 byte more for the end of your string and store '\0'.
Finally, sizeof get only the size of the type not a string, you must use strlen for getting string size.
You need to add an extra byte to hold the terminating null byte of the string:
length=sizeof(argv[1]) + 1;
Then it should be OK.

Segfaults on appending char* arrays

I'm making a lexical analyzer and this is a function out of the whole thing. This function takes as argument a char, c, and appends this char to the end of an already defined char* array (yytext). It then increments the length of the text (yylen).
I keep getting segfaults on the shown line when it enters this function. What am I doing wrong here? Thanks.
BTW: can't use the strncpy/strcat, etc. (although if you want you can show me that implementation too)
This is my code:
extern char *yytext;
extern int *yylen;
void consume(char c){
int s = *yylen + 1; //gets yylen (length of yytext) and adds 1
//now seg faults here
char* newArray = new char[s];
for (int i = 0;i < s - 1;i++){
newArray[i] = yytext[i]; //copy all chars from existing yytext into newArray
}
newArray[s-1] = c; //append c to the end of newArray
for (int i = 0;i < s;i++){ //copy all chars + c back to yytext
yytext[i] = newArray[i];
}
yylen++;
}
You have
extern int *yylen;
but try to use it like so:
int s = (int)yylen + 1;
If the variable is an int *, use it like an int * and dereference to get the int. If it is supposed to be an int, then declare it as such.
That can t work:
int s = (int)yylen + 1; //gets yylen (length of yytext) and adds 1
char newArray[s];
use malloc or a big enought buffer
char * newarray=(char*)(malloc(s));
Every C-style string should be null-terminated. From your description it seems you need to append the character at c. So, you need 2 extra locations ( one is for appending the character and other for null-terminator ).
Next, yylen is of type int *. You need to dereference it to get the length (assuming it is pointing to valid memory location ). So, try -
int s = *yylen + 2;
I don't see the need of temporary array but there might be a reason why you are doing it. Now,
yytext[i] = newArray[i]; //seg faults here
you have to check if yytext is pointing to a valid write memory location. If yes, then is it long enough to fill the appending character plus null terminator.
But I would recommend using std::string than working with character arrays. Using it would be a one liner to solve the problem.

Assigning chars to a char array with +=

Currently I'm writing a rather extensive homework assignment that - among other things - reads a file, builds a binary search tree and outputs it.
Somewhere inside all that I've written a recursive method to output the values of the binary search tree in order.
void output(node* n)
{
if(n->leftChild != NULL)
output(n->leftChild);
cout << n->keyAndValue << " || ";
outputString += n->keyAndValue << '|';
if(n->rightChild != NULL)
output(n->rightChild);
}
No problem with that, but you'll notice the line outputString += n->keyAndValue << '|';, because I also want to have all the values inside a char array (I am not allowed to use strings or other more current features of C++) that I can use later on in a different method (e.g. Main method).
The Char-Array is declared as follows:
char *outputString;
This being just one of the ways I've tried. I also tried using the const keyword and just regularly building an array char outputString[]. With the version I've shown you I encounter an error when - later on in the program in a different method - calling the following code:
cout << outputString;
I get the following error:
Unhandled exception at 0x008c2c2a in BST.exe: 0xC00000005: Access Violation reading location 0x5000000000.
Any clue as to how I'd be able to build a dynamic char array, assign values to it numerous times using += and outputting it without triggering an access violation? I am sorry for asking a rather basic question but I am entirely new to C++.
Thanks and Regards,
Dennis
I'm guessing that since you can't use std::string, you also can't use new[].
You can concatenate strings with a function like this:
char *concat(const char *s1, const char *s2)
{
size_t len = strlen(s1) + strlen(s2);
char *result = (char*)malloc(len+1);
strcpy(result, s1);
strcat(result, s2);
return result;
}
This can be done more efficiently, but that probably doesn't matter for homework. And you need to check for errors, etc. etc.
You also need to decide who is going to call free on s1 and s2.
For what it is worth, the efficient version looks like this:
char *concat(const char *s1, const char *s2)
{
size_t len1 = strlen(s1);
size_t len2 = strlen(s2);
char *result = (char*)malloc(len1+len2+1);
memcpy(result, s1, len1);
memcpy(result+len1, s2, len2);
result[len1+len2] = '\0';
return result;
}
It's more efficient because it only walks the input strings once.
+= on pointers does pointer arithmetic, not string concatenation. Eventually you get way beyond your array that outputString was pointing to, and trying to print it leads to a segfault.
Since you can't use std::string, you need to use strcat along with new[] and delete[] and make sure you allocated your original array with new[].

Array initialization issue

I need an empty char array, but when i try do thing like this:
char *c;
c = new char [m];
int i;
for (i = 0; i < m; i++)
c[i] = 65 + i;
and then I print c. can see that c = 0x00384900 "НННННННээээ««««««««юоюою"
after cycle it becomes: 0x00384900 "ABCDEFGээээ««««««««юоюою"
How can I solve this problem? Or maybe there is way with string?
If you're trying to create a string, you need to make sure that the character sequence is terminated with the null character \0.
In other words:
char *c;
c = new char [m+1];
int i;
for (i = 0; i < m; i++)
c[i] = 65 + i;
c[m] = '\0';
Without it, functions on strings like printf won't know where the string ends.
printf("%s\n",c); // should work now
If you create a heap array, OS will not initialiase it.
To do so you hvae these options:
Allocate an array statically or globally. The array will be filled with zeroes automatically.
Use ::memset( c, 0, m ); on heap-initialised or stack array to fill it with zeroes.
Use high-level types like std::string.
I believe that's your debugger trying to interpret the string. When using a char array to represent a string in C or C++, you need to include a null byte at the end of the string. So, if you allocate m + 1 characters for c, and then set c[m] = '\0', your debugger should give you the value you are expecting.
If you want a dynamically-allocated string, then the best option is to use the string class from the standard library:
#include <string>
std::string s;
for (i = 0; i < m; i++)
s.push_back(65 + i);
C strings are null terminated. That means that the last character must be a null character ('\0' or just 0).
The functions that manipulate your string use the characters between the beginning of the array (that you passed as parameter, first position in the array) and a null value. If there is no null character in your array the function will iterate pass it's memory until it finds one (memory leak). That's why you got some garbage printed in your example.
When you see a literal constant in your code, like printf("Hello");, it is translate into an array of char of length 6 ('H', 'e', 'l', 'l', 'o' and '\0');
Of course, to avoid such complexity you can use std::string.