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.
Related
I've been writing an VM/Interpreter combination thingy, I don't know how to exactly describe it.
Everything behaved as it should, now before I have hundreds of lines of code, I wanted to go into Garba Collection, because there were some pointers which somehow got lost, in some way. Not that I didn't delete pointers, I created, but they somehow got lost in the proccess of interpreting/running the code.
So, I wanted to track them. I wrote my own "Memory Manager" in some way, it's just a std::vector, where I collect all pointers in.
To track and allocate pointers, I have following code:
struct MemBlock {
bool free;
void* ptr;
size_t size;
};
std::vector<MemBlock*> mem;
size_t max_size;
size_t mem_size;
int count = 0;
void mem_init(size_t maxSize) {
max_size = size/sizeof(MemBlock*);
}
void* mem_alloc(size_t size) {
for (int i = 0; i < count; i++) {
MemBlock* block = mem[i];
if (block->free) {
mem_size -= block->size;
mem_size += size;
block->free = false;
block->ptr = malloc(size);
block->size = size;
if (block->ptr == nullptr) {
throw std::exception();
}
return block->ptr;
}
}
void* ptr = malloc(sizeof(size));
if (ptr == nullptr) {
throw PointerNullException();
}
MemBlock* block = (MemBlock*) malloc(sizeof(MemBlock));
*block = (MemBlock) {
false,
ptr,
size
};
mem_size += size;
count++;
mem.push_back(block);
return block->ptr;
}
But as soon, as I use mem_alloc() and initialize the object inside of the pointer:
Int* i = (Int*) mem_alloc(sizeof(Int));
*i = (Int) {}; // -- Here
i->value = atoi(advance().c_str());
The GCC AdressSanitizer shows following error:
==5939==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000098 at pc 0x555963d82fc5 bp 0x7fff4ec39070 sp 0x7fff4ec39060
WRITE of size 4 at 0x602000000098 thread T0
If I remove said line, then it just occurs on the nex line. The the pointer does point to a valid memory location, if not it should've had thrown an exception.
I'm sure that I missed something/did something wrong, of course.
But I don't know what. This is how I learned it, or at least what I understood...
Edit:
This would be a minimal reproducible Example:
#include <iostream>
#include <stdlib.h>
#include <vector>
struct Object {
const char* type;
};
template <typename T>
struct Primitive : Object {
T value;
};
struct Int : Primitive<int> {
const char* type = "int";
};
struct MemBlock {
bool free;
void* ptr;
size_t size;
};
std::vector<MemBlock*> mem;
size_t mem_size = 0;
int count = 0;
void* mem_alloc(size_t size) {
for (int i = 0; i < count; i++) {
MemBlock* block = mem[i];
if (block->free) {
mem_size -= block->size;
mem_size += size;
block->free = false;
block->ptr = malloc(size);
block->size = size;
if (block->ptr == nullptr) {
throw std::exception();
}
return block->ptr;
}
}
void* ptr = malloc(sizeof(size));
MemBlock* block = (MemBlock*) malloc(sizeof(MemBlock));
*block = (MemBlock) {
false,
ptr,
size
};
mem_size += size;
count++;
mem.push_back(block);
std::cout << "HI" << std::endl;
return block->ptr;
}
void mem_free(void* ptr) {
for (int i = 0; i < count; i++) {
MemBlock* block = mem[i];
if (block->ptr == ptr) {
free(ptr);
mem_size -= block->size;
block->size = 0;
block->ptr = nullptr;
block->free = true;
}
}
}
int main() {
// Create new Integer-Object
Int* i = (Int*) mem_alloc(sizeof(Int));
std::cout << "[Pointer]: " << i << std::endl;
*i = (Int) {};
i->value = 5;
std::cout << "[Value]: " << i->value << std::endl;
}
Well, thanks to Retired Ninja and Richar Critten, I've got the solution.
In mem_alloc() I've used sizeof(size) to allocate memory to the pointer, which of course is wrong. I guess my head was pretty much off after hours of coding.
But I guess this problem is now solved.
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;
}
Why can not I perform the following line?
delete [] target;
in my case?
Here is the code:
main.cpp
#include <iostream>
using namespace std;
#include "Char.h"
int main()
{
char * text = "qwerty";
char * a = new char[charlen(text)+1];
copyon(a,text);
cout<<a<<endl;
char * test = "asdfghjkl";
assign(&a, test);
assign(&a, a);
char * test1 = new char[26];
for (int i(0); i < 26; i++)
{
test1[i] = char(i+65);
}
test1[26] = '\0';
anotherAssign(test1, a);
cout << test1 << endl;
return 0;
}
Char.cpp
#include "Char.h"
#include <iostream>
#include <cassert>
#include <cstring>
size_t charlen(const char * ps)
{
size_t len=0;
while (ps[len++]);
assert(len-1==strlen(ps));
return len;
}
void assign(char** target, const char* source)
{
if (*target==source)
return;
delete [] *target;
*target = new char[charlen(source)+1];
copyon(*target, source);
return;
}
void anotherAssign(char* target, const char* source)
{
if (target==source)
return;
delete [] target;
target = new char[charlen(source)+1];
copyon(target, source);
return;
}
void copyon(char* const target, const char* const source)
{
char * t = target;
const char * s = source;
while (*t++ = *s++);
//while(*target++ = *source++)
// ;
std::cout << target << " source = " << source << std::endl;
return;
size_t len = charlen(source);
//for (size_t i=0; i<len; ++i)
// target[i]=source[i];
//target[len]='\0';
}
Here is an exception:
If you do:
char * test1 = new char[26];
then your array will go from test1[0] to test1[25].
That means:
test1[26] = '\0';
is out of bounds. At this point, the head is corrupted, and what happens next is undefined (but rarely desirable).
I have a class foo like this:
class foo
{
private:
int* a;
public:
foo()
{
a = new int[4];
cout << "a" << endl;
}
};
When I create new object named foo1 and then I debug, after the allocating line, it yields the result: a 0x005a4580 {-842150451}.
But when I replace all int-s by char-s in class definition, it yields an undesired result:
a 0x005694a0 "ÍÍÍÍýýýý\x6ŒÒ•\x5Ÿ"
that the size of a is now greater than 4.
I dont know what happened. Could you please give me an explanation?
Full code:
#include <iostream>
#include <string>
using namespace std;
class String
{
public:
String(char* data)
{
setSize(0);
while (*(data + size) != '\0')
size++;
this->data = new char[size];
//need to allocate memory for 'data' pointer because 'data' pointer is now on the stack and the data must be on the heap
memcpy(this->data, data, size * sizeof(char));
}
void operator=(String rhs)
{
if (this->data != NULL)
delete[] this->data, data = NULL;
this->data = new char[rhs.getSize()]; //allocate
memcpy(this->data, data, size * sizeof(char));
}
int getSize()
{
setSize(0);
while (*(data + size))
size++;
return size;
}
void setSize(int size)
{
this->size = size;
}
void display()
{
for (int i = 0; i < size; i++)
cout << *(data + i);
}
~String()
{
if (data != NULL)
delete[] data, data = NULL;
}
private:
char* data;
int size;
};
void main()
{
String a("abcd");
String b("1");
a.display();
cout << endl;
cout << b.getSize() << endl;
a = b;
cout << a.getSize() << endl;
system("pause");
}
Whatever you're using to look at a doesn't know how much you allocated. It just knows the type.
In the first version it sees int *, so it shows a single int.
In the second version it sees char *, so it assumes it's a C string and prints whatever is in memory up to the first '\0' byte.
I am trying to create a dynamic string array in c++. When trying to display the contents of my dynamic string array to the console I receive this error:
Exception thrown at 0x0FD670B6 (msvcp140d.dll) in Assignment4.exe: 0xC0000005: Access violation reading location 0xDDDDDDDD.
Here is my code:
DynamicStringArray.h
#pragma once
#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;
class DynamicStringArray
{
public:
DynamicStringArray();
DynamicStringArray(DynamicStringArray &array);
~DynamicStringArray();
int getSize();
void displayContents();
void addEntry(const string &addElement);
string getEntry(int index);
int deleteEntry(const string &deleteElement);
private:
string *dynamicArray;
int size;
};
DynamicStringArray.cpp
#include "stdafx.h"
#include "DynamicStringArray.h"
#include <string>
#include <iostream>
using namespace std;
DynamicStringArray::DynamicStringArray()
{
dynamicArray = NULL;
size = 0;
}
DynamicStringArray::DynamicStringArray(DynamicStringArray &array)
{
if (dynamicArray != NULL)
{
size = 0;
delete [] dynamicArray;
dynamicArray = NULL;
}
size = array.getSize();
dynamicArray = new string[size];
for (int i = 0; i < size; i++)
dynamicArray[i] = array.dynamicArray[i];
}
DynamicStringArray::~DynamicStringArray()
{
cout << "In destructor." << endl;
delete [] dynamicArray;
dynamicArray = NULL;
}
int DynamicStringArray::getSize()
{
return size;
}
void DynamicStringArray::displayContents()
{
if (size != 0)
for (int i = 0; i < size; i++)
cout << "Item-" << i << ": " << dynamicArray[i] << endl;
else
cout << "Array is empty." << endl;
}
void DynamicStringArray::addEntry(const string &addElement)
{
string *temp = new string[size + 1];
for (int i = 0; i < size; i++)
temp[i] = dynamicArray[i];
temp[size] = addElement;
size++;
delete [] dynamicArray;
dynamicArray = temp;
delete[] temp;
}
string DynamicStringArray::getEntry(int index)
{
if ((index >= 0) && (index < size))
{
return dynamicArray[index];
}
return NULL;
}
int DynamicStringArray::deleteEntry(const string &deleteElement)
{
if(size == 0)
{
return false;
}
for (int i = 0; i < size; i++)
{
if (dynamicArray[i] == deleteElement)
{
string *temp = new string[size - 1];
for (int x = 0; x < size - 1; ++x)
{
if (x < i)
temp[x] = dynamicArray[x];
else
temp[x] = dynamicArray[x + 1];
}
delete[] dynamicArray;
dynamicArray = temp;
delete[] temp;
--size;
return true;
}
}
return false;
}
main:
int main()
{
DynamicStringArray dsArray1;
cout << "dsArray1.displayContents():" << endl;
dsArray1.displayContents(); // Should indicate array is empty
cout << "Display dsArray1.getSize()= " << dsArray1.getSize() << endl;
dsArray1.addEntry("Entry-A");
dsArray1.displayContents();
dsArray1.addEntry("Entry-B");
dsArray1.displayContents();
dsArray1.addEntry("Entry-C");
dsArray1.displayContents();
return 0;
}
Can anyone tell me what I am doing wrong. How can i fix this problem?
Please note that all of this is already available by utilizing
std::vector<std::string>. The std::vector class is the dynamic array class that C++ provides, and there is little to no reason to make home-made versions of what is available to you.
Having said this, one glaring issue is that your copy constructor is incorrect. The dynamicArray is uninitialized, but you use it here:
if (dynamicArray != NULL)
There is no guarantee what value dynamicArray has. The fix is to remove this entire block of code in the copy constructor:
if (dynamicArray != NULL)
{
size = 0;
delete [] dynamicArray;
dynamicArray = NULL;
}
Since the copy constructor constructs a brand new object, there is no reason to "pretest" for a NULL pointer and thus do unnecessary work. Remember that the object did not exist, so there is nothing preliminary to do.
The second issue is that you're issuing a delete [] temp; call in the addEntry and deleteEntry functions. Remove these lines, as you are deallocating the memory that you've just assigned to dynamicArray.
The third issue is that you're missing the user-defined assignment operator. The assignment operator has the following signature, and you need to provide the implementation:
DynamicStringArray& operator=(const DynamicStringArray& );
Without this function, assigning a DynamicStringArray to another DynamicStringArray will cause memory leaks and double deallocation of memory when both objects go out of scope.
One implementation can use the copy / swap idiom:
#include <algorithm>
//...
DynamicStringArray& DynamicStringArray::operator=(const DynamicStringArray& rhs)
{
DynamicStringArray temp(rhs);
std::swap(temp.dynamicArray, dynamicArray);
std::swap(temp.size, size);
return *this;
}
Another issue is this:
string DynamicStringArray::getEntry(int index)
{
if ((index >= 0) && (index < size))
{
return dynamicArray[index];
}
return NULL; // <-- Undefined behavior if this is done
}
It is undefined behavior to assign a std::string object with NULL. Either return an empty string, or throw an exception if the index is out of bounds.
In conclusion, I highly recommend that you read up on the Rule of 3 when it comes to designing classes that must implement correct copy semantics.