【问题标题】:C++ program with map is not giving same resutls as it stored last time带有地图的 C++ 程序给出的结果与上次存储的结果不同
【发布时间】:2012-05-07 08:03:14
【问题描述】:

我有以下程序,它将四个字符串存储在地图中并第一次打印。现在它再次运行以检索存储的值。但是第二次的结果和第一次的结果不一样。

#include <map>
using namespace std;

void fun_call(void **,char * );
main(){
        void *data=NULL;
        char value[100];
        int i=0,j=0;

        char key[][10]={"disk1","disk2","disk3","disk4"};

        cout << "printing all mapped values " << endl ;
        data = (void *) malloc( 100);

        for(j=0;j<2;j++){
        for(i=0;i<4;i++){
                fun_call(&data,key[i]);
                memcpy(value,data,100);
                cout << "key ="<<key[i]<<" value is " << value << endl;
        }
        cout <<"====================="<< endl;
        }
}

void fun_call(void **tmp,char name[10])
{
        void *tmp_data;
        char str[100]="ravindra";
        int len =0;

        static std::map<std::string,void *> name_data_map;
        std::map<std::string,void *>::iterator iter   ;

        iter=name_data_map.find(name) ;

        if ( iter == name_data_map.end())
        {
                len=strlen(str)+strlen(name)+1;
                tmp_data = (void *) malloc ( len );
                strcat(str,name);
                memcpy(tmp_data,str,len);
                name_data_map[name]=tmp_data;
                cout << "Inside the if" << endl ;
        }
        else
                cout << "disk pos "<< iter->first << endl;
        cout << "Outside the if" << endl ;
        iter=name_data_map.find(name) ;
        memcpy(*tmp,iter->second,len);

}

输出:

$ ./a.out 打印所有映射值 如果里面 如果在外面 key =disk1 值为 ravindradisk1 如果里面 如果在外面 key =disk2 值为 ravindradisk2 如果里面 如果在外面 key =disk3 值为 ravindradisk3 如果里面 如果在外面 key =disk4 值为 ravindradisk4 ====================== 磁盘 pos disk1 如果在外面 key =disk1 值为 ravindradisk4 磁盘 pos disk2 如果在外面 key =disk2 值为 ravindradisk4 磁盘 pos disk3 如果在外面 key =disk3 值为 ravindradisk4 磁盘 pos disk4 如果在外面 key =disk4 值为 ravindradisk4

知道为什么第二次迭代将所有数据都提供为:“ravindradisk4”

【问题讨论】:

  • 所有这些内存复制和指针算法都很难理解。您很可能调用了未定义的行为,但没有注意到。请考虑使用 std::string 并再次询问问题是否没有消失。
  • 同意。我想问题出在您分配数组后没有使用memset 将数组初始化为0;无论如何,将 STL 容器与 C 风格的字符串混合使用是一种不好的做法。
  • 为什么在使用字符串时将 void* 存储在地图中?你有什么理由不能像这里所做的那样工作类型安全:stackoverflow.com/questions/10333484/…
  • 可以肯定这段代码不能编译。请展示SSCCE 所描述的一个很好的例子

标签: c++ map iterator


【解决方案1】:

lenfun_call 的开头设置为 0,因此如果在第二次运行中它不会进入您的 ifmemcpy 最后会复制 0 个字节。所以无论key如何,第一次迭代中main()中的最后一个value保持不变。

