简体   繁体   中英

Returning const char* array from function

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 technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM