3

I am trying to add a char array value into a map, but on displaying the value of char array is not coming, however the integer value is displayed. That is ii.first is not displayed, however ii.second is displayed correctly.

Here is the complete code which I am running,

#include <iostream>
#include <cstring>
#include <map>
#include <utility>

using namespace std;

class map_demo {
public:
    class cmp_str {
    public:
        bool operator() (char const *a, char const *b) {
                        return std::strcmp(a, b) <0;
        }
    };

private:
    typedef map <char*, int, cmp_str> ptype;
    ptype p;

public:
    void set_value() {
        char name[20];
        int empid;

        cout<<"Enter the employee name\n";
        cin.getline(name,20);

        // cout<<"name entered=:"<<name;

        cout<<"Enter the employee id\n";
        cin>>empid;

        this->p.insert(map<char *,int>::value_type(name,empid));
    }

    void get_value() {
        cout << "Map size: " << p.size() << endl;

        for(ptype::iterator ii=p.begin(); ii!=p.end(); ++ii) {
            cout <<"the first="<< (*ii).first << ": " << (*ii).second << endl;
        }
    }
};

//=====================================================================
int main() {

    map_demo  mp1;
    mp1.set_value();
    mp1.get_value();
}

The output obtained on running the code:

Enter the employee name
farhan
Enter the employee id
909
Map size: 1
the first=: 909

Here the first = farhan:909, should be the correct output, can anyone make me understand where I am making it wrong??

7
  • 3
    use std::string, not const char* for the key. Commented Jan 21, 2016 at 10:56
  • @RichardHodges, hello sir....I had tried with string as well, may I know what mistake is there in using char*. Also, to mention the key used is char* and not the const char*...pls verify once...thanks... Commented Jan 21, 2016 at 10:59
  • 1
    @FarhanPatel char * is for legacy C-code. The advantages of std::string over it are numerous (type safety, automatic memory management, no buffer overflows, overloaded operators for it, etc etc) Commented Jan 21, 2016 at 11:06
  • 1
    Char * does not manage memory for you. So the second name entered will overwrite the first (your map is storing the address of the string as the key, not the key itself) Commented Jan 21, 2016 at 11:06
  • 1
    take a look at std::make_pair, it is more convenient to use. e.g. p.insert(std::make_pair(name,empid)); Commented Jan 21, 2016 at 11:22

3 Answers 3

2

The problem there as other mentioned is the char *. Also in you case the char * becomes dangling and you are actually pointing to garbage, the reason behind that is that when name goes out of scope that memory is freed, and you are still pointing to that memory, you actually need to copy the data in the map.

this one works

// ConsoleApplication1.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


#include <iostream>
#include <string>
#include <map>
#include <utility>

using namespace std;
class map_demo
{


public:
    class cmp_str
    {
    public:
        bool operator() (char const *a, char const *b)
        {
            return std::strcmp(a, b) <0;
        }
    };

private:
    typedef map <string, int> ptype;
    ptype p;

public:
    void set_value()
    {
        char name[20];
        std::string inval;
        int empid;

        cout << "Enter the employee name\n";
        cin.getline(name, 20);
        inval = name;
        //cout<<"name entered=:"<<name;

        cout << "Enter the employee id\n";
        cin >> empid;

        //this->p.insert(map<char *, int>::value_type(name, empid));
        this->p.insert(std::pair<string , int>(inval,empid));
    }

    void get_value()
    {

        cout << "Map size: " << p.size() << endl;

        for (auto ii = p.begin(); ii != p.end(); ++ii)
        {
            std::string mysf(ii->first);
            //std::cout << mysf << std::endl;
            cout << "the first=" << mysf << ": " << (*ii).second << endl;
        }

    }

};
int main()
{
    map_demo  mp1;
    mp1.set_value();
    mp1.get_value();
}

Is just a quick fix, probably with a bit more effort can be made better. But just to give you an idea.

If you need to do it with char *, then you probably need to allocate memory yourself in bulk, each time you go and ask for a name you copy that in your data struct and retrieve a pointer to it. To properly handle that the way you make your data struct changes a lot in how clean your result will be, but the core point is, you need to manage your memory, copy in a place which will persist and not get lost, and store a pointer to that memory, not to a region of memory freed when you get out of set_value().

Sign up to request clarification or add additional context in comments.

3 Comments

@Marco...Thnx for the concept.....however correct me if am wrong, isn't the string inval also local to the function set_value() which u have created, then how that local var can be added to the map..?
std::string provides copy constructor, in my map I am not passing a pointer or a reference but the object itself, it means that the compiler will call the copy constructor. If I was using a pointer or a ref you will have the same problem. In c++ arrays are noting more then pointers with a little syntactic sugar to access values with the subscription operator aka when you pass an array to a function, you are passing a pointer, not by value. std::string, manages the memory for you, allocation, moving, copying etc.
also @Marco our empid is a local variable in the function, and even that is working...so how's it, as the empid is an integer?? pls clarify this as well....or is it wrong to insert local empid.....
1

This line

this->p.insert(map<char *,int>::value_type(name,empid));  

adds a char* pointer to the map, not the string itself. If the pointer points to the stack (name[] is on the stack) then it will be the potentially the same address in each iteration.

Either use a std::string

e.g.

typedef std::map<std::string, int> ptype;
...
p.insert(std::make_pair(name,empid))

or allocate dynamic memory manually and keep track of the string

char* nameStorage = new char[strlen(name)+1];
strcpy(nameStorage,name);
p.insert(std::make_pair(nameStorage,empid));

2 Comments

sir i agree with the concept as u mentioned that char* is becoming dangling as it is local to the function, but empid is also local however it is getting assigned properly....why is it so? pls clarify?
both the pointer (address the array starts at) and the integer are copied by value, in the case of the integer, that is what you want to copy and store
1

You defined the key of the map like char *

typedef map <char*, int, cmp_str> ptype;
             ^^^^^

So in member function set_value

void set_value() {
    char name[20];
    int empid;

    //...

    this->p.insert(map<char *,int>::value_type(name,empid));
}

the key is assigned with the address of the local valriable name (more precisely with the address of the first character of the array name) that will be destroyed after exiting the function.

After that the key will be invalied because it will point to a non-existent character array.

Moreover the key shall be copy-assignable but arrays do not have the copy assignment operator.

You could use standard class std::array<char, 20> as the key type. For example

typedef map <std::array<char, 20>, int, cmp_str> ptype;

In this case you have to change also cmp_str that it would accept objects of this type.

Another approach is to use standard class std::string instead of the array. Foir example

typedef map <std::string, int> ptype;

2 Comments

our empid is a local variable in the function, and even that is working...so how's it, as the empid is an integer?? pls clarify this as well....or is it wrong to insert local empid.....
@FarhanPatel It is the copy of the object of empid that is stored in the map not its address. The situation with the character array is different. It is the address of the first character of the array name that is stored as key. After exiting the function the array is not alive. So the program has undefined behaviour.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.