I am trying to work with an array of char pointers.
Let's say I dynamically declare such an array like so:
int numrows=100;
char** array = new char*[numrows];
And then I populate it by using getline to get strings from a file, converting the strings to char arrays, then setting a pointer in my array to point to said char array like so:
string entry;
int i=0;
while (getline(file,entry)){
char* cstring = new char[entry.length()];
array[i]=strncpy(cstring,entry.c_str(),entry.length());
free(cstring);
i++;
}
(this works, but is there a better way to do this?)
The problem is, I don't know how to grow the array once i becomes greater than numrows.
I know how to do this for a single-dimensional array, but the two-dimensionality is throwing me off.
I'm thinking I should be able to grow it the way you would grow a single-dimension array, right?
if (i==numrows){
char** temp = new char*[numrows+numrows];
for (int j=0;j<i;j++){
char* cstring = new char[strlen(array[i])];
temp[i]=strncpy(cstrin,array[i],strlen(array[i]));
free(cstring);
}
delete [] array;
array = temp;
}
So if the current array becomes full, make a second array that is twice the size of the current array and fill it with the contents of the current array. Then delete array and let array point to temp. I'm fine up to making temp the new array. I can get the contents of array into temp, but when I delete array and set array = temp, the contents of array aren't the contents of temp.
So my question is how can/should I be growing this dynamic array of char pointers?
use std::vector - it is your friend
std::vector<std::string> arr;
while(getline(file, entry))
{
arr.push_back(entry);
}
done
sort can be done using vector sort with custom compare
bool less3(const std::string &s1, const std::string &s2)
{
return s1.compare(0, 3, s2, 0, 3) == 0;
}
std::sort(arr.begin(), arr.end(), less3);
I bet that less3 could be made more efficient but readability wins unless you really suffer
edit fixed as per nice comment from gman
Apart from the remarks by Tyler McHenry, and the fact that you should use the STL, the problem is most likely that you are freeing each cstring after having copied it. Perhaps you intended to free the original string instead?
for (int j=0;j<i;j++){
char* cstring = new char[strlen(array[i])];
temp[i]=strncpy(cstring,array[i],strlen(array[i]));
delete[] array[i];
}
When you first populate the array, DO NOT call free() on the string. First of all, you should use delete[], but more importantly, you still want to access that string later, right?
now i see you say that this is a class that does allow you to use STL. A class teaching c++ that forbids one of the major language features! - anyway passing along
You do not need to be copying the strings to resize the array , you just need to copy the pointers.
if (i==numrows){
char** temp = new char*[numrows+numrows];
for (int j=0;j<i;j++){
temp[j]=array[j];
}
delete [] array;
array = temp;
}
i am sure there are still out by ones there - left as homework
Related
I have an array of a structure (with the parameters of name and number), and the initial array takes in elements from a document that I've made. The initial list size starts at 1000. When the list fills up, I call another method that I'm struggling with. I would like for it to copy the data into a new array that doubled the size, and then delete the old array.
If I name it: array1 and array2, I have my program use array1 throughout. I need help with the pointers that would get array2 to work as array1.
Is there a way to copy the array to a temp array of the same or new size, and then remake the initial array reassigning back to that? For this exercise, I can't use vectors. While I know how to use them, and that they solve this issue while being better, I'm trying to do it with only arrays.
using namespace std;
struct Information {
char functionality;
int SSN;
string name;
};
int numPeople = 1000;
//Gets called if the initial array (whatever size) is filled
void doubleArray(Information *array){
numPeople = numPeople * 2;
//Will now be the doubled array size
Information temp[numPeople]
for(int i = 0; i < numArray; i++){
temp[i].SSN = array[i].SSN;
temp[i].name = array[i].name;
}
//Normally makes it crash
delete[] array;
}
edit: This is what I currently have
void doubleArray(Information *person){
numPeople = numPeople * 2;
Information* temp = new Information[numPeople];
memcpy(temp, person, numPeople);
delete[] person;
person = temp;
}
It gets to numPeople = 1000 (the initial list size) but then crashes shortly after. Is the doubling array correct?
Arrays are fixed size. You cannot change the capacity of the original array.
{Use std::vector}
You can have a pointer to an array. And use the same pointer. When the array is full, you can allocate another array, copy old array items to new array, delete the old array and assign your array pointer to the new array.
{Did I mention std::vector?}
By the way, there is a data structure that performs resizing as necessary. If I recall correctly, it is std::vector. Try it out. :-)
Assuming you are using std::array (which you should be), then copying the array is very easy.
std::array<myStruct, 1000> array1{};
std::array<myStruct, 2000> array2{};
// codes...
std::copy(array1.begin(), array1.end(), array2.begin())
However, this is a specific scenario in which you only use these two arrays. It will not dynamically double the size of the array as you simply cannot do this dynamically with stack-based arrays, just like c arrays[].
What you can, and should, be using is std::vector<myStruct>. This will dynamically grow as you need it. Until you provide us with code and a more specific issue, this is the best advice that I can offer with the information provided.
If you aren't allowed to use std::vector, as one of your comments stated, then you'll want to look at dynamic allocation.
size_t sz = [whatever];
// Dynamically allocate an array of size sz.
T* T_array = new T[sz];
// Do whatever...
delete[] T_array; // new[] needs to be paired with delete[].
T_array = nullptr; // Not strictly necessary, but a good idea if you have more code after.
As the size doesn't need to be constant for a dynamic array, this will allow you to allocate memory as necessary. You can then use std::copy() to copy data from one array to the other, as Goodies mentioned.
[For more information on dynamic allocation, see here.]
Why is this code wrong? Am I missing something regarding the behaviour of delete and delete[]?
void remove_stopwords(char** strings, int* length)
{
char** strings_new = new char*[*length];
int length_new = 0;
for(int i=0; i<*length; i++) {
if(is_common_keyword(strings[i]) == 0) {
strings_new[length_new] = strings[i];
length_new++;
}
else {
delete strings[i];
strings[i] = nullptr;
}
}
delete[] strings;
strings = new char*[length_new];
for(int i=0; i<length_new; i++) {
strings[i] = strings_new[i];
}
delete[] strings_new;
*length = length_new;
}
Explanations: this code should take an array of C-style strings and remove some particular strings of them; the array of C-style strings was created using new[] and every C-style string was created using new. The result of the code is that no word is canceled, but the array is only sliced.
I don't see any problem in the use of new[] or delete[] in the code shown.
No, wait.
I see a lot¹ of problems, but your intent is clear and the code seems doing what you want it to do.
The only logical problem I notice is that you're passing strings by value (it's a char** and reassigning it in the function will not affect the caller variable containing the pointer). Changing the signature to
void remove_stopwords(char**& strings, int* length)
so a reference is passed instead should fix it.
(1) Using std::vector<const char *> would seem more logical, even better an std::vector<std::string> if possible, that would take care of all allocations and deallocations.
every C-style string was created using new.
I suspect this is your problem -- C style strings are char arrays, so you can't readily create them with new, you need to use new[]. Which means you need to use delete[].
As #6502 pointed out, your basic problem is fairly simple: you're passing a char **, and attempting to modify it (not what it points at) in the function.
You're using that as a dynamically allocated array of strings, so what you're modifying is just the copy of the pointer that was passed into the function. Since you (apparently) want the function to modify what was passed into it, you need to either pass a char *** (ugh!) or char **& (still quite awful).
You really should use a vector<std::string> for the data. At least in my opinion, the code to remove the stop words should be written as a generic algorithm, something on this general order:
template <typename InIt, typename OutIt>
void remove_stop_words(InIt b, InIt e, OutIt d) {
std::remove_copy_if(b, e, d,
[](std:string const &s) { is_stop_word(s); });
}
With this, the calling code would look something like this:
// read input
std::vector<std::string> raw_input { std::istream_iterator<std::string>(infile),
std::istream_iterator<std::string>() };
// Filter out stop words:
std::vector<std::string> filtered_words;
remove_stop_words(raw_input.begin(), raw_input.end(),
std::back_inserter(filtered_words));
In a case like this, however, you don't really need to store the raw input words into a vector at all. You can pass an istream_iterator directly to remove_stop_words, and have it just produce the desired result:
std::ifstream in("raw_input.txt");
std::vector<std::string> filtered_words;
remove_stop_words(std::istream_iterator<std::string>(in),
std::istream_iterator<std::string>(),
std::back_inserter(filtered_words));
As an aside, you could also consider using a Boost filter_iterator instead. This would/will allow you to do the filtering in the iterator as you read the data rather than in an algorithm applied to the iterator.
I have an array of characters allocated with new and i want to modify the size of the array. Can i use realloc function for that? What is the best way to do so?
No, you can't... realloc() can only be used with malloc()/free()
Best call for a new[] allocated array is to create a new one and then memcpy() the data from one to another.
Better way - use an std::vector or std::string instead of array if you know you'll need resizing. Internally they're pretty much the same array.
In C++ it is best to use the STL std::vector class for this kind of thing. Either that, or a std::string.
I have an array of characters allocated with new and i want to modify the size of the array.
You can't resize an array, you can only allocate a new, larger one, move the contents to the new array, and delete the old one.
Can i use realloc function for that?
If you used malloc to allocate the original array, yes. But that's usually a bad idea in C++, where you usually want to deal with arrays of non-trivial objects not raw memory.
What is the best way to do so?
Use std::string (or perhaps std::vector<char>) to manage a dynamic array of characters automatically. These also have the advantage of using RAII to reduce the risk of memory leaks and other memory management errors.
You can use realloc(), if your array is allocated dynamically(via malloc/calloc/realloc). If you have static array, you can't resize it.If you have allocated with new:
int* Copy = new int[newSize];
std::copy(oldCopy,oldCopy+size,Copy);
But the best way would be to use std::vector<type> from c++ standard library
As was said by others, you cannot resize the array that was allocated per se, but you can create a larger one, copy the content of the first array to the second and delete the first one.
Here's an example, using std::copy().
int main(int argc, char** argv) {
int* arr = new int[5];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;
int* tmp = new int[10];
std::copy(arr, arr + 5, tmp);
std::copy(arr, arr, tmp + 5);
delete[] arr;
arr = tmp;
for(int i = 0; i < 10; i++) {
std::cout << arr[i] << "\n";
}
delete[] arr;
std::cin.get();
return 0;
}
This first creates an integer array and fills it. It then creates a larger array and fills it with the content of the first array twice and display that new larger array.
The principle is the same for an array of characters.
As the others have mentionned, your best bet in C++ is to use the standard library. For a resizable array of characters, you should probably use std::string or a vector of strings, but it's a bit overkill in some situations.
I'm trying to use an array in C++ that changes in size. For some reason the size does not change, it only ever holds 1 string. The difficult part is that the user cannot input the number of courses they are going to add, instead the addCourse function is called until the user stops. A vector cannot be used (this is for a school assignment, and a resizing array is required). I'm stuck as to why the array only seems to hold one string, I would think it to hold the equivalent of numCourses strings. How would I go about resizing to hold multiple strings after each call to the function?
void Student::addCourse(string* courseName)
{
int x;
numCourses += 1;//increments number of courses
string newCourse = *courseName;
string* newCourses = new string[numCourses];//temporary array
for(x=0; x<numCourses - 1; x++)//fills temp array with the values of the old
{
newCourses[x] = courses[x];
}
newCourses[numCourses - 1] = newCourse;//adds extra value
delete[] courses;//removes original array
courses = newCourses;//sets the new course list
}
Edit: For those asking why a vector cannot be used because the point of the assignment is to actively avoid memory leak using the heap. Using an array like this forces intentional delete of stored values.
The comment should have answered your question: there is no way for the debugger to know that a pointer to a string is pointed to an array, nor does it know its bounds, because no such information is kept at runtime (a std::vector will show its whole contents in the debugger, in contrast).
Your method prototype should read:
void Student::addCourse(const string& courseName);
If you don't want to have a memory leak, declare a pointer to courses in your class:
private:
string* courses;
Allocate space for an array of strings in your constructor:
Student::Student()
{
courses = new String[5];
}
Then deallocate in the destructor:
Student::~Student()
{
delete[] courses;
}
This gives you room for up to 5 courses. If you need more you need to adjust the size of the array of strings at run time:
void Student::ExtendArray()
{
delete[] courses;
courses = new String[10];
}
Note this code is not exception safe, but will give you the basic idea.
I'm truely baffled by this throwing an error....
char** results = new char*[numRes]; //this is where it breaks
for(int i = 0; i < numRes; i++)
{
results[i] = new char[64];
}
It's throwing a corruption of the heap error. but surely it should work? Im assigning 4 char* to a list of character pointers so I can pass them into functions etc.
I looked around everywhere but they all seem to be showing the malloc and free... Im using them in classes so I want to stick to c++ new and delete.
Could someone lend me a hand please?
What are you doing after you allocate? You only allocated an array of character pointers, you did not allocate space for each element (a pointer). If you try to store items in the elements, you'll run into problems.
For example, if you wanted to store anything in results[0] after your allocation, you would need to allocate to it as well. For example:
results[0] = new char[100]; // NEED TO ALLOCATE BEFORE WRITING TO results[0]!
strcpy(results[0], "Test");
You cannot just copy to results[0] without the allocation. The same holds for any element of results.
You are allocating memory for a pointers array. After that you have to allocate memory for every pointer in your array. I think your code should be like this:
int numRes = 4;
char** results = new char*[numRes];
for(int i=0; i<numRes; i++)
{
results[i] = new char;
}
If using c++ is it possible to use STL? specifically std::string, and std::list or std::vector classes in your code? Unlike traditional c-strings, which are mere sequences of characters in a memory array, C++ string objects belong to a class with many built-in features to operate with strings in a more intuitive way and with some additional useful features common to C++ containers.
#include <string>
#include <list>
std::list<std::string> results; // create a list of strings
// and populate it
results.push_back("blah");
results.push_back("blah1");
results.push_back("blah2");
...