Insert char * with new into a vector C++ - c++

I have code sample like below..
std::vector<char*> vNameList;//it will be defined globally..
int main(int argc, char* argv[])
{
CollectName();
for(int i = 0; i<(int)vNameList.size(); i++)
{
printf("\n %s" , vNameList[i]);//Here gabage values are getting printed on console
}
return 0;
}
void CollectName()
{
char *Name = new char[sizeof(NAME)+1];//NAME datatype is defined having size of 32 char
//processing for Name is performed , which includes assigning Name variable with value..
//now insert it into vector
vNameList.push_back(Name);
delete[] Name; //at this Name value inserted into vector become garbage
}
I believe if we initialize char * with new it must be deleted to avoid memory leaks. But this is leading to modifying values from a vector.
Please guide me so that I can correct my code which will give me correct values.
I have some restriction to use Char * only , so suggest way to achieve this using char*.

You can correct your code by deleting the pointers only after you're done using them (assuming you actually have code which uses the pointers. Your example code doesn't print anything at all, garbage or otherwise).
But it's usually better design to store character strings in a std::string and when you store std::strings in a std::vector, you no longer need to manage the memory manually.

It is because the name of array is treated as pointer in c++(or c, it is inherted).
Therefore when you do
vNameList.push_back(Name);
It inserts the char * into vector,i.e the pointer to the first char(ans hence the string) to the vector, but you delete the pointer afterwards , therefore you get junk values. However if you don't delete the pointer , it works just fine as the pointer still exists , but this way you will not free up memory. Therefore : DONOT USE THIS
here it is : LIVE EXAMPLE
To avoid this hassle : you should use
std::vector<std::string> vNameList;
instead.

Related

Needing to use char** in C++ instead of std::string*

