I am creating a dynamic array inside a function. The code (posted below) runs without any problem. I am wondering if the way I have written is the right way to do it or if it will create problems in more complicated codes in the future. I am aware that the specific task my program (below) is trying to achieve is better effected with strings or vectors. But I created this artificial example to get my question across. however, if you strongly feel that dynamic arrays should be avoided, please feel free to share your opinion and the reasoning for it.
Result of my prior research: I was unable to find a coherent discussion on legality and morality of creating dynamic arrays using new [] and later deleting them in a different scope.
Thanks for your thoughts and insights.
My example code follows:
==========================
#include<iostream>
#include<string>
#include<cctype>
using namespace std;
void getNonPunct(string _str, char* &_npcarr, int &_npsize);
int main()
{
string input_string;
char* npchar_arr;
int npsize;
cout << "Enter any string: ";
getline(cin, input_string);
getNonPunct(input_string, npchar_arr, npsize);
// Now display non-punctuation characters in the string
cout << "string with non-punctuation characters removed:\n";
for (int n = 0; n <= npsize - 1; n++)
cout << npchar_arr[n];
cout << "\n(" << npsize << ") non-punctuation characters\n";
// Now return the memory allocated with 'new' to heap
delete [] npchar_arr;
// Is it okay to 'delete' npchar_arr eve if it was created in the function
// getNonPunct() ?
return(0);
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void getNonPunct(string _str, char* &_npcarr, int &_npsize)
//This void function takes an input array of strings containing arbitrary
//characters and returns a pointer to an array of characters containing only
//the non-punctuation characters in the input string. The number of
//non-punctuation characters are stored in size. Prior to the function call,
//int_arr and size are undefined. After the function call, char_arr points to
//the first location of an array of the non-punctuation character array.
//'size' is equal to the number of non-punctuation characters in the input
//string.
{
// First get the number of non-punctuation characters in the string
int str_len, npcount = 0;
str_len = static_cast<int>( _str.length() );
_npsize = 0;
for (int i = 0; i <= str_len - 1; i++)
{
if ( !ispunct(_str[i]) )
_npsize++;
}
// Now that you know how many non-punctuation characters are in the string,
// create a (dynamic) character array of _npsize.
_npcarr = new char [_npsize];
for (int k = 0; k <= str_len - 1; k++)
{
if ( !ispunct(_str[k]) )
_npcarr[npcount++] = _str[k];
}
return;
}
Is it valid? Yes. The array pointed to by npchar_arr exists until you destroy it and it is okay to destroy it using a delete[] expression in another function.
Is it a good idea? No. You would be much better off using a smart pointer that manages the lifetime of the object automatically, releasing you from the responsibility of delete[]ing the pointer manually yourself.
Consider using a std::unique_ptr<char[]> if your compiler and Standard Library support unique_ptr, or std::auto_ptr or std::shared_ptr if you cannot use unique_ptr (shared_ptr may also be found in Boost and C++ TR1).
Allocating in one scope and deleting in another is one of the major reasons for using dynamic allocation instead of an automatic local variable.
As James says, prefer to use smart pointers to track dynamically allocated objects.
The only constraint I can think of on allocation in one scope and deallocation in another is multi-component applications where different components are built with different compilers (or maybe different versions of the same compiler). On Windows, each compiler provides its own memory management library, and you shouldn't free memory from a different component than the one that allocated it. Or you can use Windows-provided memory management routines which are common to all compilers (e.g. HeapAlloc/HeapFree). On Linux, everyone uses the glibc-supplied malloc, free, new[], and delete[], so it isn't such an issue. For other OSes, you need to investigate.
This example is very trivial, but image the following modification of your code:
int main()
{
string input_string;
char* npchar_arr;
int npsize;
cout << "Enter any string: ";
getline(cin, input_string);
getNonPunct(input_string, npchar_arr, npsize);
// Now display non-punctuation characters in the string
cout << "string with non-punctuation characters removed:\n";
for (int n = 0; n <= npsize - 1; n++)
cout << npchar_arr[n];
cout << "\n(" << npsize << ") non-punctuation characters\n";
return(0); // WHOOPS! I JUST RETURNED AND DIDN'T FREE THE MEMORY
delete [] npchar_arr;
// Is it okay to 'delete' npchar_arr eve if it was created in the function
// getNonPunct() ?
return(0);
}
I just returned before freeing the memory, which results in a memory leak! Of course, you might be thinking that you would never do such a thing, but imagine a much more complex program, it is easy to make such a mistake as the above, and using smart pointers as the other answers suggest, would alleviate you of having to worry about such tasks.
Related
I am trying to create a string by accessing its individual index positions and trying to print it.
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s="";
s[0]='a';
s[1]='b';
s[2]='c';
s[3]='d';
cout << s << endl;
for(int i = 0; i < 4; i++)
cout << s[i] << endl;
return 0;
}
This doesn't print a entire string but does print its individual characters.
Following code prints both the strings and its individual characters.
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s=" ";
s[0]='a';
s[1]='b';
s[2]='c';
s[3]='d';
cout << s << endl;
for(int i = 0; i < 4; i++)
cout << s[i] << endl;
return 0;
}
Why does this happen. Links to any further reading are appreciated.
Strings don't grow automatically
This string has zero size
string s="";
This code is an error because s[0] does not exist (since the string has zero size).
s[0]='a';
Because this is an error your program has undefined behaviour, which means any output is possible, including the strange behaviour you see.
If you want to add a character to a string use push_back (or +=)
s.push_back('a');
s.push_back('b');
s.push_back('c');
s.push_back('d');
Now that the string has some characters you can use [] to access them (or change them).
String, similarly to some other STL(Standard template library) classes is a dynamic template class.
What you're doing in your code is creating an empty string and then trying to insert elements into non-existing indices, which in an STL type class means you try to access a memory which was not allocated.
What you want to do when inserting elements into your string is use the push_back method, which will insert elements to the end of your string (and handle the memory allocation automatically).
If you know how many elements will be inserted before performing those actions, you can minimize the number of memory allocations by using the reserve method before using push_back.
For example, in your code we could opt to something like this:
s.reserve(5);
s.push_back('a');
s.push_back('b');
s.push_back('c');
s.push_back('d');
If we do not reserve memory ahead there could be more allocations and de-allocations as the string increases in size which can be expensive.
The problem is that I would like to allocate memory dynamically according to the size of user's input.Please note that the user does not specify the size of input. We have to calculate how long it was, and then only allocate the exact amount of memory for it.What I'm looking for is something like this:
char* input_str = NULL; // No initial size whatsoever
cout<<"\nEnter the string: ";
//Determine how long the string entered was, and allocate memory for it
//make input_str point to it.
May be this can help us write our version of std::string ?
Just use a std::string, which does all of this for you:
std::cout << "\nEnter the string: ";
std::string input;
// To get a word:
std::cin >> input;
// To get a line of input:
std::getline(std::cin, input);
You won't need to worry about memory allocation at all.
Internally, these functions will call std::cin.rdbuf() to get access to the underlying stream buffer and read character by character until the stopping condition is met. As it does this, it grows some internal storage than it (de)allocates with new[] and delete[].
An alternative simple (but limited and potentially dangerous) approach you might use, is to allocate a buffer that is as large as you're ever going to need (something like new char[100] and read the input into it (with something like std::cin.read or std::cin.getline, etc.). Then you can determine how many characters were inserted into the buffer, allocate some storage of the correct size, transfer your input into this storage, and then deallocate the old buffer. With this approach, you have to be extra careful to avoid buffer overflows which may introduce security vulnerabilities to your program, and you are of course limited to a particular maximum input size.
As I understand the question, in particular the “May be this can help us write our version of std::string ”, it's about
doing what std::getline from the <string> header does, to see what that involves.
That's already discussed in a good way by Bjarne Stroustrup in his paper “Learning Standard C++ as a New Language”, except that Bjarne discusses input via the >> operator, which only inputs a single whitespace-delimited word.
Bjarne starts with pseudo-code for a hypothetical student's exercise:
write a prompt "Please enter your first name"
read the name
write out "Hello <name>"
He then presents one possible C++ solution:
#include<iostream> // get standard I/O facilities
#include<string> // get standard string facilities
int main()
{
using namespace std; // gain access to standard library
cout << "Please enter your first name:\n";
string name;
cin >> name;
cout << "Hello " << name << '\n';
}
And after some discussion he presents a C style solution, a DIY C style program to do just about the same as the C++ style solution:
#include<stdio.h>
#include<ctype.h>
#include<stdlib.h>
void quit() // write error message and quit
{
fprintf(stderr," memory exhausted\n") ;
exit(1) ;
}
int main()
{
int max = 20;
char* name = (char*) malloc(max) ; // allocate buffer
if (name == 0) quit();
printf("Please enter your first name:\n");
while (true) { // skip leading whitespace
int c = getchar();
if (c == EOF) break; // end of file
if (!isspace(c)) {
ungetc(c,stdin);
break;
}
}
int i = 0;
while (true) {
int c = getchar() ;
if (c == '\n' || c == EOF) { // at end; add terminating zero
name[i] = 0;
break;
}
name[i] = c;
if (i== max-1) { // buffer full
max = max+max;
name = (char*)realloc(name, max) ; // get a new and larger buffer
if (name == 0) quit() ;
}
i++;
}
printf("Hello %s\n",name);
free(name) ; // release memory
return 0;
}
The two programs are not exactly equivalent: the C++ style first program only reads a single “word” of input, while the C program skips whitespace and then reads a complete line of input. But it illustrates what's involved for doing this yourself. In short, better use C++ style. ;-)
Allocate an array with space for 1 or more characters. Read characters from your input source into the array until the array runs out of space, or you reach your terminating character. If you ran out of space, allocate a new array twice the size, copy the characters over, and deallocate the old array. Then go back to reading characters again. Repeat until you reach your terminating character (or whatever other condition you want to stop at).
You need to either store the whole user string in a temporary buffer with a max size, or read the input character by character.
Note that since you are using C++, it probably doesn't make any sense to allocate the memory manually: you could use std::string instead.
I have a problem I cannot really understand how it could exist.
I have a bunch of files ordered by time and containing a bunch of objects. The result should be one file per time ordered in a directory per object.
It works quite fine but at the point where I convert the Outputstring to a char[] to use fstream.open(), the array has 3 characters more than the string has.
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
int main()
{
string strOutput;
char *OutputFile;
short z;
strOutput = "/home/.../2046001_2013-02-25T0959.txt";
cout << strOutput << endl;
OutputFile = new char[strOutput.length()];
z = 0;
while (z < strOutput.length())
{
OutputFile[z] = strOutput[z];
z++;
}
cout << OutputFile << endl;
return 0;
}
The first output is always correct but the second sometimes has the end .txt60A, .txt5.a or .txt9.A.
When it occurs its always the same object and time and it happens every try. But not every object does that.
For obvious reasons I cannot reproduce this error in this minimal code snippet, but I also don't want to post the whole 390 lines of code.
Do you have any suggestions?
You are missing terminating null at the end of C string. To fix:
OutputFile = new char[strOutput.length() + 1]; // notice +1
z = 0;
while (z < strOutput.length())
{
OutputFile[z] = strOutput[z];
z++;
}
OutputFile[z] = 0; // add terminating 0 byte
Of course there are better ways to do the whole thing... you don't really need to copy at all, just get rid of OutputFile and the whole loop, and use the char array inside std::string:
cout << strOutput.c_str() << endl;
I assume the real code wants a C string. std::cout can print std::string directly, of course:
cout << strOutput << endl;
If you actually want to create a copy, it's best to just copy std::string and store that, and use c_str-method to get the C buffer when you need it:
string OutputFile = strOutput;
If you know you really do need a raw char array allocated from heap, you should use std::unique_ptr (or possibly some other C++ smart pointer class) to wrap the pointer, so you do not need to delete manually and avoid memory leaks, and also use standard library function to do copying:
#include <memory>
#include <cstring>
...
unique_ptr<char[]> OutputFile(new char[strOutput.length() + 1];
::strcpy(OutputFile, strOutput.c_str()); // :: means top level namespace
Char arrays need an extra null character or \0 appended to the end, otherwise the code reading the string will run past the end of the array until it finds one.
OutputFile = new char[strOutput.length() + 1];
z = 0;
while (z < strOutput.length())
{
OutputFile[z] = strOutput[z];
z++;
}
OutputFile[z] = '\0';
It may appear to work if the next byte after the array happens to be a null, but that's just a coincidence. I'm sure that's why your code works on the first pass.
at the point where I convert the Outputstring to a char[] to use fstream.open()
You don't have to do that. Do something like this instead:
outfile.open(Outputstring.c_str(), std::fstream::out)
Of course, if you have a C++11-compliant compiler, you can just do:
outfile.open(Outputstring, std::fstream::out)
I am creating int array using c++ and trying to get the length of it
int *masterArray;
int count = 0;
int a = 0;
int var = 0;
ifstream myfile("sample_10.txt");
if (myfile.is_open())
{
while(myfile.good())
{
string word;
while(getline(myfile, word))
{
count++;
}
cout << "count: " << count << endl;
masterArray = new int [count];
myfile.clear();
myfile.seekg(0);
while(getline(myfile, word, '\n'))
{
cout << word << " ";
istringstream ( word ) >> var;
masterArray[a] = var;
a ++;
}
}
}
name of the int array is master array, and after I add variables in the array
I do..
cout << "sizeof(masterArray) : " <<sizeof(masterArray);
which gives me 8, instead of 10.
I tried to print out all variables stored in the array and it gives me 10, which means all variables are stored correctly.
should I retrieve the length by doing
cout << "sizeof(masterArray) : " <<sizeof(masterArray) / sizeof(*masterArray);
??
Because that gives me 2 (obviously, cuz it is dividing 8 by 4)
Thanks
Your masterArray variable is of pointer type. I suppose you are on a 64bit machine, so the pointers are 8 bytes. That's why it gives you 8 when you do a sizeof().
There is no standard way of getting the size of an array, at least not that I know of. You have a count that you get from the user and allocate the array with. I guess it would be best to keep that and use it.
I would suggest to use std::vector in your case. Note, that in C++ it is a common practice to use vectors for any array-like objects. You should have very strong arguments if you want to manage dynamically allocated arrays by yourself.
You already got the length - it's count. It's the only way of knowing a length of dynamically allocated array, by manually keeping track of it. As others have pointed out, you only get a pointer to the first element if you allocate an array via new. sizeof(masterArray) will return the size of this pointer and it happens to be 8 bytes on your platform.
Im guessing you are using 64 bit computer? sizeof returns the size of the variable it given in this case a pointer in other words a memory address which in 64 bit computer is equal to 8 bytes. In order to find the length of the array in c you need to use another variable with the size of the array stored in it.
This
sizeof(masterArray);
gives you the size of an int*, which is 8 on your platform (or 64 bits, assuming an 8 bit char).
Looking at your code, it seems to me that you could use std::vector instead of the array, and add elements using the std::vector::push_back method. If you actually needed the length, you could get it from the size() method, but what you normally do with a vector is to iterate over its contents using it's begin and end iterators (see methods begin() and end() respectively).
Using sizeof to get the number of elements in an array will only work for variables that are declared as arrays. E.g. int masterArray[10];.
Your array is declared as an int * - later allocated dynamically - so you're getting the size of that type.
sizeof() only works on arrays. You're getting 8 because a pointer is 8 bytes (on a 64 bit system). You cannot determine the length of a dynamically allocated array. This means that you need to keep track of the length, rather than re-determining it.
By the way, this looks like a very good situation to use a vector.
Here's your example rewritten to using std::vector
std::vector<int> masterArray;
int var = 0;
ifstream myfile("sample_10.txt");
if (myfile.is_open())
{
while(myfile.good())
{
string word;
while(getline(myfile, word))
{
count++;
}
cout << "count: " << count << endl;
masterArray.reserve(count); // will allocate at least count ints
myfile.clear();
myfile.seekg(0);
while(getline(myfile, word, '\n'))
{
cout << word << " ";
istringstream ( word ) >> var;
masterArray.push_back(var); // store the current word
}
}
}
How can I access elements from myVector like i would do with arrays ( for(i = 0; i < n; i++) cout << v[i] << " "; )
My code:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Month
{
public:
char *name;
int nr_days;
Month(char* c, int nr) : name(c), nr_days(nr){};
~Month() { /* free(name); */}
};
int main()
{
Month January("January", 31);
Month February("February", 28);
Month March("March", 31);
Month April("April", 30);
Month May("May", 31);
Month June("June", 30);
Month July("July", 31);
Month August("August", 31);
Month September("September", 30);
Month Octomber("Octomber", 31);
Month November("November", 30);
Month December("December", 31);
vector<Month> *myVect = new vector<Month>;
myVect->push_back(January);
myVect->push_back(February);
myVect->push_back(March);
myVect->push_back(April);
myVect->push_back(May);
myVect->push_back(June);
myVect->push_back(July);
myVect->push_back(August);
myVect->push_back(September);
myVect->push_back(Octomber);
myVect->push_back(November);
myVect->push_back(December);
for(vector<Month>::const_iterator i = myVect->begin(); i != myVect->end(); i++)
{
/*
Month myMonth = i;
cout << myMonth.name << " " << myMonth.nr_days << endl;
*/
}
free(myVect);
return 0;
}
I would want to be something like a foreach algorithm: foreach(Month in myVect) cout << ...
And another question: why it gives me a run-time error at the destructor if I uncomment my line?
Ok, there are a lot of problems here.
You declare myVect as a pointer to a vector. This is unnecessary. One of the major benefits of using a vector is so that you don't have to worry about memory management as the vector does it for you. You stack allocate the vector, but internally it heap allocates the memory used to store the items it contains.
You never initialize the pointer. You are invoking undefined behavior as that pointer is not valid. To initialize a pointer you use new. All you have is an invalid stack allocated pointer that does not point to a vector on the heap. EDIT: I just realized that the new was edited out, so you can disregard this one. Still, it shouldn't be a pointer at all.
You are using free to deallocate a C++ class (that you never allocated to begin with...). Don't. This isn't C, you use new and delete to manage memory (when necessary!) in C++. free does not call destructors, it simply frees up a chunk of memory. delete on the other hand does as it knows how to deal with complex C++ types. Never mix new/delete with malloc/free.
myVect->begin() returns a const_iterator, not a T (i.e., in this case, not a Month object). Dereferencing the iterator via the * operator will yield the current iteration object, so:
Month myMonth = *i // <--- IMPORTANT!
As an aside, if you are going to be looping over the vector often you may want to typedef the iterator to reduce verbosity, i.e.,
typedef vector<Month>::const_iterator cmonth_iter;
Now you can write
for(cmonth_iter i = myVect.Begin(); i != myVect.end(); ++i )
{
Month m = *i;
// do stuff with m
}
You can access elements using iterator using the * operator:
for(vector<Month>::const_iterator i = myVect->begin(); i != myVect->end(); i++)
{
Month myMonth = *i;
cout << myMonth.name << " " << myMonth.nr_days << endl;
}
Also, you never allocate a vector in your code. You shouldn't use free() on a pointer you haven't received from malloc() earlier. It is undefined behavior to do otherwise and a run-time error is likely to occur at the point you call free().
Try this:
vector<Month> *myVect = new vector<Month>;
...
delete myVect;
If you remove the unitialized pointer bug by changing:
vector<Month> *myVect;
to:
vector<Month> myVect;
Then this will work. (Once you define ostream << Month)
for(i = 0; i < myVect.size(); i++)
cout << v[i] << " ";
You have a pointer myVect, but never assign a value to it before using (turn compiler warnings on). you should do something like myVect = new vector<Month>(). (or do not make it pointer and change -> into .). The rest of your "foreach" implementation looks fine. And you can use [] to access elements as well.
You free constant strings, you did not allocate them, so you need not to free them either.
You're declaring myVect as a pointer but never allocating it, that's going to give you lots of trouble. Just drop the * and you should be fine.
If you insist, you can use an index just like you would with an array:
for(int i = 0; i < myVect.size(); i++)
{
Month myMonth = myVect[i];
cout << myMonth.name << " " << myMonth.nr_days << endl;
}
Although I'd rather use iterators as you have done - just one simple fix:
Month myMonth = *i;
You can use the arrow operator with iterators...
for(vector<Month>::const_iterator i = myVect->begin(); i != myVect->end(); i++)
{
cout << i->name << " " << i->nr_days << endl;
}
note also that it's more idiomatic with iterators using ++i instead of i++ (the reason is that i++ will need to create a copy of the iterator that will be thrown away).
Note also that your code is UB (undefined behavior) because you are using a pointer to a vector, but you are not allocating it. By the way the use of a pointer in this case is nonsense, the code would be correct and simpler with:
vector<Month> myVect;
myVect.push_back(January);
myVect.push_back(February);
...
for(vector<Month>::const_iterator i = myVect.begin(); i != myVect->end(); ++i)
...
My suggestion is also to avoid to try learning C++ just by experimenting with a compiler (something that I've the impression you're trying to do).
C++ is powerful but also complex and unfortunately quite illogical and asymmetrical in many parts (due to its evolution history). Add to this that when you make a mistake (e.g. not allocating the vector in your original code) you cannot expect the compiler to help you and even at runtime the program may do ANYTHING, including apparently work as you expected (the worst possible thing). This combo is deadly.
Complexity, asymmetry and lack of runtime checks all make C++ impossible to learn by experimentation... just get a good book and read it. It's much simpler this way.