将const unsigned char *数据分配给数组的正确方法是什么

时间:2014-12-29 02:15:12

标签: c++ arrays sqlite

我有以下代码:

sqlite *sql = new sqlite; // create a new sqlite object.
const char *dbFile = "database.db"; // sqlite database file.

sql->open(dbFile);  // open the connection.
sql->query("SELECT * FROM categories"); // make a query.
int numRows = sql->numRows(); // get the number of rows.
const unsigned char *result[numRows]; // an array to store data that will be brought in from the database.
int index = 0; // counter

while(sql->fetch() != SQLITE_DONE){
    // store the data into an array
    result[index] = sql->getValue("name");
    index++;

    // print the data directly without storing in an array
    cout << sql->getValue("name") << endl;
}
sql->close();

// print the content of the `result` array.
for(int i=0;i<numRows;i++){
    cout << result[i] << endl;
}

结果:

注意:

方法sql->getValue("name");返回const unsigned char*数据。

正如您在结果图中看到的那样,为什么在直接打印数据时出现没有问题,而在打印时不会出现存储在数组中的相同数据?


解决方案

首先:感谢所有帮助我(通过评论或回答)帮助我找到问题解决方案的人。 他们的答案确实应该得到最好的答案。

sqlite *sql = new sqlite;
const char *dbFile = "database.db";
sql->open(dbFile);
sql->query("SELECT * FROM categories");
int numRows = sql->numRows();
char **result = (char**)malloc(sizeof(char*) * numRows);
int index = 0;

while(sql->fetch() != SQLITE_DONE){
    result[index] = (char*)malloc(sizeof(char*) * CHAR_MAX);
    strcpy(result[index], reinterpret_cast<const char*> (sql->getValue("name")));
    index++;

    cout << sql->getValue("name") << endl;
}
cout << "\n\n" << endl;
sql->close();

for(int i=0;i<numRows;i++){
    cout << *(result+i) << endl;
}

结果:

3 个答案:

答案 0 :(得分:1)

好的,请替换此原始代码:

const unsigned char *result[numRows]; // an array to store data that will be brought in from the database.
int index = 0; // counter

while(sql->fetch() != SQLITE_DONE){
    // store the data into an array
    result[index] = sql->getValue("name");
    index++;
}

用这个:

#include <string>
#include <vector>
using std::string; using std::vector;
    ⋮

vector<string> result;
while( sql->fetch() != SQLITE_DONE )
{
    result.emplace_back( reinterpret_cast<const char*>(
        sql->getValue("name")
        ) );
}

并将printf替换为cout <<,或者printf调用替换result[i]替换为result[i].c_str()

所有假设原始代码都有效。

免责声明:编译器未触及的代码。


<强>附录:

你最近的代码 - 没有编译,

const unsigned char **result = (const unsigned char**)malloc(sizeof(const unsigned char*) * numRows);
int index = 0;
while(sql->fetch() != SQLITE_DONE){
    result[index] = (const unsigned char*)malloc(sizeof(const unsigned char*) * CHAR_MAX);
    strcpy(result[index], sql->getValue("name"));
    index++;

    cout << sql->getValue("name") << endl;
}

...可以用C风格的C ++表示为

char** result = new char*[numRows]();
int index = 0;
while( sql->fetch() != SQLITE_DONE )
{
    assert( index < numRows );
    char const* const s = reinterpret_cast<char*>( sql->getValue( "name" ) );
    result[index] = new char[1 + strlen( s )];
    strcpy( result[index], s );
    cout << s << endl;
    ++index;
}

再次假设原始代码有效。

免责声明:同样,编译器未触及代码。此外,读者应该注意,直接使用new,虽然使用malloc进行了改进,但这是不正确的做法。好的做法是使用std::vectorstd::string,正如我先建议的那样。

答案 1 :(得分:1)

我最近开始自己开始查看Sqlite,所以我碰巧遇到了一些我已经解压缩的代码,作为一个例子。为了完成工作,这不应该特别高性能

它还包括使SQL REGEXP函数正常工作的代码。

我们的想法是在select callback期间向数据结构中添加元素。数据结构是std::vector的{​​{1}}。 std::map是从列名称映射的表的列数据和包含列行的std::map

std::vector

注意:我刚看到您不打算使用#include <map> #include <regex> #include <string> #include <vector> #include <iterator> #include <iostream> #include <sqlite3.h> typedef std::map<std::string, std::string> result; typedef std::vector<result> result_vec; #define log(msg) std::cout << msg << std::endl static int select_callback(void* user, int argc, char** argv, char** azColName) { result_vec& v = *static_cast<result_vec*>(user); result r; for(int i = 0; i < argc; ++i) if(argv[i]) r.emplace(azColName[i], argv[i]); v.emplace_back(std::move(r)); return 0; } static void regexp_callback(sqlite3_context* context, int argc, sqlite3_value** argv) { unsigned count = 0; if(argc == 2) { const char* pattern = (const char*) sqlite3_value_text(argv[0]); const char* value = (const char*) sqlite3_value_text(argv[1]); if(pattern && value) { std::string s = value; std::regex r(pattern, std::regex::icase); std::smatch m; if(std::regex_search(s, m, r)) count = m.size(); } } sqlite3_result_int(context, (int) count); } sqlite3* open(const std::string& name) { sqlite3* db; if(sqlite3_open(name.c_str(), &db) != SQLITE_OK) { log("ERROR: opening db: " << name); log("ERROR: " << sqlite3_errmsg(db)); return nullptr; } sqlite3_create_function_v2(db, "REGEXP", 2, SQLITE_ANY, 0, regexp_callback, NULL, NULL, NULL); return db; } bool select(sqlite3* db, const std::string& sql, result_vec& v) { char* error = 0; if(sqlite3_exec(db, sql.c_str(), select_callback, &v, &error) != SQLITE_OK) { log("ERROR: " << error); log("ERROR: " << sqlite3_errmsg(db)); log(" SQL: " << sql); sqlite3_free(error); return false; } return true; } int main() { sqlite3* db = open("mydatabase.db"); if(db) { result_vec results; if(!select(db, "select * from mytable", results)) return 1; for(auto&& row: results) for(auto&& col: row) std::cout << col.first << ": " << col.second << '\n'; } } C ++ 11 功能,所以这个答案不是您要找的(虽然它可能对你有用)。但是我会留给那些可能谷歌这个问题的人。

答案 2 :(得分:1)

您的问题是您正在分配指针而不是复制字符串。这是一个重要的区别。

指定指针不会存储数据&#34;正如你的评论所暗示的,它只是复制指针。 SQLite在完成记录后丢弃数据,因此其缓冲区可以与其他记录一起使用。当你打印出字符串时,指针不再有效。

为了保留字符串以便以后打印,您需要将字符串复制到您自己的缓冲区。

这意味着您需要为字符串分配缓冲区空间,复制字符串,然后在完成后释放空间。

听起来您希望避免使用C ++功能,因此您需要查看malloc()free()strncpy()来完成您的任务。否则,如果你想使用C ++ STL String类,这将很好地解决你的问题。