Hello,
I have a problem. I need to make an array of pointers, without knowing beforehand the size of the array. Is this possible?
Code:const char* arrayOfPointers[];
Printable View
Hello,
I have a problem. I need to make an array of pointers, without knowing beforehand the size of the array. Is this possible?
Code:const char* arrayOfPointers[];
Sure:
Thing is, though, you have to remember to delete the memory. A much better way is to use an std::vector, or similar, eg:Code:int main( void )
{
const char** arrayOfPointers = 0;
// sometime later
unsigned size = 1024;
arrayOfPointers = new const char*[ size ];
// do something usefull
delete [ ] arrayOfPointers;
}
Code:#include <vector>
int main( void )
{
std::vector< const char* > arrayOfPointers;
// sometime later
unsigned size = 1024;
arrayOfPointers.resize( size );
// do something useful...memory will be deleted by vector object...
}
Thank you.
Quick question:
Can an element of an array of pointers be assigned a string, or just a single character?
if it is an array of char * ptrs sure...
How about this?
Would the output be:Code:string* str = NULL;
str = new string;
*str = "Yes, a string...\n";
*str += "Yes, another string...\n";
*str += "Yes, even another string...\n";
int sizeOfStr = str->size();
char* arrayOfPointers[] = NULL;
//several code lines later...
arrayOfPointers = new char*[sizeOfStr];
string* anotherStr = NULL;
anotherStr = new string;
void doStuff();
void doStuff() {
for (int = 0; i < sizeOfStr; i++) {
while (str->at[i] != '\n') {
*anotherStr += str->at[i];
}
*arrayOfPointers[i] = *anotherStr->data();
anotherStr = NULL; //reset this pointer
}
}
int main() {
doStuff();
int sizeOfArray = sizeof(arrayOfPointers);
for (int = 0; i < sizeOfArray; i++) {
cout<< *arrayOfPointers[i] <<endl;
}
delete str;
delete [] arrayOfPointers;
delete anotherStr;
return 0;
}
?Quote:
Yes, a string...
Yes, another string...
Yes, even another string...
Bump.
For others' benefit.
For the record, I tried it (though obviously including <string> and <iostream>), but it didn't compile. It said it expected constructor, destructor, or type conversion before the '=' token for the "string* str = NULL" line, as well as several lines after that. After changing the string pointer to a string object, it now says 'string' does not name a type, so obviously its not seeing the string class's definition for some reason, and I don't know why, seeing as I included it....
Also note that the code was only an example anyway. In the real code that I was really asking about, all variables (including the ones that are created in dynamic memory) are created inside functions.
Dude! The clumsiest "programming ninja" ever!
Yep, I guessed that as soon as I realised that I did that.
Anyway, the code was only an example, designed to sort of mirror what I did in my real code, only in the real code I initialized all variables or pointers to variables inside functions.
Ok, here is what finally compiled:
However, it doesn't output the strings.Code:#include <iostream>
#include <string>
using namespace std;
struct stringStruct {
stringStruct();
void initialize();
string* str;
string* anotherStr;
const char** arrayOfPointers;
};
stringStruct::stringStruct() {
initialize();
}
void stringStruct::initialize() {
str = NULL;
anotherStr = NULL;
}
void doStuff();
int main() {
doStuff();
cin.get();
return 0;
}
void doStuff() {
stringStruct object;
object.str = new string;
*object.str = "Yes, a string...\n";
*object.str += "Yes, another string...\n";
*object.str += "Yes, even another string...\n";
int sizeOfStr = object.str->size();
object.anotherStr = new string;
int sizeOfArrayOfPointers = 0;
for (int i = 0; i < sizeOfStr; i++) {
char currentChar = object.str->at(i);
if (currentChar != '\n') {
sizeOfArrayOfPointers++; //increment this
}
}
object.arrayOfPointers = new const char*[sizeOfArrayOfPointers];
for (int i = 0; i < sizeOfStr; i++) {
while (object.str->at(i) != '\n') {
object.anotherStr += object.str->at(i);
}
object.arrayOfPointers[i] = object.str->data();
object.anotherStr = NULL; //reset this pointer
}
int sizeOfArray = sizeof(object.arrayOfPointers);
for (int i = 0; i < sizeOfArray; i++) {
cout<< *object.arrayOfPointers[i] <<endl;
}
delete object.str;
delete [] object.arrayOfPointers;
delete object.anotherStr;
}
*object.arrayofPointers[i] is just a single character. If you want the C-string, ditch the star.
Well, I already tried that, but still nothing is outputted. It just gets stuck at a blank cursor in the terminal window.
I wish you the best of luck at getting out of this loop:
Code:while (object.str->at(i) != '\n') {
object.anotherStr += object.str->at(i);
}
Having a member that is a pointer to a string is virtually always wrong.FTFY.Code:struct stringStruct {
stringStruct() : arrayOfPointers(NULL) {}
string str;
string anotherStr;
const char** arrayOfPointers;
};
Hmm...fixed that error, but now its printing out memory addresses.
Code:#include <iostream>
#include <string>
using namespace std;
struct stringStruct {
stringStruct() : arrayOfPointers(NULL) { initialize(); }
void initialize();
string* str;
string* anotherStr;
const char** arrayOfPointers;
};
void stringStruct::initialize() {
str = NULL;
anotherStr = NULL;
}
void doStuff();
int main() {
doStuff();
cin.get();
return 0;
}
void doStuff() {
stringStruct object;
object.str = new string;
*object.str = "Yes, a string...\n";
*object.str += "Yes, another string...\n";
*object.str += "Yes, even another string...\n";
int sizeOfStr = object.str->size();
object.anotherStr = new string;
int sizeOfArrayOfPointers = 0;
for (int i = 0; i < sizeOfStr; i++) {
char currentChar = object.str->at(i);
if (currentChar != '\n') {
sizeOfArrayOfPointers++; //increment this
}
}
object.arrayOfPointers = new const char*[sizeOfArrayOfPointers];
for (int i = 0; i < sizeOfStr; i++) {
int i2 = 0;
while (object.str->at(i2) != '\n') {
object.anotherStr += object.str->at(i2);
i2++;
}
object.arrayOfPointers[i] = object.anotherStr->data();
object.anotherStr = NULL; //reset this pointer
}
int sizeOfArray = sizeof(object.arrayOfPointers);
for (int i = 0; i < sizeOfArray; i++) {
cout<< object.arrayOfPointers[i] <<endl;
}
delete object.str;
delete [] object.arrayOfPointers;
delete object.anotherStr;
}
Hmm you didn't listen. Let me restate it then:
Having a member that is a pointer here is actually the wrong thing to do.
The only thing it achieves is making this harder for yourself, and making the program longer.
No, actually I just ignored that part, and added an initializer list to initialize the array of pointers to NULL like you have in your posted code a few posts back... ;) So I still followed your advice, just not the statement about no member pointers. :)
Do you think that it would output the strings, and not the memory addresses, if I did that though? If not, I wont bother changing it since its just a test program anyway.
And, btw, the reason I did that was because that's how I did it in my real program (only I have the array of pointers as a member of a class, instead of a struct). And the reason I did it that way is because its an array of pointers that I'm using across multiple member functions of the class. I prefer it that way, precisely because it makes it easier (NOT harder)...
And so I create the array of pointers on the heap in one member function of the class, assign all the elements addresses of strings, then return a pointer to that array in another function. It makes the most sense, in my opinion.
The problem is that even though I followed all advice given in this thread with the exception of the "no member pointers to strings" rule, it still outputs the addresses contained in the elements of the pointer array, instead of the strings contained at those addresses.
does not about output the strings being pointed at by those pointer array elements like tabstop suggested it would, but instead outputs the memory addresses instead. I'm thinking that maybe if I try outputting the whole array, instead of individual elements, it'll output the whole strings. Only problem with that is, all strings will be on the same line...Code:cout<< object.arrayOfPointers.[i] <<endl;
arrayofPointers was char**, n'est ce pas? I don't feel like going back up to find your code.
The following does what I expect:
I'll leave it to you to compare and contrast. (We'll ignore the lack of delete[]s for the moment.)Code:#include <iostream>
#include <cstring>
int main() {
char **arrayofPointers;
arrayofPointers = new char*[10];
for (int i = 0; i < 10; ++i) {
arrayofPointers[i] = new char[15];
strcpy(arrayofPointers[i], "Hello world!");
}
for (int i = 0; i < 10; ++i) {
std::cout << arrayofPointers[i] << std::endl;
}
return 0;
}
Yes, it is.
Do I really have to point every individual element of the array of pointers to a "new" allocation in dynamic memory? I thought the "arrayOfPointers = new char*[10]" part takes care of all elements. And anyway, seeing as I'm assigning (i.e. pointing) each element of the pointer of arrays to an already existing string, I would think I wouldn't have to do that.Quote:
The following does what I expect:
I'll leave it to you to compare and contrast. (We'll ignore the lack of delete[]s for the moment.)Code:#include <iostream>
#include <cstring>
int main() {
char **arrayofPointers;
arrayofPointers = new char*[10];
for (int i = 0; i < 10; ++i) {
arrayofPointers[i] = new char[15];
strcpy(arrayofPointers[i], "Hello world!");
}
for (int i = 0; i < 10; ++i) {
std::cout << arrayofPointers[i] << std::endl;
}
return 0;
}
Code:#include <iostream>
#include <string>
using namespace std;
struct stringStruct {
stringStruct() : arrayOfPointers(NULL) { initialize(); }
void initialize();
string* str;
string* anotherStr;
const char** arrayOfPointers;
};
void stringStruct::initialize() {
str = NULL;
anotherStr = NULL;
}
void doStuff();
int main() {
doStuff();
cin.get();
return 0;
}
void doStuff() {
stringStruct object;
object.str = new string;
*object.str = "Yes, a string...\n";
*object.str += "Yes, another string...\n";
*object.str += "Yes, even another string...\n";
int sizeOfStr = object.str->size();
object.anotherStr = new string;
int sizeOfArrayOfPointers = 0;
for (int i = 0; i < sizeOfStr; i++) {
char currentChar = object.str->at(i);
if (currentChar != '\n') {
sizeOfArrayOfPointers++; //increment this
}
}
object.arrayOfPointers = new const char*[sizeOfArrayOfPointers];
int i2 = 0;
for (int i = 0; i < sizeOfStr; i++) {
while (object.str->at(i2) != '\n') {
object.anotherStr += object.str->at(i2);
i2++;
}
object.arrayOfPointers[i] = object.anotherStr->data();
object.anotherStr = NULL; //reset this pointer
}
int sizeOfArray = sizeof(object.arrayOfPointers);
for (int i = 0; i < sizeOfArray; i++) {
cout<< object.arrayOfPointers[i] <<endl;
}
delete object.str;
delete [] object.arrayOfPointers;
delete object.anotherStr;
}
Currently, when I run the code I just posted, I receive the following message:
I press Enter, and the program terminates. What its SUPPOSED to do is output the following:Quote:
Segmentation fault
Press ENTER to continue.
Quote:
Yes, a string...
Yes, another string...
Yes, even another string...
Press ENTER to continue.
That code doesn't print any memory addresses. It just segfaults, when you try to access NULL the second time through that for loop around line 65.
(EDIT: You're not actually accessing NULL -- you started the pointer out at NULL and you've been adding characters to the value of the pointer, so by the time you're done you're at some number in thousands, but that isn't actually a pointer to a string now is it.)
Well, it was until I realized that I had accidentally assigned the data of the wrong string to the elements of the array (i.e. object.str->data() instead of object.anotherStr->data after the while loop). I fixed that problem, so now it just seg-faults.
In mine, there is no line 65. Line 53 is the last for loop of the "doStuff" function, and is the loop which handles outputting object.arraysOfPointers[i]. The for loop before that is at line 43. That's the one which loops while 'i' is less than sizeOfStr (a variable which is assigned the value of object.str->size()). There is no reason why it should be accessing a NULL value.
Not sure if you're right...
Are you referring to object.str?
If so, yes, that pointer is initialized to NULL, however before I start adding characters, I first allocate it a "new" spot in memory. Then I de-reference (i.e. access the memory location pointed at by that pointer) and start adding characters to it.
I next get the size of that string, then loop through it with the first for loop, incrementing the "sizeOfArrayOfPointers" variable value by 1 each loop, while the current character of the string does not equal a '\n' character so as to get the size of the string, minus the new-line characters. I next use that size to create the array of pointers. The second for loop next moves through the str, adding each character to another string (called "anotherStr" for convenience) as long as the current character is not a new-line character. The resulting string pointed at by "object.anotherStr" is next assigned to the current element of the array of pointers (or to put it another way, the current pointer element of the pointer array is assigned the memory address of the anotherStr). This does this as long as i < sizeOfStr. And there we have the problem...Code:stringStruct object;
object.str = new string;
*object.str = "Yes, a string...\n";
*object.str += "Yes, another string...\n";
*object.str += "Yes, even another string...\n";
I should be doing it as long as i < sizeOfArrayOfPointers instead.
EDIT: Hmm...that made no difference. Still, a seg-fault.
Ok, I'm done with this for the night. Ridiculous that this took me all day...
I should have got it working hours ago.
Oh well, I'll have another go at it tomorrow.
If you're hell bent on using pointers when there is no need, then why is 'object' not declared as a pointer to stringStruct?Oh and how about sizeOfArrayOfPointer:Code:stringStruct *object;
object->str = new string;
*object->str = "Yes, a string...\n";
// etc
delete object;
I mean, you can see why this is redundant and silly right? So how is it that you think declaring pointers to those strings isn't just as redundant?Code:int *sizeOfArrayOfPointer;
sizeOfArrayOfPointer = new int;
*sizeOfArrayOfPointers = 0;
// etc
delete sizeOfArrayOfPointers;
I'm going to make assumptions about what your code is meant to be doing and just show you an improvement. I assume you are trying to split the string at the newlines and print out each string from the array of pointers. The following is better, and should achieve your goal. This code is still not ideal, but this is probably not a sensible thing to be doing in the first place.
Your code had several major flaws that meant it could never work as it was, hence the significant changes. E.g. you couldn't cout the pointers obtained from the string's data() because they weren't guaranteed to be null-terminated. There is no place for 'sizeof' in that program.Code:#include <iostream>
#include <string>
using namespace std;
struct stringStruct {
stringStruct() : arrayOfPointers(NULL) {}
string str;
const char** arrayOfPointers; // Should really be a vector
~stringStruct() { delete [] object.arrayOfPointers; } // Safer to do this here
private: // Standard code for preventing an object from being copied, follows
stringStruct(const stringStruct&);
stringStruct& operator=(const stringStruct&);
};
void doStuff();
int main(void) {
doStuff();
cin.get();
return 0;
}
void doStuff() {
stringStruct object;
object.str = "Yes, a string...\n"
"Yes, another string...\n"
"Yes, even another string...\n"; // You can concatenate string literals more efficiently like this
int sizeOfStr = object.str.size();
int numberOfPointers = 0;
for (int i = 0; i < sizeOfStr; i++) {
if (object.str.at(i) != '\n') {
numberOfPointers++;
} else {
object.str[i] = '\0'; // We cant output the string later if it isn't null-terminated
// Also we have to make all modifications prior to calling data(), so it's done here
}
}
object.arrayOfPointers = new const char*[numberOfPointers];
int i2 = 0;
for (int i = 0; i < numberOfPointers; i++) {
object.arrayOfPointers[i] = &object.str.data()[i2];
while (object.str.at(i2) != '\0)
i2++;
i2++;
}
for (int i = 0; i < numberOfPointers; i++) {
cout<< object.arrayOfPointers[i] << endl;
}
}
You're right, however, even after changing that line to:
*object.anotherStr += object.str->at(i2);
instead, it still seg-faults. There shouldn't be a problem, there, even though object.str->at() returns a char reference, because the '+=' operator of the string class can handle a char.
What is your current code? Why are you not using a std::vector or other appropriate container?
Because the point of using an array of pointers to those strings is so I can later return a pointer to the whole array, and be able to easily access each string I want to access (separately), in my real program. If I try using just a normal char array[], for example, then I can only store one continuous string. I could of course separate individual strings inside the normal char array with a character like '\n', but I don't want to do that. I want to have the ability to access each individual string of the array, by simply accessing a specific array index. See, what this real program does is it reads from a file line by line, storing the contents of the line in a char array called "filestreamInBuffer". I then check the contents of that array to see if it contains the string which I'm searching for (i.e. "enum"). If it does, I continue reading lines from that point until the terminating semicolon has been found (or if, for some bizarre reason, it doesn't, and it reaches the end of the file without finding it, I set a bool variable called "terminatingSemicolonDoesNotExist" as true, then break out of the loop). :) Now, here is where the fun begins...
Before I find the terminating semicolon, I add each line of the file after the line of which "enum" was found to a string (after first stripping it to contain only the string name of the enum value). I also count how many enum values have been found, so that once all that is done, I can create a new char* array with the size of the number of enum values. I then do something similiar to what I did in the test program, i.e. loop through that string which contains all the enum values, and adding characters to another string so long as the current character is not a new-line character, and then assigning the resulting string to the current index element of the char* array. Once that function has done all the stuff I wrote it to do, I can then retrieve all the bits and pieces of the enum separately (i.e. like the enum name, the names of the enum values as a char* array), and everything's just dandy. The whole point of the real program is so I can easily retrieve the string names of the enum values of any enum.
Of course, though, it would be nice to get the whole logic of the array of pointers working first...
You are correct with that assumption. Basically, I want to add individual strings to a string type, separating them with a '\n' character. I then want to loop through that string, and assign each string to the current element of the char* array up until the '\n' character is detected.Quote:
I'm going to make assumptions about what your code is meant to be doing and just show you an improvement. I assume you are trying to split the string at the newlines and print out each string from the array of pointers. The following is better, and should achieve your goal. This code is still not ideal, but this is probably not a sensible thing to be doing in the first place.
Well, yes, obviously its best to delete a member variable from inside the class or struct destructor. I know this, but I didn't do it in this test program, because its so small, and I made sure to delete all "new" allocatations inside the "doStuff" function. And of course, I think you mean "delete [] arrayOfPointers;" not "delete [] object.arrayOfPointers" since we're inside the member function destructor, and "object" is a "stringStruct" object created inside the "doStuff" function.Quote:
Code:#include <iostream>
#include <string>
using namespace std;
struct stringStruct {
stringStruct() : arrayOfPointers(NULL) {}
string str;
const char** arrayOfPointers; // Should really be a vector
~stringStruct() { delete [] object.arrayOfPointers; } // Safer to do this here
private: // Standard code for preventing an object from being copied, follows
stringStruct(const stringStruct&);
stringStruct& operator=(const stringStruct&);
};
Well, I tried to fix that problem in my new version, however when I do this:Quote:
Your code had several major flaws that meant it could never work as it was, hence the significant changes. E.g. you couldn't cout the pointers obtained from the string's data() because they weren't guaranteed to be null-terminated. There is no place for 'sizeof' in that program.Code:void doStuff();
int main(void) {
doStuff();
cin.get();
return 0;
}
void doStuff() {
stringStruct object;
object.str = "Yes, a string...\n"
"Yes, another string...\n"
"Yes, even another string...\n"; // You can concatenate string literals more efficiently like this
int sizeOfStr = object.str.size();
int numberOfPointers = 0;
for (int i = 0; i < sizeOfStr; i++) {
if (object.str.at(i) != '\n') {
numberOfPointers++;
} else {
object.str[i] = '\0'; // We cant output the string later if it isn't null-terminated
// Also we have to make all modifications prior to calling data(), so it's done here
}
}
object.arrayOfPointers = new const char*[numberOfPointers];
int i2 = 0;
for (int i = 0; i < numberOfPointers; i++) {
object.arrayOfPointers[i] = &object.str.data()[i2];
while (object.str.at(i2) != '\0)
i2++;
i2++;
}
for (int i = 0; i < numberOfPointers; i++) {
cout<< object.arrayOfPointers[i] << endl;
}
}
It refuses to compile, because the bolded line is said to be trying to access a "read-only" location, which I guess means the array IS terminated with that character already after all...Code:for (int i = 0; i < sizeOfStr; i++) {
while (object.str->at(i2) != '\n') {
*object.anotherStr += object.str->at(i2);
i2++;
}
if (i == sizeOfArrayOfPointers - 2) {
object.arrayOfPointers[i] = object.anotherStr->data();
*object.arrayOfPointers[i+1] = '\0';
break; //out of the loop since we're at the last element in the array of pointers
}
object.arrayOfPointers[i] = object.anotherStr->data();
object.anotherStr = NULL; //reset this pointer
}
EDIT: Nevermind...I see what you mean. Each character string (which means EVERY string pointed at by each element of the char* array) should be NULL-terminated. I'll get right on that right away..
I would reach for a std::vector<std::string>.Quote:
Originally Posted by Programmer_P
As to why I'm not using a vector, its because I don't need a resizable array. I only need a fixed-size array which is given a size based on how many individual strings are contained within one string (separated by a '\n' character)...Code:#include <iostream>
#include <string>
using namespace std;
struct stringStruct {
stringStruct() : arrayOfPointers(NULL) { initialize(); }
void initialize();
string* str;
string* anotherStr;
const char** arrayOfPointers;
};
void stringStruct::initialize() {
str = NULL;
anotherStr = NULL;
}
void doStuff();
int main() {
doStuff();
cin.get();
return 0;
}
void doStuff() {
stringStruct object;
object.str = new string;
*object.str = "Yes, a string...\n" + '\0';
*object.str += "Yes, another string...\n" + '\0';
*object.str += "Yes, even another string...\n" + '\0';
int sizeOfStr = object.str->size();
object.anotherStr = new string;
int sizeOfArrayOfPointers = 3;
object.arrayOfPointers = new const char*[sizeOfArrayOfPointers];
int i2 = 0;
for (int i = 0; i < sizeOfStr; i++) {
while (object.str->at(i2) != '\n') {
*object.anotherStr += object.str->at(i2);
i2++;
}
if (i == sizeOfArrayOfPointers - 1) {
object.arrayOfPointers[i] = object.anotherStr->data();
break; //out of the loop since we're at the last element in the array of pointers
}
object.arrayOfPointers[i] = object.anotherStr->data();
object.anotherStr = NULL; //reset this pointer
}
for (int i = 0; i < sizeOfArrayOfPointers; i++) {
cout<< object.arrayOfPointers[i] <<endl;
}
delete object.str;
delete [] object.arrayOfPointers;
delete object.anotherStr;
}
It still makes sense to use a vector since the size is not fixed at compile time. If you insist that it does not make sense, then rewrite your code to remove this:Quote:
Originally Posted by Programmer_P
because you don't need it. If you do need it, then you should use a std::vector or other appropriate container.Code:new const char*[sizeOfArrayOfPointers]
You need to start heeding the advice of people on here who know how to do what you want instead of insisting on doing it your way. If you need this for a collection of strings I would recommend, as others here have, a vector of strings.
A vector of pointers is so much simpler than an array of pointers. Neither one is all that difficult but the latter has some nasty syntax associated with it.
Quite simply:
Is about the simplest way to maintain pointers to several heterogenous objects.Quote:
std::vector<Object *> objects;
Ah yes, a copy and paste error on my part. Forgot to give the "untested" disclaimer on that code.
One more thing: Although you're noted that you don't need a resizeable array. You can see that you are having to perform two passes over the data instead. Once to count the newlines, and then once to get the pointer to the strings. If you use a vector, then you can do it all in one pass. For each newline you find, you can just push_back the string immediately.
Efficiency wise there's probably not much difference, but if you can do it in less code then there's less room for bugs.
Are you writing an interpreter or someting like that?
Not sure if that's what you really meant, but I wasn't *counting* new-line characters. I was only using them as a separation character between individual strings contained in a single string. So I simply was looping through the string, and adding characters to anotherStr as long as the current character was not a newline character. This is done in a while loop inside a for loop. Once the while loop completes each iteration of the for loop, I was simply pointing at the string being pointed at by anotherStr with the current element pointer of the array of pointers. As for vector::push_back(), I'm not sure exactly what you mean by that either. What that function does is appends to the end of the string, and performs the same function as the += operator of the string class, and I wasn't wanting to add the new-line characters to the anotherStr, before pointing at the resulting anotherStr with the current element of the array of pointers, because the result strings weren't supposed to contain any new-line characters.
Not exactly. I was originally writing a program for web coding in various languages, but then I had need of a function which could get the string names of an enum member, and I went looking for that, without finding anything for that. So I then decided to write my own program which could take a file containing an enum (or multiple enums) and output some kind of interface to file which would make retrieving enum value string names a piece of cake, regardless of what the enum is. That is when I decided I wanted a function which would return a const char* array of which each element is pointed at the relevant string of the enum value name, which resulted in this current thread.Quote:
Efficiency wise there's probably not much difference, but if you can do it in less code then there's less room for bugs.
Are you writing an interpreter or someting like that?
Anyway, I got the code to work using an array of pointers finally (even though I decided to go with vectors instead):
This works as expected, and outputs:Code:#include <iostream>
#include <string>
using namespace std;
struct stringStruct {
stringStruct() : arrayOfPointers(NULL) { initialize(); }
void initialize();
string* str;
string* anotherStr;
const char** arrayOfPointers;
};
void stringStruct::initialize() {
str = NULL;
anotherStr = NULL;
}
void doStuff();
int main() {
doStuff();
cout<< "\nHaha, you got to press Enter."<<endl;
cin.get();
return 0;
}
void doStuff() {
stringStruct object;
object.str = new string;
*object.str = "Yes, a string...\n";
*object.str += "Yes, another string...\n";
*object.str += "Yes, even another string...\n";
int sizeOfStr = object.str->size();
object.anotherStr = new string;
int sizeOfArrayOfPointers = 3;
object.arrayOfPointers = new const char*[sizeOfArrayOfPointers];
int i2 = 0;
for (int i = 0; i < sizeOfStr; i++) {
if (object.anotherStr == NULL) {
object.anotherStr = new string;
}
if (object.str->at(i2) == '\n') { //we're at a new-line character
i2++; //increment so as to skip the new-line character to the next character, which wont be a new-line character
}
if (i == sizeOfArrayOfPointers - 1) { //we're at the last element of the array of pointers
for (int num = i; num < sizeOfStr; num++) { //continue looping through object.str...
if (object.str->at(i2) == '\n') { //we're at a new-line character
i2++; //increment so as to skip the new-line character to the next character, which wont be a new-line character
}
while (object.str->at(i2) != '\n') {
*object.anotherStr += object.str->at(i2);
i2++;
}
object.arrayOfPointers[num] = object.anotherStr->data();
object.anotherStr = NULL;
for (int i = 0; i < sizeOfArrayOfPointers; i++) {
cout<< object.arrayOfPointers[i] <<endl;
}
delete object.str;
delete object.anotherStr;
delete [] object.arrayOfPointers;
return;
}
}
while (object.str->at(i2) != '\n') { //iterate till the next new-line character
*object.anotherStr += object.str->at(i2); //add characters to the "anotherStr"
i2++;
}
object.arrayOfPointers[i] = object.anotherStr->data();
object.anotherStr = NULL; //reset this pointer
}
}
Quote:
Yes, a string...
Yes, another string...
Yes, even another string...
Yes you were. How else do you know how large to allocate arrayOfPointers?!Quote:
Not sure if that's what you really meant, but I wasn't *counting* new-line characters.
I love how you are able to use the word "simply" in there. Using that word does not make it true.Quote:
I was only using them as a separation character between individual strings contained in a single string. So I simply was looping through the string, and adding characters to anotherStr as long as the current character was not a newline character. This is done in a while loop inside a for loop. Once the while loop completes each iteration of the for loop, I was simply pointing at the string being pointed at by anotherStr with the current element pointer of the array of pointers.
What's more, it probably isn't actually doing what you think it's doing. I know this both because your descriptions don't quite make sense and because you've still failed to realise why allocating those strings dynamically does not do anything good for that piece of code. If you understood how it really worked you'd realise this. I've already shown how that unnecessary complication can be avoided in a code example.I mean exactly what anyone else who mentions it means. The mere mention of it to most C++ programmers should immediately put you both into a state of mutual understanding. You don't know about much of the most commonly used stuff in the standard C++ Library and that is most unfortunate for you as your job will be that much harder without it.Quote:
As for vector::push_back(), I'm not sure exactly what you mean by that either.
It makes sense that your description would be as convoluted as your code is. They both come from the same thought process. You need to learn to think about the effect you are trying to achieve at a higher level.Quote:
What that function does is appends to the end of the string, and performs the same function as the += operator of the string class, and I wasn't wanting to add the new-line characters to the anotherStr, before pointing at the resulting anotherStr with the current element of the array of pointers, because the result strings weren't supposed to contain any new-line characters.
Well, what you've ended up with is far from pretty. It has memory leaks and usage of string::data() that is almost certainly going to mean trouble in your real code. Any attempt to return an array of const char *'s along the lines of what you've been doing is pretty much going to guarantee a memory leak or accessing deleted memory.
Do you realise that you have a for-loop there that will never loop? In other words, it's equivalent to just an if-statement.
Well it's all for your own benefit. Perhaps if you get stuck in and learn more about the language you'll come to look back on this code one day and realise how far you've come. If that happens - mission accomplished.
Only because you got lucky. Consider:
Can you see a problem with this code? If not, I would highly recommend that you stick with the pre-made stl objects (std::string, std::vector, etc) until you've got a really firm grasp on how pointers work, their implications, etc.Code:object.arrayOfPointers[i] = object.anotherStr->data();
object.anotherStr = NULL; //reset this pointer
It seems to me that this would be a good time for you to learn about the STL and the power, and responsibility, it brings. The STL is a truly a life saver and has so many benefits that it is almost criminal not to leverage it at some point.
I highly recommend learning about the STL. The only book I know for the STL is Effective STL but I cannot really recommend it to you since you are quite new to the STL. That book is really geared towards those that have been using it but want ways to use it more effectively. You might be able to learn how to use the STL from it but that is not the goal of the book.
Perhaps others here could recommend a beginner book for learning the STL.
I was originally iterating through object.str, and incrementing the sizeOfArrayOfPointers only if the current character of the string was not a new-line character. That's not counting new-lines...
Then I realized that wasn't the size that I wanted for the array of pointers, because each pointer is supposed to point at an individual string. And there are three of those...
So I rewrote the code to create the array of pointers with a size of 3, and that's how I have it now.Quote:
Yes, a string...
Yes, another string...
Yes, even another string...
I do understand how it works, and I also understand that I don't have to point object.anotherStr at a "new" allocation in memory each loop. The reason I did it that way is because my code in my real program also does it that way, and the reason for that is the anotherStr (named differently in my real program) is a member of a class, and I just prefer to do it that way. And, my class destructor takes care of deleting every "new" thing, so no memory leaks there.Quote:
I love how you are able to use the word "simply" in there. Using that word does not make it true.
What's more, it probably isn't actually doing what you think it's doing. I know this both because your descriptions don't quite make sense and because you've still failed to realise why allocating those strings dynamically does not do anything good for that piece of code. If you understood how it really worked you'd realise this. I've already shown how that unnecessary complication can be avoided in a code example.I mean exactly what anyone else who mentions it means. The mere mention of it to most C++ programmers should immediately put you both into a state of mutual understanding. You don't know about much of the most commonly used stuff in the standard C++ Library and that is most unfortunate for you as your job will be that much harder without it.It makes sense that your description would be as convoluted as your code is. They both come from the same thought process. You need to learn to think about the effect you are trying to achieve at a higher level.
No, it doesn't. I delete all the "new"s at the end of the doStuff() function. And what is wrong with my usage of string::data()? Each element of my array of pointers is being assigned the memory location of each anotherStr->data(), and I don't delete anything until I've outputted the strings contained at those memory locations.Quote:
Well, what you've ended up with is far from pretty. It has memory leaks and usage of string::data() that is almost certainly going to mean trouble in your real code. Any attempt to return an array of const char *'s along the lines of what you've been doing is pretty much going to guarantee a memory leak or accessing deleted memory.
Do you realise that you have a for-loop there that will never loop? In other words, it's equivalent to just an if-statement.
As for returning a const char* array in my real program, that also is not a problem, because I make sure to only delete with my class destructor.
Yes, and I do appreciate that. And I do plan on learning more of the language. I have a few programming books that I've been planning to read. I am far from being an expert at programming, I know.Quote:
Well it's all for your own benefit. Perhaps if you get stuck in and learn more about the language you'll come to look back on this code one day and realise how far you've come. If that happens - mission accomplished.
You forgot to use a try/catch block to handle cases where an exception (e.g., std::bad_alloc) is thrown before those delete statements can be reached.Quote:
Originally Posted by Programmer_P
Ya know in the time it has taken to build this code and the time that would be wasted for other developers to figure out what in the world you were trying to do you could have used the STL in several places and been done. As well your code would be much clearer to anyone later who was tasked with maintaining and/or integrating your code into a code line.
Programming isn't just about what works.
Not only does your inner for-loop never loop, but your outer one is looping up to the wrong constant.
Here's another leak:You've just thrown away your only pointer to anotherStr. No way to free it now.Code:object.anotherStr = NULL; //reset this pointer
I certainly see why you want to use pointers to strings. It's because you don't want their actual contents to be freed at all, since the app is broken and would cause accessing data after it was freed. Using data() at all is really pushing you into doing bad things. You should really be using .c_str() if you must avoid directly having an array of strings.
There are two ways to write code. Such that:
- there are no obvious bugs, or
- there are obviously no bugs
Many people mistakenly aim for the first one
I must confess I am baffled by all this convoluted code to do such a simple task.
You're right. I understand this...
More coding, less posting is what I'll do in the future. Perfect practice of programming will make an almost-perfect programmer. I got it. :)
Btw, thanks everyone for the support. Hopefully soon once I get really good at programming, it'll be me who's the one providing support for other programmers, not the other way around, though I suppose I'll never know everything there is to know about programming, and will still occasionally need assistance and advice. That's what makes us human.
Well, yes, I obviously noticed that when I first wrote the code (i.e. that the loop would only be entered once), just wasn't thinking as straight as I should have. Guess I should have used an if statement instead. As for the outer loop, I don't really see a problem with that, because once the if statement, and then the inner for loop, is entered, we then just move on from the current index of 'i', wherever 'i' is, and continue iterating through the rest of the string. And then inside the if statement, I delete the stuff I created on the heap, then return out of the function. What would your suggestion be in regards to this? Please post some example code.
You're right. I was thinking that by keeping a pointer to each string with an element of the array of pointers, I could then delete those new objects in memory by doing:Quote:
Here's another leak:You've just thrown away your only pointer to anotherStr. No way to free it now.Code:object.anotherStr = NULL; //reset this pointer
But I see now where I made my error.Code:delete [] object.arrayOfPointers;
Yep, and I'll shoot for Way #2 next time around. :DQuote:
I certainly see why you want to use pointers to strings. It's because you don't want their actual contents to be freed at all, since the app is broken and would cause accessing data after it was freed. Using data() at all is really pushing you into doing bad things. You should really be using .c_str() if you must avoid directly having an array of strings.
There are two ways to write code. Such that:
- there are no obvious bugs, or
- there are obviously no bugs
Many people mistakenly aim for the first one
The task I was trying to achieve was not just to output a few strings. The task was to test working with arrays of pointers, and to see if I could achieve the task of outputting the strings being pointed at by the elements of the array of pointers.
Obviously, I understand now a lot more about arrays of pointers, and also about vectors, and about coding in generally. But fortunately, I managed to sort all of the bugs out eventually, thanks to all y'all's help.
Actually more listening is what I was after. The reason we suggest to you option A or B is b/c we are trying to save you the pain that we have gone through once before trying to do a similar task.Quote:
More coding, less posting is what I'll do in the future.
For the record, here is the code that works with an array of char pointers (now free of all bugs -- I hope):
And here is the code with the vector that I ended up using in place of the array of pointers:Code:#include <iostream>
#include <string>
using namespace std;
struct stringStruct {
stringStruct() : arrayOfPointers(NULL) { initialize(); }
void initialize();
string* str;
string* anotherStr;
const char** arrayOfPointers;
};
void stringStruct::initialize() {
str = NULL;
anotherStr = NULL;
}
void doStuff();
int main() {
doStuff();
cout<< "\nHaha, you got to press Enter."<<endl;
cin.get();
return 0;
}
void doStuff() {
stringStruct object;
object.str = new string;
*object.str = "Yes, a string...\n";
*object.str += "Yes, another string...\n";
*object.str += "Yes, even another string...\n";
int sizeOfStr = object.str->size();
object.anotherStr = new string;
int sizeOfArrayOfPointers = 3;
object.arrayOfPointers = new const char*[sizeOfArrayOfPointers];
int i2 = 0;
for (int i = 0; i < sizeOfStr; i++) {
if (object.anotherStr == NULL) {
object.anotherStr = new string;
}
if (object.str->at(i2) == '\n') { //we're at a new-line character
i2++; //increment so as to skip the new-line character to the next character, which wont be a new-line character
}
if (i == sizeOfArrayOfPointers - 1) { //we're at the last element of the array of pointers
//continue looping through object.str...
while (object.str->at(i2) != '\n') {
*object.anotherStr += object.str->at(i2);
i2++;
}
object.arrayOfPointers[i] = object.anotherStr->data();
for (int i = 0; i < sizeOfArrayOfPointers; i++) {
cout<< object.arrayOfPointers[i] <<endl;
}
if (object.arrayOfPointers) {
delete [] object.arrayOfPointers;
}
if (object.anotherStr) {
delete object.anotherStr;
object.anotherStr = NULL;
}
return; //out of the function
}
while (object.str->at(i2) != '\n') { //iterate till the next new-line character
*object.anotherStr += object.str->at(i2); //add characters to the "anotherStr"
i2++;
}
object.arrayOfPointers[i] = object.anotherStr->data();
object.anotherStr = NULL; //reset this pointer
}
}
Both work as expected, but the vector method is obviously preferred.Code:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
string *str = new string;
*str = "Yes, a string...\n";
*str += "Yes, another string...\n";
*str += "Yes, even another string...\n";
string* anotherStr = new string;
int sizeOfStr = str->size();
vector <string> aVectorOfStrings; //create a vector for storing individual strings
int i2 = 0;
for (int i = 0; i < sizeOfStr; i++) {
if (anotherStr == NULL) {
anotherStr = new string;
}
if (str->at(i2) == '\n') {
i2++; //skip past the new-line character
}
while (str->at(i2) != '\n') { //read up until the next new-line character
*anotherStr += str->at(i2); //add characters to the string
i2++;
}
aVectorOfStrings.push_back(*anotherStr); //add a new string to the vector of strings
delete anotherStr;
anotherStr = NULL; //reset the pointer
if (i == 2) { //we just added the last string to the vector...
break; //out of the loop
}
}
int sizeOfVector = aVectorOfStrings.size();
for (int i = 0; i < sizeOfVector; i++) {
cout<< aVectorOfStrings.at(i) <<endl;
}
cout<< "\nHaha, you got to press Enter..."<<endl;
cin.get(); //make the user press enter
return 0;
}