storing char pointers inside a vector - c++

On rare occasions my program crashes and i don't know why.
I think it might be related to how i store char pointers inside a vector.
vector<char*> vec;
// here i store text for later
void pushToVector(const char *text) {
char *t = new char[strlen(text)+1];
strcpy(t, text);
vec.push_back(t);
}
// now i want to print the first element and then erase it
void print() {
if (vec.size() < 1) return;
printf("print: %s", vec.front());
char *t = vec.front();
vec.erase(vec.begin(), vec.begin()+1);
delete[] t;
}
will this always work? or am i doing something wrong?

The code seems to be ok, although it is rather risky code. These are the unsafe points I see:
You do not check if the parameter text is really a null-terminated string
Working with pure pointers
No clear ownership of the string object on the heap (I wouldn't expect a print function to delete something)
Unless you are bound to C-style strings I would strongly recommend to use std::string.

Related

Can't add item to const char* vector? C++

I have an issue where I need a vector of const char* but for some reason whenever I try adding something nothing happens. Here is the code sample in question.
std::vector<const char*> getArgsFromFile(char* arg) {
std::ifstream argsFile(arg);
std::vector<const char*> args;
while (!argsFile.eof()) {
std::string temp;
argsFile >> temp;
args.push_back(temp.c_str());
}
args.pop_back();
return args;
}
The strange part is if I make this change
std::vector<const char*> getArgsFromFile(char* arg) {
std::ifstream argsFile(arg);
std::vector<const char*> args;
while (!argsFile.eof()) {
std::string temp;
argsFile >> temp;
const char* x = "x";
args.push_back(x);
}
args.pop_back();
return args;
}
It will add 'x' to the vector but I can't get the value of temp into the vector. Any thoughts? Help would be greatly appreciated. Thanks!
A const char* is not a string, but merely a pointer to some memory, usually holding some characters. Now std::string under the hood either holds a small region of memory (like char buff[32]) or, for larger strings, keeps a pointer to memory allocated on the heap. In either case, a pointer to the actual memory holding the data can be obtained via string::c_str(). But when the string goes out of scope that pointer no longer points to secured data and becomes dangling.
This is the reason why C++ introduced methods to avoid direct exposure and usage of raw pointers. Good C++ code avoid raw pointers like the plague. Your homework is for poor/bad C++ code (hopefully only to learn the problems that come with such raw pointers).
So, in order for the pointers held in your vector to persistently point to some characters (and not become dangling), they must point to persistent memory. The only guaranteed way to achieve that is to dynamically allocate the memory
while (!argsFile.eof()) {
std::string temp;
argsFile >> temp;
char* buff = new char[temp.size()+1]; // allocate memory
std::strncpy(buff,temp.c_str(),temp.size()+1); // copy data to memory
args.push_back(buff); // store pointer in vector
}
but then the memory allocated in this way will be leaked, unless you de-allocate it as in
while(!args.empty()) {
delete[] args.back();
args.pop_back();
}
Note that this is extremely bad C++ code and not exception safe (if an exception occurs between allocation and de-allocation, the allocated memory is leaked). In C++ one would instead use std::vector<std::string> or perhaps std::vector<std::unique_ptr<const char[]> (if you cannot use std::string), both being exception safe.
Use a standard-library-based implementation
Guideline SL.1 of the C++ coding guidelines says: "Use the standard library whenever possible" (and relevant). Why work so hard? People have already done most of the work for you...
So, using your function's declaration, you could just have:
std::vector<std::string> getArgsFromFile(char* arg) {
using namespace std;
ifstream argsFile(arg);
vector<string> args;
copy(istream_iterator<string>(argsFile),
istream_iterator<string>(),
back_inserter(args));
return args;
}
and Bob's your uncle.
Still, #Walter's answer is very useful to read, so that you realize what's wrong with your use of char * for strings.

Understanding pointers used for out-parameters in C/C++

How do I return a char array from a function?
has the following code in the answers:
void testfunc(char* outStr){
char str[10];
for(int i=0; i < 10; ++i){
outStr[i] = str[i];
}
}
int main(){
char myStr[10];
testfunc(myStr);
// myStr is now filled
}
Since I will be using an Arduino where memory is precious I dont want a "temporary variable" for storing some string and then copying it over. I also don't want the function to return anything. I want to use the idea above and do something like:
void testfunc(char* outStr){
outStr="Hi there.";
}
int main(){
char myStr[10];
testfunc(myStr);
}
However, in this case myStr is empty!
Why doesn't this work? How do I fix it?
(I'm relatively new to C, and do have a basic understanding of pointers)
Thanks.
Why doesn't this work?
void testfunc(char* outStr){
outStr="Hi there.";
}
You have:
testfunc(myStr);
When testfunc is called, outStr is assigned the address of the first element of myStr. But in the testfunc function, you overwrite outStr and it now points to a string literal. You are only modifying ouStr, not myStr.
How do I fix it?
To copy a string, use strcpy function (or its secure sister strncpy):
void testfunc(char* outStr){
strcpy(ouStr, "Hi there.");
}
If you want memory reuse - and this is a dangerous place, has you must take really good care about allocation/deallocation responsibility, you should use pointer to string, ie:
#define STATIC_STRING "Hi there"
void testfunc(char**outStr){
*outStr=STATIC_STRING;
}
int main(){
char*myStr;
testfunc(&myStr);
//From now on, myStr is a string using preallocated memory.
}

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! :-)

C++ error - returning a char array

Consider the following code:
char CeaserCrypt(char str[256],int key)
{
char encrypted[256],encryptedChar;
int currentAsci;
encrypted[0] = '\0';
for(int i = 0; i < strlen(str); i++)
{
currentAsci = (int)str[i];
encryptedChar = (char)(currentAsci+key);
encrypted[i] = encryptedChar;
}
return encrypted;
}
Visual Studio 2010 gives an error because the function returns an array. What should I do?
My friend told me to change the signature to void CeaserCrypt(char str[256], char encrypted[256], int key). But I don't think that is correct. How can I get rid of the compile error?
The return type should be char * but this'll only add another problem.
encrypted is "allocated" on the stack of CeaserCrypt and might not be valid when the function returns. Since encrypted would have the same length as the input, do:
int len = strlen(str);
char *encrypted = (char *) malloc(len+1);
encrypted[len] = '\0';
for (int i = 0; i < len; i++) {
// ...
}
Don't forget to deallocate the buffer later, though (with free()).
EDIT: #Yosy: don't feel obliged to just copy/paste. Use this as a pointer to improve your coding practice. Also, to satisfy criticizers: pass an already allocated pointer to your encryption routine using the above example.
It wants you to return a char* rather than a char. Regardless, you shouldn't be returning a reference or a pointer to something you've created on the stack. Things allocated on the stack have a lifetime that corresponds with their scope. After the scope ends, those stack variables are allowed to go away.
Return a std::vector instead of an array.
std::vector<char> CeaserCrypt(char str[256],int key)
{
std::vector<char> encrypted(256);
char encryptedChar;
int currentAsci;
encrypted[0] = '\0';
for(int i = 0; i < strlen(str); ++i)
{
currentAsci = (int)str[i];
encryptedChar = (char)(currentAsci+key);
encrypted[i] = encryptedChar;
}
return encrypted;
}
There's another subtle problem there though: you're casting an integer to a character value. The max size of an int is much larger than a char, so your cast may truncate the value.
Since you're using C++ you could just use an std::string instead. But otherwise, what your friend suggested is probably best.
There are a few problems here. First up:
char CeaserCrypt(char str[256],int key)
As others have pointed out, your return type is incorrect. You cannot return in a single character an entire array. You could return char* but this returns a pointer to an array which will be allocated locally on the stack, and so be invalid once the stack frame is removed (after the function, basically). In English, you'll be accessing that memory address but who knows what's going to be there...
As your friend suggested, a better signature would be:
void CeaserCrypt(char* encrypted, const char str*, const size_t length ,int key)
I've added a few things - a size_t length so you can process any length string. This way, the size of str can be defined as needed. Just make sure char* encrypted is of the same size.
Then you can do:
for(int i = 0; i < length; i++)
{
// ...
For this to work your caller is going to need to have allocated appropriately-sized buffers of the same length, whose length you must pass in in the length parameter. Look up malloc for C. If C++, use a std::string.
If you need C compatibility make encrypted string function argument.
If not, than use C++ std::string instead C style string.
And also In your code encrypted string isn't ending with '\0'
The problem with the original code is that you are trying to return a char* pointer (to which your local array decayed) from a function that is prototyped as one returning a char. A function cannot return arrays in C, nor in C++.
Your friend probably suggested that you change the function in such a way, that the caller is responsible for allocation the required buffer.
Do note, that the following prototypes are completely equal. You can't pass an array as a parameter to normal function.
int func(char array[256]);
int func(char* array);
OTOH, you should (if you can!) decide the language which you use. Better version of the original (in C++).
std::vector<unsigned char> CeaserCrypt(const std::string& str, const int key)
{
std::vector<unsigned char> encrypted(str.begin(), str.end());
for (std::vector<unsigned char>::iterator iter = vec.begin();
iter != vec.end(); ++iter) {
*iter += key;
}
return vec;
}
Do note that overflowing a signed integer causes undefined behavior.
VS2010 is "yelling" at you because you are trying to return a value that is allocated on the stack, and is no longer valid once your function call returns.
You have two choices: 1) Allocate memory on the heap inside your function, or 2) use memory provided to you by the caller. Number 2 is what your friend in suggesting and is a very good way to do things.
For 1, you need to call malloc() or new depending on whether you are working in C or C++. In C, I'd have the following:
char* encrypted = malloc(256 * sizeof(char));
For C++, if you don't want to use a string, try
char* encrypted = new char[256];
Edit: facepalm Sorry about the C noise, I should have looked at the question more closely and realized you are working in C++.
You can just do your Ceaser cipher in place, no need to pass arrays in and out.
char * CeaserCrypt(char str[256], int key)
{
for(unsigned i = 0; i < strlen(str); i++)
{
str[i] += key;
}
return str;
}
As a further simplification, skip the return value.
void CeaserCrypt(char str[256], int key)
{
for(unsigned i = 0; i < strlen(str); i++)
{
str[i] += key;
}
}
well what you're returning isn't a char, but a char array. Try changing the return type to char*(char* and a char array are ostensibly the same thing for the compiler)
char* CeaserCrypt(char str[256],int key)
EDIT: as said in other posts, the encrypted array will probably not be valid after the function call. you could always do a new[] declaration for encrypted, remembering to delete[] it later on.