【讨论】:

    【解决方案2】:

    首先,一般来说,在C++ 中考虑使用new/delete 而不是malloc()/free()

    我不确定您到底想要完成什么(即为什么您不断复制值),但您没有设置长度,因此 memcpy() 不会复制任何内容。

    解决此问题的另一个简单方法是使用存储在 iter-&gt;second 中的指针(请注意,您可以修改 data 并更新该映射条目 - 所以这可能不是您想要的)。

    例如,不要在 main 中为 data 变量分配内存,只需更改此行

    memcpy(*tmp, iter->second, len);
    

    *tmp = iter->second;
    

    现在main中data的指针地址设置为map中存储的指针地址。

    【讨论】:

      【解决方案3】:

      如果您的代码旨在成为一个有效的(或远程惯用的)C++ 程序,那么它有很多问题。

      正如@starbugs 指出的那样,您第二次复制结果时没有使用正确的长度。单行“修复”将是更改:

      memcpy(*tmp,iter->second,len);
      

      ...到:

      memcpy(*tmp,iter->second,strlen((char*)iter->second)+1);
      

      关于为什么脆弱的 C 字符串技术最好用 C++ 方法代替的一些基础知识,我想向人们展示这个:

      Learning Standard C++ As A New Language (PDF) by Bjarne

      一旦您掌握了可能更能接受应该使用 C++ 和标准库的精神。

      您的程序非常简单,很容易展示如何简化它以生成更加健壮且易于阅读的惯用代码:

      #include <map>
      #include <iostream>
      #include <string>
      
      using namespace std;
      
      string fun_call(string name)
      {
          static map<string,string> name_data_map;
      
          map<string,string>::iterator iter;
          iter = name_data_map.find(name);
      
          if (iter == name_data_map.end()) {
              string mapvalue = "ravindra";
              mapvalue += name;
              name_data_map[name] = mapvalue;
              cout << "Inside the if" << endl ;
          }
          else
              cout << "disk pos "<< iter->first << endl;
      
          cout << "Outside the if" << endl;
          iter = name_data_map.find(name) ;
          return iter->second;
      }
      
      int main() {
          string keys[] = {"disk1","disk2","disk3","disk4"};
      
          cout << "printing all mapped values " << endl ;
      
          for(int j = 0; j < 2; j++) {
              for(int i = 0; i < 4; i++){
                  string value = fun_call(keys[i]);
                  cout << "key =" << keys[i] <<" value is " << value << endl;
              }
              cout << "=====================" << endl;
          }
      }
      

      我将停止提供具有相同输出和控制流的基本等效程序。

      注意事项:

      • 在标准 C++ 中,main 必须有一个 int 作为返回类型(虽然它不需要参数或 return 语句,这很奇怪)

      • using namespace std; 行使您不必在标准库类(如字符串、映射及其迭代器)前面输入std::。但是不要把它放在头文件中,因为它可能会导致包含它们的其他源文件出现问题,并且有自己的定义,如果没有消除歧义,可能会与标准名称冲突。

      • 如果您使用标准库,那么值类型在后台进行内存管理,它们使用的内存在类内部分配并在析构函数中释放。如果您需要进行显式内存管理,请使用 new 和 delete。

      【讨论】:

      • 我有点喜欢在你的代码中找到错误的挑战,但我必须支持@HostileFork 在这里所说的内容。另外,我要补充一点,你真的不应该在这里使用静态地图。
      【解决方案4】:

      首先,我什至不确定您的代码是如何编译的。您的 main 函数缺少返回类型,而 void/no-return 只是不好的做法。对其进行重组以适应简单的 0 返回并使其返回类型为int

      此外,在编译之前就缺少几个包含(即 iostream 和 string)。不要使用using namespace std,而是尝试从 std 命名空间中仅“拉”出您需要的东西。一般来说,将其全部引入是一种潜在的危险和不好的做法,因为您将来可能会遇到命名约定冲突(这会带来很多麻烦)。

      回到手头的问题。如果您不尝试/惩罚您的思想,那么您就是在这里应用一些非常糟糕的做法。即使我正在使用移动顶点缓冲区,我也不会做这么多的内存复制和指针移动。并将您的分配与解除分配相匹配,这是一些非常糟糕的内存管理。而在 C++ 中,我们使用 new/delete。

      由于您将指针的地址传递给数据变量,您可以使用 *tmp 简单地修改数据的指针。

      由于您的 name_data_map 是静态的,它可以在循环中存活。因此,iter 的第二个数据成员是指向当前数据对象的实际指针。只需更改第二个函数的最后一行代码:

      *tmp = iter->second;
      

      无论如何,这是我的两分钱......我什至不明白你想要做什么。祝你好运!

      【讨论】:

      • 你提出了一些好的观点,但这不是给定问题的解决方案。它使它部分工作,但说实话,OP 的问题充满了不良做法,静态地图确实弹出。
      • @DomagojPandža 是的,这对我来说是全新的。希望对您有所帮助。
      猜你喜欢
      • 2014-01-30
      • 1970-01-01
      • 2021-06-01
      • 1970-01-01
      • 2020-12-10
      • 2015-08-25
      • 1970-01-01
      • 2012-08-21
      • 1970-01-01
      相关资源
      最近更新 更多