在 C++ 向量中存储指针时出错

Posted

技术标签:

【中文标题】在 C++ 向量中存储指针时出错【英文标题】:Error when storing pointers in c++ vectors 【发布时间】:2020-04-10 17:39:56 【问题描述】:

我正在尝试使用 c++ 向量来存储指向如下结构的指针:

  struct SourceDir 
    int id;
    const unsigned char *alias;
    const unsigned char *description;
    const unsigned char *path;
  ;

但是,我无法检索似乎已损坏的值

      std::vector<SourceDir*> source_dirs;
      while ((step = sqlite3_step(stmt)) != SQLITE_DONE) 
        if (step != SQLITE_ROW) 
          std::cerr << "[ERROR] internal error (SQLite error code" << step << ")\n";
          exit(1);
        

        struct SourceDir *sourceDir = new SourceDir;
        sourceDir->id = sqlite3_column_int(stmt, 0);
        sourceDir->alias = sqlite3_column_text(stmt, 1);
        sourceDir->path = sqlite3_column_text(stmt, 2);
        sourceDir->description = sqlite3_column_text(stmt, 3);

        source_dirs.push_back(sourceDir);
      

      for (std::vector<SourceDir*>::iterator it = source_dirs.begin() ; it != source_dirs.end() ; ++it) 
        SourceDir *s = *it;
        std::cerr << s->description << "\n";
      

它给出像这样的随机值:

@O?V
@O?V

我不明白我做错了什么

【问题讨论】:

sqlite 函数返回指向与语句句柄关联的内部内存的指针,这些指针很快就会被销毁。您保存的指针最终指向毫无价值的垃圾。关于如何在 C++ 应用程序中管理内存的更多信息和理解,您应该查阅 C++ 教科书。 你为什么不立即获取那些 sqllite 函数的返回值,并从中创建真正的 std::string,而不是在 unsigned char * 中使用 unsigned char * 指针? 谢谢你的建议,我去试试 【参考方案1】:

C 字符串的类型为 char*const char*。因为它们很常见而且仍然很重要,所以&lt;&lt; 运算符在std::ostream 中有一个额外的重载。 char* 的这种特殊重载会打印取消引用的字符,直到找到空字节。

但是,unsigned char* 是不同的类型,将使用另一个重载。在这里,只需打印指针值。这就是你所看到的。

解决方案:只需将您的字符串成员更改为const char*

编辑:这个答案是完全错误的。 unsigned char* 也选择了相同的 C 字符串重载。

【讨论】:

对不起,它没有改变结果..我将结构更改为具有 const char* 成员,然后在将 sqlite3_column_text 调用为 (const char*) 时进行了强制转换,但它仍在打印指针价值观.. 你说得对,我的参考资料已经过时了。 感谢您帮助我!【参考方案2】:

指针很好,但它们指向的内存内容可能发生了变化(例如,这里:https://sqlite.org/capi3ref.html#sqlite3_column_blob 你可以读到返回的指针仅在一段时间内有效)。

一般来说,避免在 C++ 中使用原始指针。在这种情况下,您可以只使用 std::string 来复制 sqlite 提供的数据。

比较这段代码,它做了一些小的改动以避免手动内存管理和原始指针:

struct SourceDir

        int id;
        std::string alias;
        std::string description;
        std::string path;
;



std::vector<SourceDir> source_dirs;
while ((step = sqlite3_step(stmt)) != SQLITE_DONE) 
        if (step != SQLITE_ROW) 
                std::cerr << "[ERROR] internal error (SQLite error code" << step << ")\n";
                exit(1);
        

        source_dirs.emplace_back();
        auto& sourceDir = source_dirs.back();
        sourceDir.id = sqlite3_column_int(stmt, 0);
        sourceDir.alias = sqlite3_column_text(stmt, 1);
        sourceDir.path = sqlite3_column_text(stmt, 2);
        sourceDir.description = sqlite3_column_text(stmt, 3);


for (const auto& s : source_dirs)

        std::cerr << s.description << "\n";

【讨论】:

谢谢斯利马克!【参考方案3】:

从 sqlite3_column_text 返回的数据归 sqlite3 库所有,仅在那个时刻可用。在读取下一列或下一行之前,我们需要将其保存到我们自己的空间中。 sqlite3 内部的相同数据将用于存储新返回的数据。这是一个示例,您可以尝试解决此问题。

struct SourceDir 
  int id;
  unsigned char *alias;
  unsigned char *description;
  unsigned char *path;
;

std::vector<SourceDir*> source_dirs;
while ((step = sqlite3_step(stmt)) != SQLITE_DONE) 
  if (step != SQLITE_ROW) 
    std::cerr << "[ERROR] internal error (SQLite error code" << step << ")\n";
    exit(1);
  
  struct SourceDir *sourceDir = new SourceDir;
  sourceDir->id = sqlite3_column_int(stmt, 0);
  const unsigned char *col = sqlite3_column_text(stmt, 1);
  sourceDir->alias = new unsigned char[strlen(col)+1];
  strcpy((char *)sourceDir->alias, col);
  col = sqlite3_column_text(stmt, 2);
  sourceDir->path = new unsigned char[strlen(col)+1];
  strcpy((char *)sourceDir->path, col);
  col = sqlite3_column_text(stmt, 3);
  sourceDir->description = new unsigned char[strlen(col)+1];
  strcpy((char *)sourceDir->description, col);
  source_dirs.push_back(sourceDir);


std::vector<SourceDir *>::iterator it = source_dirs.begin();
for (; it != source_dirs.end() ; ++it)
  std::cout << (*it)->id << " \"" << (*it)->alias << "\" \"" 
            << (*it)->path << "\" \"" << (*it)->description 
            << "\"\n";

//to clean up
it = source_dirs.begin();
for (; it != source_dirs.end(); it++) 
  delete [] (*it)->alias;
  delete [] (*it)->path;
  delete [] (*it)->description;
  delete *it;

source_dirs.clear();

【讨论】:

以上是关于在 C++ 向量中存储指针时出错的主要内容,如果未能解决你的问题,请参考以下文章

将指针向量元素推回非指针向量c ++时出错

使用 unordered_map 值初始化指向向量的指针时出错

在 C++ 的构造函数中分配对象向量时出错

在 C++ 中调用指向成员函数的指针时出错

通过单应矩阵转换向量时出错 - C++ OpenCV

编译类型向量类的私有成员时出错 - C++ [重复]