C++ new & delete and string & functions

Okay the previous question was answered clearly, but i found out another problem.
What if I do:
char *test(int ran){
char *ret = new char[ran];
// process...
return ret;
}
And then run it:
for(int i = 0; i < 100000000; i++){
string str = test(rand()%10000000+10000000);
// process...
// no need to delete str anymore? string destructor does it for me here?
}
So after converting the char* to string, I don't have to worry about the deleting anymore?
Edit: As answered, I have to delete[] each new[] call, but on my case its not possible since the pointer got lost, so the question is: how do I convert char to string properly?
Here you are not converting the char* to a [std::]string, but copying the char* to a [std::]string.
As a rule of thumb, for every new there should be a delete.
In this case, you'll need to store a copy of the pointer and delete it when you're done:
char* temp = test(rand()%10000000+10000000);
string str = temp;
delete[] temp;
You seem to be under the impresison that passing a char* into std::string transfers ownership of the allocated memory. In fact it just makes a copy.
The easiest way to solve this is to just use a std::string throughout the entire function and return it directly.
std::string test(int ran){
std::string ret;
ret.resize(ran - 1); // If accessing by individual character, or not if using the entire string at once.
// process... (omit adding the null terminator)
return ret;
}
Yes, yes you do.
If you are using linux/os x, look into something like valgrind which can help you with memory issues
You can change your test function so that it returns a string instead of char *, this way you can delete [] ret in the test function.
OR you could just use a string in test as well and not have to worry about new/delete.
You must call delete for every new otherwise you will leak memory. In the case you have shown you are throwing away the pointer, if you must leave the function as returning a char* then you will need to use two lines to create the std::string so you can retain a copy of the char* to delete.
A better solution would be to rewrite your test() function to return a std::string directly.
You need to do something like this:
for(int i = 0; i < 100000000; i++){
int length = rand()%10000000+10000000;
char* tmp = test(length);
string str(tmp);
delete[length] tmp;
}
This deletes the allocated char-array properly.
By the way, you should always zero-terminate a string if you create it this way (i.e. inside the function test), otherwise some functions can easily get "confused" and treat data behind your string as part of it, which in the best case crashes your application, and in the worst case creating a silent buffer overflow leading to undefined behaviour at a later point, which is the ultimate debugging nightmare... ;)