I am working on an assignment for my operating systems class. We have the option of using C or C++, so I decided to use C++ since I practised it on the job a bit more recently than C.
I will need to call (from "
$ man execvp " on Linux)
int execvp(const char *file, char *const argv[]);
which (unless I am mistaken) means I need a C-style char* array (a string array in C), and will not be able to use std::string from C++.
My question is: what is the proper way to make/use char* arrays instead of string arrays in C++? Most people tend to say malloc is not used any more in C++ (which I tried now with some complications)
char** cmdList = (char**)malloc(128 * sizeof(char*));
but I don't know how to make a char* array without. Is it still appropriate to solve this how I would in C even though I'm using C++? I haven't ever run into a circumstance where I couldn't use string in C++.
Thanks for everyone's time.
If you put your arguments into a std::vector<std::string>, as you should in C++, then you need a small conversion to get to the char** that execvp wants. Luckily, both std::vector and std::string are continuous in memory. However, a std::vector<std::string> isn't an array of pointers, so you need to create one. But you can just use a vector for that too.
// given:
std::vector<std::string> args = the_args();
// Create the array with enough space.
// One additional entry will be NULL to signal the end of the arguments.
std::vector<char*> argv(args.size() + 1);
// Fill the array. The const_cast is necessary because execvp's
// signature doesn't actually promise that it won't modify the args,
// but the sister function execlp does, so this should be safe.
// There's a data() function that returns a non-const char*, but that
// one isn't guaranteed to be 0-terminated.
std::transform(args.begin(), args.end(), argv.begin(),
[](std::string& s) { return const_cast<char*>(s.c_str()); });
// You can now call the function. The last entry of argv is automatically
// NULL, as the function requires.
int error = execvp(path, argv.data());
// All memory is freed automatically in case of error. In case of
// success, your process has disappeared.
Instead of malloc use new[]:
char ** cmdlist = new char*[128];
There is no need for sizeof, since new knows the size of the type it creates. For classes this also calls the default constructor if it exists. But be careful: If there is no (public) default constructor for a type you can not use new[].
Instead of free use delete[] to release your memory when you are done:
delete[] cmdlist;
Of course, you could also use a vector. This has the advantage that the memory used to store the vector's content is automatically released when the vector is destroyed.
#include <vector>
...
std::vector<char*> cmdlist(128, nullptr); // initialize with nullpointers
// access to entries works like with arrays
char * firstCmd = cmdList[0];
cmdlist[42] = "some command";
// you can query the size of the vector
size_t numCmd = cmdlist.size();
// and you can add new elements to it
cmdlist.push_back("a new command");
...
// the vector's internal array is automatically released
// but you might have to destroy the memory of the char*s it contains, depending on how they were created
for (size_t i = 0; i < cmdlist.size(); ++i)
// Free cmdlist[i] depending on how it was created.
// For example if it was created using new char[], use delete[].
Assuming you have an std::vector<std::string> args variable that represents the argument list, you could do the following to get a C-style array of strings:
auto argvToPass = std::make_unique<const char*[]>(args.size() + 1);
int i = 0;
for (const auto& arg : args)
{
argvToPass[i++] = arg.c_str();
}
// make we have a "guard" element at the end
argvToPass[args.size()] = nullptr;
execvp(yourFile, argvToPass.get());
You can create an array of const char* and still use strings by using string.c_str() the code will look like this
const char ** argv = new const char*[128];
string arg1 = "arg";
argv[0] = arg1.c_str();
If you want to get fancy (and safe) about it you can use std::unique_ptr to good effect to ensure everything gets properly deleted/freed in the event of an error or an exception:
// Deleter to delete a std::vector and all its
// malloc allocated contents
struct malloc_vector_deleter
{
void operator()(std::vector<char*>* vp) const
{
if(!vp)
return;
for(auto p: *vp)
free(p);
delete vp;
}
};
// self deleting pointer (using the above deleter) to store the vector of char*
std::unique_ptr<std::vector<char*>, malloc_vector_deleter> cmds(new std::vector<char*>());
// fill the vector full of malloc'd data
cmds->push_back(strdup("arg0"));
cmds->push_back(strdup("arg1"));
cmds->push_back(strdup("arg2"));
// did any of the allocations fail?
if(std::find(cmds->begin(), cmds->end(), nullptr) != cmds->end())
{
// report error and return
}
cmds->push_back(nullptr); // needs to be null terminated
execvp("progname", cmds->data());
// all memory deallocated when cmds goes out of scope
which (unless I am mistaken) means I need a C-style char* array (a string array in C), and will not be able to use std::string from C++.
No you can still use string from C++. The string class has a constructor that takes C-string:
char str[] = "a string";
string cppStr(str);
Now you can manipulate your string using the string class in C++.

C++ copying char to a char array (Debug assertion failed) says string is not null terminated

Just trying to assign chars to the char array and it says string in not null terminated?
I want to be able to change the teams around in the array like a scoreboard.
#include <string.h>
#include <iostream>
int main(int argc, char* argv[])
{
char Team1[7] = "Grubs";
char Team2[7] = "Giants";
char Team3[7] = "Bulls";
char Team4[7] = "Snakes";
char Team5[7] = "Echos";
char TeamList[5][7];
strcpy_s(TeamList[0], Team1);
strcat_s(TeamList[1], Team2);
strcat_s(TeamList[2], Team3);
strcat_s(TeamList[3], Team4);
strcat_s(TeamList[4], Team5);
TeamList[5][7]= '\0';
system("pause");
return 0;
}
strcat() (which is a "less-safe" version of strcat_s()) requires both strings to be null-terminated. That's because strcat() appends its second parameter (source) where first parameter (dest) ends. It replaces null-terminator of dest with first character of source, appends rest of source and then
a null-character is included at the end of the new string formed by
the concatenation of both
I would simply change
strcpy_s(TeamList[0], Team1);
strcat_s(TeamList[1], Team2);
strcat_s(TeamList[2], Team3);
strcat_s(TeamList[3], Team4);
strcat_s(TeamList[4], Team5);
to
strcpy_s(TeamList[0], Team1);
strcpy_s(TeamList[1], Team2);
strcpy_s(TeamList[2], Team3);
strcpy_s(TeamList[3], Team4);
strcpy_s(TeamList[4], Team5);
strcpy_s() does not have any requirements regarding contents of destination - only its capacity matters.
If you want to stick with strcat_s(), do this:
char TeamList[5][7];
memset(TeamList, 0, sizeof(char) * 5 * 7);
Then, this line:
TeamList[5][7]= '\0';
is not required, It is incorrect anyway, because for N-element array valid indexes are [0; N-1].
EDIT
Since in your case swapping comes into play, I would suggest you totally different approach.
First of all:
#include <string>
Then, initialize teams this way:
std::string TeamList[] =
{
"Grubs",
"Giants",
"Bulls",
"Snakes",
"Echos"
};
Now, TeamList is an array containing 5 elements and each of these elements is an object of type std::string, containing name of a particular team.
Now, if you want to swap, let's say, teams 1 and 3:
std::swap(TeamList[1], TeamList[3]);
std::swap() is a standard C++ function extensively used in standard library implementation. It is overloaded for many standard types, including std::string. This solution has one, critical benefit: if string's content is held on the heap, swapping two strings is as simple as swapping pointers (and some length/capacity variables).
Oh, and one more thing: if you are not familiar with std::string and you would need to get pointer to a buffer containing string's data, you can do it this way:
const char* team_1_raw_name = TeamList[0].c_str();
See this page for more info about std::string
strcat requires that there already be a null-terminated string in the destination to concatenate the source string onto; you're calling it with uninitialised values in the destination.
It looks like you want strcpy in every case, not just the first.
Also, remove the bogus TeamList[5][7]= '\0';. Even if you fix it to write inside the array bounds, each string has already been terminated by strcpy so there's no need to try to do that yourself.
Then stop messing around with low-level arrays and pointers. std::vector<std::string> would be much friendlier.

C++: How to use new to find store for function return value?

I'm reading the 3rd edition of The C++ Programming Language by Bjarne Stroustrup and attempting to complete all the exercises. I'm not sure how to approach exercise 13 from section 6.6, so I thought I'd turn to Stack Overflow for some insight. Here's the description of the problem:
Write a function cat() that takes two C-style string arguments and
returns a single string that is the concatenation of the arguments.
Use new to find store for the result.
Here's my code thus far, with question marks where I'm not sure what to do:
? cat(char first[], char second[])
{
char current = '';
int i = 0;
while (current != '\0')
{
current = first[i];
// somehow append current to whatever will eventually be returned
i++;
}
current = '';
i = 0;
while (current != '\0')
{
current = second[i];
// somehow append current to whatever will eventually be returned
i++;
}
return ?
}
int main(int argc, char* argv[])
{
char first[] = "Hello, ";
char second[] = "World!";
? = cat(first, second);
return 0;
}
And here are my questions:
How do I use new to find store? Am I expected to do something like std::string* result = new std::string; or should I be using new to create another C-style string somehow?
Related to the previous question, what should I return from cat()? I assume it will need to be a pointer if I must use new. But a pointer to what?
Although the problem doesn't mention using delete to free memory, I know I should because I will have used new to allocate. Should I just delete at the end of main, right before returning?
How do I use new to find store? Am I expected to do something like std::string* result = new std::string; or should I be using new to create another C-style string somehow?
The latter; the method takes C-style strings and nothing in the text suggests that it should return anything else. The prototype of the function should thus be char* cat(char const*, char const*). Of course this is not how you’d normally write functions; manual memory management is completely taboo in modern C++ because it’s so error-prone.
Although the problem doesn't mention using delete to free memory, I know I should because I will have used new to allocate. Should I just delete at the end of main, right before returning?
In this exercise, yes. In the real world, no: like I said above, this is completely taboo. In reality you would return a std::string and not allocate memory using new. If you find yourself manually allocating memory (and assuming it’s for good reason), you’d put that memory not in a raw pointer but a smart pointer – std::unique_ptr or std::shared_ptr.
In a "real" program, yes, you would use std::string. It sounds like this example wants you to use a C string instead.
So maybe something like this:
char * cat(char first[], char second[])
{
char *result = new char[strlen(first) + strlen(second) + 1];
...
Q: How do you "append"?
A: Just write everything in "first" to "result".
As soon as you're done, then continue by writing everything in "second" to result (starting where you left off). When you're done, make sure to append '\0' at the end.
You are supposed to return a C style string, so you can't use std::string (or at least, that's not "in the spirit of the question"). Yes, you should use new to make a C-style string.
You should return the C-style string you generated... So, the pointer to the first character of your newly created string.
Correct, you should delete the result at the end. I expect it may be ignored, as in this particular case, it probably doesn't matter that much - but for completeness/correctness, you should.
Here's some old code I dug up from a project of mine a while back:
char* mergeChar(char* text1, char* text2){
//Find the length of the first text
int alen = 0;
while(text1[alen] != '\0')
alen++;
//Find the length of the second text
int blen = 0;
while(text2[blen] != '\0')
blen++;
//Copy the first text
char* newchar = new char[alen + blen + 1];
for(int a = 0; a < alen; a++){
newchar[a] = text1[a];
}
//Copy the second text
for(int b = 0; b < blen; b++)
newchar[alen + b] = text2[b];
//Null terminate!
newchar[alen + blen] = '\0';
return newchar;
}
Generally, in a 'real' program, you'll be expected to use std::string, though. Make sure you delete[] newchar later!
What the exercise means is to use new in order to allocate memory. "Find store" is phrased weirdly, but in fact that's what it does. You tell it how much store you need, it finds an available block of memory that you can use, and returns its address.
It doesn't look like the exercise wants you to use std::string. It sounds like you need to return a char*. So the function prototype should be:
char* cat(const char first[], const char second[]);
Note the const specifier. It's important so that you'll be able to pass string literals as arguments.
So without giving the code out straight away, what you need to do is determine how big the resulting char* string should be, allocate the required amount using new, copy the two source strings into the newly allocated space, and return it.
Note that you normally don't do this kind of memory management manually in C++ (you use std::string instead), but it's still important to know about it, which is why the reason for this exercise.
It seems like you need to use new to allocate memory for a string, and then return the pointer. Therefore the return type of cat would be `char*.
You could do do something like this:
int n = 0;
int k = 0;
//also can use strlen
while( first[n] != '\0' )
n ++ ;
while( second[k] != '\0' )
k ++ ;
//now, the allocation
char* joint = new char[n+k+1]; //+1 for a '\0'
//and for example memcpy for joining
memcpy(joint, first, n );
memcpy(joint+n, second, k+1); //also copying the null
return joint;
It is telling you to do this the C way pretty much:
#include <cstring>
char *cat (const char *s1, const char *s2)
{
// Learn to explore your library a bit, and
// you'll see that there is no need for a loop
// to determine the lengths. Anything C string
// related is in <cstring>.
//
size_t len_s1 = std::strlen(s1);
size_t len_s2 = std::strlen(s2);
char *dst;
// You have the lengths.
// Now use `new` to allocate storage for dst.
/*
* There's a faster way to copy C strings
* than looping, especially when you
* know the lengths...
*
* Use a reference to determine what functions
* in <cstring> COPY values.
* Add code before the return statement to
* do this, and you will have your answer.
*
* Note: remember that C strings are zero
* terminated!
*/
return dst;
}
Don't forget to use the correct operator when you go to free the memory allocated. Otherwise you'll have a memory leak.
Happy coding! :-)

Passing 2-D array with dynamic size between functions in C/++

This is "popular" question so I already checked the similar threads but still didnt resolve my issue.
How can I declare 2-D array to hold "strings" - I need array of array of chars AFAIK - and use it as argument to 5 functions one after another where I can pass it by reference and update the content dynamically so following function can compare it. (I might get 10 "strings" or even empty array, so I want to do it correctly with dynamic array coz array content is different from system to system).
"string" => C style string aka array of chars. MAXLEN < 32;
C solution would be more disirable but if vectors can work, why not.
One possible solution in C is as follows:
char **p_strings = calloc(num_strings, sizeof(*p_strings));
for (i = 0; i < num_strings; i++)
{
// Allocate storage for the i-th string (always leave room for '\0')
p_strings[i] = calloc(len_string[i]+1, sizeof(*p_strings[i]));
}
...
// Call a function
my_function(p_strings, num_strings);
You will need to remember to free all this data when you're done with it.
If you need to alter the length of a string, or change the number of strings, you will have to do some fairly painful reallocation. So if you're working in C++, you should probably just be using a std::vector<std::string>.
std::vector<std::string> strings;
strings.push_back("Foo");
strings.push_back("Bar");
...
my_function(strings);
You can even get const pointers to C-style strings for each element, using c_str().
Assuming C++; for this I see no problem with using a vector to string (the string serves as the second dimension):
void foo(vector<string> v) {
cout << v[0]; // Assuming the elements exist!
}
int main(int argc, char *argv[])
{
vector<string> vString; // Make the vector
vString.push_back("something"); // Add a string
foo(vString); // Print out 'something'
}
In your edit you also described that the only thing that will change would be the actual string, so instead of push_backing your strings when they are needed, you can init the vector with the length:
vector<string> vString(10); // Assuming a size of 10
and then use them normally:
vString[4] = "something";
and (in response to the comment), to resize at runtime:
vString.resize(15); // Make it bigger, generates new blank strings

(C++) Very Basic Questions Regarding Syntax

C++ novice here. I have some basic questions. In int main( int argc, char *argv[] )
How is char *argv[] supposed to be read (or spoken out to humans)?
Is it possible to clear/erase specific content(s), character(s) in this case, of such array? If yes, how?
Can arrays be resized? If yes, how?
How can I copy the entire content of argv[] to a single std::string variable?
Are there other ways of determining the number of words / parameters in argv[] without argc? If yes, how? (*)
I'd appreciate explanations (not code) for numbers 2-5. I'll figure out the code myself (I learn faster this way).
Thanks in advance.
(*) I know that main(char *argv[]) is illegal. What I mean is whether there's at least a way that does not involve argcat all, like in the following expressions:
for( int i = 0; i < argc; ++i ) {
std::cout << argv[i] << std::endl;
}
and
int i = 0;
while( i < argc ) {
std::cout << argv[i] << std::endl;
++i;
}
Or
int i = 0;
do {
std::cout << argv[i] << std::endl;
++i; } while( i < argc );
It's an array of pointers to char.
Sort of - you can overwrite them.
Only by copying to a new array.
Write a loop and append each argv[i] to a C++ string.
Most implementations terminate the array with a NULL pointer. I can't remember if this is standard or not.
char **argv[]
Is wrong. It should be either char **argv or char *argv[], not a mixture of both. And then it becomes a pointer-to-pointer to characters, or rather a pointer to c-strings, i.e., an array of c-strings. :) cdecl.org is also quite helpful at thing like this.
Then, for the access, sure. Just, well, access it. :) argv[0] would be the first string, argv[3] would be the 4th string. But I totally wouldn't recommend replacing stuff in an array that isn't yours or that you know the internals of.
On array resize, since you're writing C++, use std::vector, which does all the complicated allocation stuff for you and is really safe. Generally, it depends on the array type. Dynamically allocated arrays (int* int_arr = new int[20]) can, static arrays (int int_arr[20]) can't.
To copy everything in argv into a single std::string, loop through the array and append every c-string to your std::string. I wouldn't recommend that though, rather have a std::vector<std::string>, i.e., an array of std::strings, each holding one of the arguments.
std::vector<std::string> args;
for(int i=0; i < argc; ++i){
args.push_back(argv[i]);
}
On your last point, since the standard demands argv to be terminated by a NULL pointer, it's quite easy.
int myargc = 0;
char** argv_copy = argv;
while(++argv_copy)
++myargc;
The while(++argv_copy) will first increment the pointer of the array, letting it point to the next element (e.g., after the first iteration it will point at c-string #2 (argv[1])). After that, if the pointer evaluates to false (if it is NULL), then the loop brakes and you have your myargc. :)
Several options: array of pointer to char OR array of C-string.
You can assign to particular characters to clear them, or you can shift the rest of the array forwards to "erase" characters/elements.
Normal C-style arrays cannot be resized. If you need a resizable array in C++ you should use std::vector.
You'll have to iterate over each of the items and append them to a string. This can be accomplished with C++ algorithms such as copy in conjunction with an ostream_iterator used on an ostringstream.
No. If there was such a way, there wouldn't be any need for argc. EDIT: Apparently for argv only the final element of the array is a null pointer.
1) It is supposed to be char **argv or char *argv[] which is a pointer to an array of characters more commonly known as an array of strings
2) CString is the std library to manipulate C strings (arrays of characters). You cannot resize an array without reallocating, but you can change the contents of elements by referencing it by index:
for(int i = 0; i < argc; ++i)
{
//set all the strings to have a null character in the
//first slot so all Cstring operations on this array,
//will consider it a null (empty) string
argv[i] = 0;
}
3) Technically no, however they can be deleted then reallocated:
int *array = new int[15]; //array of size 15
delete[] array;
array = new int[50]; //array of size 50
4) This is one way:
string *myString;
if(argc > 0)
{
myString = new string(argv[0]);
for(int i = 1; i < argc; ++i)
myString->append(argv[i]);
}
5) Yes, according to Cubbi:
POSIX specifies the final null pointer
for argv, see for example "The
application shall ensure that the last
member of this array is a null
pointer." at
pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
Which means you can do:
char *val = NULL;
int i = 0;
do
{
val = argv[i++]; //access argv[i], store it in val, then increment i
//do something with val
} while(val != NULL); //loop until end of argv array
It is spoken as "array of pointers to pointers to character" (note that this is not the signature of the main function, which is either int argc, char **argv or int argc, char *argv[] -- which is equivalent).
The argv array is modifiable (lack of const). It is illegal to write beyond the end of one of the strings though or extend the array; if you need to extend a string, create a copy of it and store a pointer in the array; if you need to extend the array, create a copy of the array.
They cannot be resized per se, but reinterpreted as a smaller array (which sort of explains the answer to the last question).
You will be losing information this way -- argv is an array of arrays, because the individual arguments have already been separated for you. You could create a list of strings using std::list<std::string> args(&argv[1], &argv[argc]);.
Not really. Most systems have argv NULL terminated, but that is not a guarantee.
char *argv[] can be read as: "an array of pointers to char"
char **argv can be read as: "a pointer to a pointer to char"
Yes, you may modify the argv array. For example, argv[0][0] = 'H' will modify the first character of the first parameter. If by "erase/clear" you mean remove a character from the array and everything automatically shift over: there is no automatic way to do that - you will need to copy all the characters one-by-one over to the left (including the NULL termination)
No, arrays cannot be resized. You will need to create a new one and copy the contents
How do you want to represent ALL the parameter strings as 1 std::string? It would make more sense to copy it to an array of std::strings
No, there is no special indication of the last entry in the array. you need to use argc
Array in C/C++ is not an object, but just a pointer to first element of array, so you cannot simply delete or insert values.
Answering your questions:
char *argv[] can be read as 'array of pointers to char'
It's possible, but involves direct manipulations with data in memory, such as copying and/or moving bytes around.
No. But you may allocate new array and copy necesary data
By manually copying each element into std::string object
No.
As a summary: C++ is much more low-level language that you think.