I want to pass a double pointer as argument to a function, but I cant see what I am doing wrong.
#include <iostream>
#include <string>
using namespace std;
void changeString(string ***strPtr) {
strPtr = new string**[1];
*strPtr = new string*[1];
**strPtr = new string("hello");
//cout << strPtr[0][0][0];
}
int main()
{
string **strPtr;
changeString(&strPtr);
//cout << strPtr[0][0];
return 0;
}
The cout in changeString works fine, but the cout in main throws the exception read access violation. strPtr was 0xCCCCCCCC.
Your example is basically equivalent to this:
void changeString(string **strPtr) {
strPtr = new string*[1];
*strPtr = new string("hello");
//cout << strPtr[0][0];
}
int main()
{
string *strPtr;
changeString(&strPtr);
//cout << strPtr[0];
return 0;
}
And that is basically equivalent to this:
void changeString(string *strPtr)
{
strPtr = new string("hello"); // Changes the pointer, not the string!
//cout << strPtr[0];
}
int main()
{
string str;
changeString(&str);
//cout << str;
return 0;
}
At this point it should start to become obvious that you are assigning a new value to the pointer, not the pointed-to object. Consider this:
void change(SomeType t)
{
t = somethingElse;
}
int main()
{
SomeType t;
change(t); // Does not change t.
}
In your case, SomeType happens to be string* (or string***) - you are just overwriting a local variable.
To fix your code, just skip the first line in changeString:
http://coliru.stacked-crooked.com/a/88874ee3601ef853
void changeString(string ***strPtr)
{
*strPtr = new string*[1];
**strPtr = new string("hello");
cout << strPtr[0][0][0];
}
int main()
{
string **strPtr;
changeString(&strPtr);
cout << strPtr[0][0];
return 0;
}
Related
So I am with some XML files and I want to make a function that reads an XML file and returns an array that includes things like parameters and their values. So far, I am able to read the correct values. My problem occurs when I make a const char* read() function and include the code that is in the bottom, and return const char*. It has to be const char* because the return value from parsing the XML file is a const char*. How do I make a fuction that returns an array that I am able to read in a different function?
I tried using pointers from an example I read, but it gives me: cannot convert 'const char*[3][2] to int* in assignement.
How do I use these pointers to arrays properly without gtting a type error?
#include <QCoreApplication>
#include <iostream>
#include <stdio.h>
#include <tinyxml.h>
#include <sstream>
using namespace std;
int main (void)
{
const char* ptr;
ptr = read();
cout<<ptr[0][0]<<endl;
return 1;
}
const char* read()
{
//READING XML FILE
const char* pFilename = "Profile.xml";
TiXmlDocument doc (pFilename);
if(!doc.LoadFile()) return 1;
const char *xmlread [3][2] = {0};
TiXmlElement *pRoot, *pParm;
int i = 0;
pRoot = doc.FirstChildElement("PRO");
if (pRoot) //parsing
{
pParm = pRoot->FirstChildElement("Parameter");
while (pParm)
{
xmlread[i][0] = pParm->Attribute("name");
xmlread[i][1] = pParm->Attribute("value");
pParm = pParm->NextSiblingElement("Parameter");
cout<<xmlread[i][0]<<endl;
cout<<xmlread[i][1]<<endl;
i++;
}
}
return xmlread;
}
You are writing a program in C++, not C, so you really should not be using raw pointers at all! Just because the XML library returns values as const char* does not mean you have to do the same. Especially since you are trying to return pointers owned by an XML document that is destroyed when your function exits, thus invalidating any pointers you store in the array.
If you absolutely need to return an array of strings using raw pointers (which you don't in C++!), it would look something more like this instead:
#include <QCoreApplication>
#include <iostream>
#include <tinyxml.h>
#include <stdio.h>
using namespace std;
char* myStrDup(const char *s)
{
//return strdup(s);
int len = strlen(s);
char *ptr = new char[len+1];
memcpy(ptr, s, len);
ptr[len] = '\0';
return ptr;
}
char*** read(int *count)
{
*count = 0;
//READING XML FILE
TiXmlDocument doc ("Profile.xml");
if (!doc.LoadFile())
return NULL;
TiXmlElement *pRoot = doc.FirstChildElement("PRO");
if (pRoot) //parsing
{
TiXmlElement *pParm = pRoot->FirstChildElement("Parameter");
while (pParm)
{
++(*count);
pParm = pParm->NextSiblingElement("Parameter");
}
}
char ***xmlread;
int i = 0;
try
{
xmlread = new char**[*count];
try
{
pRoot = doc.FirstChildElement("PRO");
if (pRoot) //parsing
{
pParm = pRoot->FirstChildElement("Parameter");
while (pParm)
{
xmlread[i] = new char*[2];
try
{
xmlread[i][0] = NULL;
xmlread[i][1] = NULL;
try
{
xmlread[i][0] = myStrDup(pParm->Attribute("name"));
xmlread[i][1] = myStrDup(pParm->Attribute("value"));
}
catch (...)
{
delete[] xmlread[i][0];
delete[] xmlread[i][1];
throw;
}
}
catch (...)
{
delete[] xmlread[i];
throw;
}
++i;
pParm = pParm->NextSiblingElement("Parameter");
}
}
}
catch (...)
{
for (int j = 0; j < i; ++j)
{
delete[] xmlread[j][0];
delete[] xmlread[j][1];
delete[] xmlread[j];
}
delete[] xmlread;
throw;
}
}
catch (...)
{
return NULL;
}
return xmlread;
}
int main()
{
int count;
char*** ptr = read(&count);
if (ptr)
{
for(int i = 0; i < count; ++)
{
cout << ptr[i][0] << endl;
cout << ptr[i][1] << endl;
}
for(int i = 0; i < count; ++)
{
delete[] ptr[i][0];
delete[] ptr[i][1];
delete[] ptr[i];
}
delete[] ptr;
}
return 0;
}
Not so nice, is it? You could make it slightly nicer by returning an array whose elements are a struct type to hold the string pointers:
#include <QCoreApplication>
#include <iostream>
#include <tinyxml.h>
#include <stdio.h>
using namespace std;
struct NameValue
{
char *name;
char *value;
NameValue() : name(NULL), value(NULL) {}
~NameValue() { delete[] name; delete[] value; }
};
char* myStrDup(const char *s)
{
//return strdup(s);
int len = strlen(s);
char *ptr = new char[len+1];
memcpy(ptr, s, len);
ptr[len] = '\0';
return ptr;
}
NameValue* read(int *count)
{
*count = 0;
//READING XML FILE
TiXmlDocument doc ("Profile.xml");
if (!doc.LoadFile())
return NULL;
TiXmlElement *pRoot = doc.FirstChildElement("PRO");
if (pRoot) //parsing
{
TiXmlElement *pParm = pRoot->FirstChildElement("Parameter");
while (pParm)
{
++(*count);
pParm = pParm->NextSiblingElement("Parameter");
}
}
NameValue *xmlread;
int i = 0;
try
{
xmlread = new NameValue[*count];
try
{
pRoot = doc.FirstChildElement("PRO");
if (pRoot) //parsing
{
pParm = pRoot->FirstChildElement("Parameter");
while (pParm)
{
xmlread[i].name = myStrDup(pParm->Attribute("name"));
xmlread[i].value = myStrDup(pParm->Attribute("value"));
++i;
pParm = pParm->NextSiblingElement("Parameter");
}
}
}
catch (...)
{
delete[] xmlread;
throw;
}
}
catch (...)
{
return NULL;
}
return xmlread;
}
int main()
{
int count;
NameValue* ptr = read(&count);
if (ptr)
{
for (int i = 0; i < count; ++i)
{
cout << ptr[i].name << endl;
cout << ptr[i].value << endl;
}
delete[] ptr;
}
return 0;
}
However, in C++, the best option is to have your function return a std::vector instead, where the struct type holds std::string members for the strings. Let the C++ standard library handle all of the memory management for you:
#include <QCoreApplication>
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <tinyxml.h>
using namespace std;
struct NameValue
{
string name;
string value;
};
vector<NameValue> read()
{
vector<NameValue> xmlread;
//READING XML FILE
TiXmlDocument doc ("Profile.xml");
if (doc.LoadFile())
{
TiXmlElement *pRoot = doc.FirstChildElement("PRO");
if (pRoot) //parsing
{
TiXmlElement *pParm = pRoot->FirstChildElement("Parameter");
while (pParm)
{
NameValue elem;
elem.name = pParm->Attribute("name");
elem.value = pParm->Attribute("value");
xmlread.push_back(elem);
pParm = pParm->NextSiblingElement("Parameter");
}
}
}
return xmlread;
}
int main()
{
try
{
vector<NameValue> elems = read();
for (vector<NameValue>::size_type i = 0; i < elems.size(); ++i)
{
cout << elems[i].name << endl;
cout << elems[i].value << endl;
}
/* or:
for (vector<NameValue>::iterator iter = elems.begin(); iter != elems.end(); ++iter)
{
cout << iter->name << endl;
cout << iter->value << endl;
}
*/
/* or:
for (auto &elem : elems)
{
cout << elem.name << endl;
cout << elem.value << endl;
}
*/
}
catch (const exception &e)
{
cerr << e.what() << endl;
}
return 0;
}
const char* ptr;
...
cout<<ptr[0][0]<<endl;
This cannot possibly work. If ptr is pointer to a(n array of) character, then ptr[0] is a character object (specifically, the first character of the pointed array of characters). Further applying [0] on that character is ill-formed since there is no subscript operator for arguments char and int.
TiXmlDocument doc (pFilename);
...
xmlread[i][0] = pParm->Attribute("name");
...
return xmlread;
You've declared doc as an automatic variable. Automatic variables are destroyed automatically at the end of the scope where they are declared. Attribute member function returns pointers to memory owned by the document. The destructor of TiXmlDocument will destroy the owned memory, and the pointers in the array xmlread will be dangling after the function has returned. The behaviour of accessing memory pointed by a dangling pointer is undefined.
The xmlread array itself is also an automatic variable, and is destroyed at the end of read as well. It is not possible to return an array out of a function, and returning a pointer to an array would simply result in a dangling pointer.
Finally there is the problem that the return type is "pointer to char", while you're attempting to return an array of arrays of pointers to char. That is simply ill-formed.
You can return containers from a function such as std::vector. You can structure the "rows" of your 2d array into a readable form using a class that contains instances of std::string. Remy has shown you how to do this in practice in another answer.
Is there a way to use pointers correctly instead of having to change my array and add struct types?
Well, if you can change where you keep the array, then a minimal fix to your code is to put the array into main, and pass a reference to it into read, so that read can fill it. You must also do the same for TiXmlDocument.
The cplusplus.com documentation on getenv() states...
The pointer returned points to an internal memory block, whose content or validity may be altered by further calls to getenv
...which I take to mean, "If you want to keep the content, copy it." So, since I need to retrieve several variables, I wrote a couple of little wrapper functions:
#include <iostream>
#include <string.h>
using namespace std;
void getEnv (char *val, const char *var) {
val = nullptr;
char *enVar = getenv(var);
if (enVar != nullptr) {
val = new char[strlen(enVar) + 1];
strcpy(val, enVar);
}
}
void getEnv (int &val, const char *var) {
val = -1;
char *enVar = getenv(var);
if (enVar != nullptr) {
val = atoi(enVar);
}
}
int main() {
char *textMode = nullptr;
int cLen = 0;
getEnv(cLen, "CONTENT_LENGTH");
cout << cLen << endl << endl;
getEnv(textMode, "TEXT_MODE");
if (textMode == nullptr)
cout << "Not set.";
else
cout << "[" << textMode << "]<br>\n";
return 0;
}
The int version works as expected, but I get nothing back from the char version, and I mean nothing: if I don't initialize *textMode at declaration it remains an uninitialized pointer.
It's pointers, right? Right? I know it is. Gotta be pointers. I'll figure them out one of these days, but hey -- at least I got my linked list to work! Yay!
Your second function takes val (an int) by reference: void getEnv (int &val, const char *var) and so can modify the variable passed to it as you expect.
Your first function takes val (a char*) by value: void getEnv (char *val, const char *var) so modifying val has no affect on the variable passed to it. A simple solution is to simply take it as a reference as well: void getEnv (char *&val, const char *var)
Follow up to my comments and the OP's response to them.
Here's what I was thinking:
#include <iostream>
#include <string.h>
using namespace std;
// Use a class to encapsulate the data need to be captured
// in an environment variable.
class EnvironmentVariable
{
public:
EnvironmentVariable(char const* name) : name_(name), isSet_(false)
{
char *val = getenv(name);
if ( val != nullptr )
{
isSet_ = true;
this->value_ = val;
}
}
bool isSet() const
{
return isSet_;
}
void getValue(char const*& val) const
{
if ( isSet_ )
{
val = this->value_.c_str();
}
else
{
val = nullptr;
}
}
void getValue(int& val) const
{
if ( isSet_ )
{
val = stoi(this->value_);
}
else
{
val = 0; // Find a suitable default value
}
}
private:
std::string name_;
std::string value_;
bool isSet_;
};
int main() {
char const* textMode = nullptr;
int cLen = 0;
EnvironmentVariable env1("CONTENT_LENGTH");
env1.getValue(cLen);
cout << cLen << endl << endl;
EnvironmentVariable env2("TEXT_MODE");
env2.getValue(textMode);
if (textMode == nullptr)
cout << "Not set.\n";
else
cout << "[" << textMode << "]<br>\n";
return 0;
}
I made a class str for practice, and I use operator = to
assign an object into another.
I make like this.
#include <string.h>
class Str{
private:
char *str;
int len;
int num;
public:
Str(int leng);
Str(char *neyong);
~Str();
int length(void);
char *contents(void);
int compare(Str a);
int compare(char *a);
void operator=(char *a);
void operator=(Str a);
};
Str::Str(char *neyong){
len = strlen(neyong);
str = new char[len + 1];
strcpy(str, neyong);
}
Str::~Str(){
delete[] str;
}
int Str::length(void){
return len;
}
char* Str::contents(void){
return str;
}
void Str::operator=(Str a){
len = a.length();
str = new char[len+1];
strcpy(str, a.contents());
}
(I skip some functions for ease of reading)
And I execute this like below code.
Str a("hahahaha"), b("hihihihihihi");
cout << a.contents() << endl;
cout << b.contents() << endl;
a = b;
cout << b.contents() << endl;
cout << a.contents() << endl;
Problem: When I assign a = b, then destructors of b is
automatically called. Probably b.~Str() erases all its content and hence b.contents() doesn't return proper value.
How can I solve this problem????
How to use properly external pointers for allocating memory in constructors and deallocating in destructors in C++? Following is a code example that is not working properly. Please consider it.
#include <iostream>
using namespace std;
class Foo
{
public:
Foo() : str(nullptr)
{
str = new string();
}
~Foo()
{
if (str != nullptr)
{
delete str;
str = nullptr;
}
}
void getString(string ** pStr)
{
*pStr = str;
}
private:
string * str;
};
int main()
{
string * mainStr = nullptr;
Foo().getString(&mainStr);
if (mainStr == nullptr)
cout << "is null";
else
cout << "is not null";
return 0;
}
How to write above code in order to mainStr variable has the correct value (nullptr in this case)?
In C++, you will need an instance of an object in order to use it's non-static member functions.
In your case:
int main(void)
{
Foo my_foo;
std::string * p_string = nullptr;
my_foo.getString(&p_string);
if (mainStr == nullptr)
cout << "is null";
else
cout << "is not null";
return 0;
}
You will need to test pStr in getString for null, as assigning to a null pointer is undefined behavior.
It's crashing at the very end of the main() function where it needs to delete the starters objects. The error message that pops up when I run the program says: Debug assertion failed! Expression: _BLOCK_IS_VALID(pHead->nBlockUse). How do i fix it from crashing when deleting the starters objects?
#include <iostream>
#include <fstream>
#include "olympic.h"
using namespace std;
ofstream csis;
int main() {
const int lanes = 4;
Ranker rank(lanes);
csis.open("csis.txt");
// First make a list of names and lane assignments.
Competitor* starters[lanes];
starters[0] = new Competitor("EmmyLou Harris", 1);
starters[1] = new Competitor("Nanci Griffith", 2);
starters[2] = new Competitor("Bonnie Raitt", 3);
starters[3] = new Competitor("Joni Mitchell", 4);
// The race is run; now assign a time to each person.
starters[0]->setTime((float)12.0);
starters[1]->setTime((float)12.8);
starters[2]->setTime((float)11.0);
starters[3]->setTime((float)10.3);
// Put everyone into the ranker.
for (int i = 0; i < lanes; i++)
rank.addList(starters[i]);
// Now print out the list to make sure its right.
cout << "Competitors by lane are:" << endl;
csis << "Competitors by lane are:" << endl;
for (int i = 1; i <= lanes; i++)
rank.getLane(i)->print();
// Finally, show how they finished.
cout << "Rankings by finish are:" << endl;
csis << "Rankings by finish are:" << endl;
for (int i = 1; i <= lanes; i++)
rank.getFinish(i)->print();
for (int i = 0; i < lanes; i++)
delete starters[i];
csis.close();
}
ranker.cpp:
#include "ranker.h"
#include "competitor.h"
#include <stdlib.h>
Ranker::Ranker(int lanes) {
athlete = new Competitor*[lanes];
numAthletes = 0;
maxAthletes = lanes;
}
int Ranker::addList(Competitor* starter) {
if (numAthletes < maxAthletes && starter != NULL) {
athlete[numAthletes] = starter;
numAthletes++;
return numAthletes;
}
else
return 0;
}
Competitor* Ranker::getLane(int lane) {
for (int i = 0; i < numAthletes; i++) {
if (athlete[i]->getLane() == lane) {
return athlete[i];
}
}
return NULL;
}
Competitor* Ranker::getFinish(int position) {
switch(position) {
case 1:
return athlete[3];
break;
case 2:
return athlete[2];
break;
case 3:
return athlete[1];
break;
case 4:
return athlete[0];
break;
}
return NULL;
}
int Ranker::getFilled() {
return numAthletes;
}
Ranker::~Ranker() {
delete [] athlete;
}
competitor.h:
#ifndef _COMPETITOR_H
#define _COMPETITOR_H
class Competitor {
private:
char* name;
int lane;
double time;
public:
Competitor(char* inputName, int inputLane);
Competitor();
void setTime(double inputTime);
char* getName();
int Competitor::getLane();
double getTime();
void print();
~Competitor();
};
#endif
competitor.cpp:
#include "competitor.h"
#include <string>
#include <iostream>
#include <iomanip>
using namespace std;
Competitor::Competitor(char* inputName, int inputLane) {
name = inputName;
lane = inputLane;
}
Competitor::Competitor() {
name = 0;
lane = 0;
time = 0;
}
void Competitor::setTime(double inputTime) {
time = inputTime;
}
char* Competitor::getName() {
return name;
}
int Competitor::getLane() {
return lane;
}
double Competitor::getTime() {
return time;
}
void Competitor::print() {
cout << setw(20) << name << setw(20) << lane << setw(20) << setprecision(4) << time << endl;
}
Competitor::~Competitor() {
delete [] name;
}
Call stack:
before crash: http://i.imgur.com/d4sKbKV.png
after crash: http://i.imgur.com/C5cXth9.png
After you've added Competitor class, it seems the problem is that you delete its name in Competitor's destructor. But you assign it from string literal which can't really be deleted. I'm sure the stack trace leading to assertion will prove that.
One way of solving the problem would be using std::string to store the name.
Problem is when deleting the char* value on destructor, which is assigned with const char instead new char. So i have slightly changed the constructor to copy the const char to new char.
Competitor::Competitor(char* inputName, int charlen, int inputLane)
{
name = new char[charlen + 1];
memcpy(name , inputName, charlen );
name [charlen] = '\0';
lane = inputLane;
}