Linux57:读取人脸数据库并保存到map

目录

1.本章节介绍

2.读取数据库保存到map流程框图

3.读取数据库保存到map的代码截图

[3.1. 加载并连接sqlite3数据库](#3.1. 加载并连接sqlite3数据库)

[3.2. 通过select查询人脸表格的数据](#3.2. 通过select查询人脸表格的数据)

[3.3. 读取查询的结果并循环插入map容器](#3.3. 读取查询的结果并循环插入map容器)

4.代码详解

[4.1 连接数据库](#4.1 连接数据库)

[4.2 定义 task_id](#4.2 定义 task_id)

[4.3 创建 S_THREAD_MAP 对象](#4.3 创建 S_THREAD_MAP 对象)

[thread_map 对象创建后的内存状态](#thread_map 对象创建后的内存状态)

[4.4 查询数据库](#4.4 查询数据库)

[1. sqlite3_prepare()](#1. sqlite3_prepare())

[2. sqlite3_step()](#2. sqlite3_step())

[3. sqlite3_column_text()](#3. sqlite3_column_text())

[4. sqlite3_column_int()](#4. sqlite3_column_int())

[5. sqlite3_column_blob()](#5. sqlite3_column_blob())

[6. memcpy()](#6. memcpy())

7.vector::push_back()

8.make_pair()

9.map::insert()

[4.5 赋值给 thread_map 的成员](#4.5 赋值给 thread_map 的成员)

[4.6 注册到全局](#4.6 注册到全局)

[4.7 完整执行流程图](#4.7 完整执行流程图)

扩展

1.map容器

[1.1 查找相关函数](#1.1 查找相关函数)

[1.2 插入相关函数](#1.2 插入相关函数)

[1.3 删除相关函数](#1.3 删除相关函数)

[1.4 容量相关函数](#1.4 容量相关函数)

[1.5 迭代器相关函数](#1.5 迭代器相关函数)

[1.6 修改相关函数](#1.6 修改相关函数)

[1.7 查找操作详解](#1.7 查找操作详解)

2.stmt和从数据库提取数据

[2.1、stmt 的本质](#2.1、stmt 的本质)

2.2、实际执行过程详解

[2.2.1 准备SQL语句](#2.2.1 准备SQL语句)

[2.2.2 第一次调用 sqlite3_step()](#2.2.2 第一次调用 sqlite3_step())

[2.2.3 提取数据](#2.2.3 提取数据)

1.提取姓名(第0列)

2.提取特征大小(第1列)

3.提取特征数据(第2列)

4.提取图片大小(第3列)

5.提取图片数据(第4列)

[2.2.4 继续下一行](#2.2.4 继续下一行)

2.3完整数据提取流程图

[3.string str(name)](#3.string str(name))

3.1.内存对比

3.2.原因


1.本章节介绍

本章节主要介绍如何通过查询人脸数据库数据库表格把数据存储到map容器,map容器底层是内存处理,用map去处理数据能够达到高效,快速查询的效果。在这个项目里面,map起到了快速查询的功能,key存储的是人脸的结构体(People),value存储的是人脸具体的数据(rockx_face_feature_t)。

2.读取数据库保存到map流程框图

上图是读取数据库并加载到Map的过程,分别有三步:第一步,加载并连接sqlite3数据库、第二步:通过select查询人脸表格的数据、第三步:把表格的数据循环插入到map容器。

3.读取数据库保存到map的代码截图

init_face_data 是读取数据库数据并插入到map容器的自定义函数,这里有几个重要的步骤分别是加载数据库(Connection_sqlite3DataBase)、查询数据库信息并存放到Map容器(QueryPeopleData )、把Map存放到全局变量里面(set_thread_map)

3.1. 加载并连接 sqlite3 数据库

这里封装了一个了SQLITE3连接数据库的函数Connection_sqlite3Database,这个函数的实现如下图

这个函数直接调用了sqlite3的api sqlite3_open来初始化人脸数据库,若返回值不等于SQLITE_OK则初始化数据库失败,否则就初始化成功。

3.2. 通过 select 查询人脸表格的数据

上图是通过sql语句查询人脸表格,"select name, feature_size, face_feature, image_size, image_data from face_data_table ", 其中name : 名字、feature_size 特征值长度、face_feature: 人脸特征数据、image_size 图片长度、image_data 图片数据。face_data_table则是存储人脸的数据库表格。并使用sqlite3_prepare 进行sql的预处理**。**

3.3. 读取查询的结果并循环插入 map 容器

上图是读取sqlite3数据库的数据,并循环插入到map容器。这里的关键是使用了sqlite3提供的函数sqlite3_step来获取select的结果,若结果等于SQLITE_ROW( 表示当前返回结果有数据) 则表示有数据,就获取每一行的数据库数据。

sqlite3_column_text(stmt, 0) 表示的是查找select语句的第一个元素name,索引号是0,类型是字符串,并进行绑定。

sqlite3_column_int(stmt, 1) 表示的是查找select语句的第二个元素feature_size,索引号是1,类型是int, 并进行绑定。

sqlite3_column_blob(stmt, 2) 表示的是查找select语句的第三个元素face_feature,索引号是2,类型是blob, 并进行绑定。

sqlite3_column_int(stmt, 3) 表示的是查找select语句的第四个元素image_size,索引号是3,类型是int, 并进行绑定。

sqlite3_column_blob(stmt, 4) 表示的是查找select语句的第五个元素image_data,索引号是4,类型是blob, 并进行绑定。

读取完上述的数据后,就把上述的数据插到map。

4.代码详解

4.1 连接数据库

int Connection_sqlite3DataBase()

|---------|---------------------|
| 功能 | 打开SQLite3数据库连接 |
| 参数 | 无 |
| 返回值 | 0-成功,失败则exit(1)退出程序 |

4.2 定义 task_id

int task_id = 0;

定义 task_id,标识当前任务或线程的ID编号

4.3 创建 S_THREAD_MAP 对象

S_THREAD_MAP thread_map;
class S_THREAD_MAP {

public:

int map_id;
map<string, rockx_face_feature_t> thread_map;
map<People, rockx_face_feature_t> thread_people_map;

};

|----------|----------------|
| | map_id |
| 类型 | int |
| 访问权限 | public(公开) |
| 用途 | 存储这个map对象的ID编号 |
| 当前值 | 未初始化(垃圾值) |

|----------|-------------------------------------|
| | thread_map |
| 类型 | map<string, rockx_face_feature_t> |
| 访问权限 | public |
| 用途 | 存储 姓名→人脸特征 的映射表 |
| 当前状态 | 空map |

typedef struct {

float feature[512]; // 512维特征向量

int len; // 特征长度,固定512

} rockx_face_feature_t;

map 容器说明:

  • 键(key):string类型,存储姓名
  • 值(value):rockx_face_feature_t类型,存储512维特征向量
  • 特点:自动按姓名排序

map容器详细使用看扩1

|----------|-------------------------------------|
| | thread_people_map |
| 类型 | map<People, rockx_face_feature_t> |
| 访问权限 | public |
| 用途 | 存储 People对象→人脸特征 的映射表 |
| 当前状态 | 空map |

struct People {

string people_name; // 姓名

vector<char> images; // 图片二进制数据

};

thread_map 对象创建后的内存状态

thread_map (对象)

├── map_id = ? (垃圾值,未初始化)

├── thread_map = {} (空map)

└── thread_people_map = {} (空map)

4.4 查询数据库

map<People, rockx_face_feature_t> maps = QueryPeopleData();

|---------|-----------------------------------------------------|
| 功能 | 从数据库查询所有人脸完整数据 |
| 参数 | 无 |
| 返回值 | map<People, rockx_face_feature_t> - People到特征的映射表 |

cs 复制代码
map<People, rockx_face_feature_t> QueryPeopleData()
{
  rockx_face_feature_t rockx_face_feature = {0, 0};
  map<People, rockx_face_feature_t> people_map;
  sqlite3_stmt *stmt;
  char *sql = "select name, feature_size, face_feature, image_size, image_data from face_data_table";

  int id = 0, len = 0;
  char *name;
  int feature_size;
  int image_size;
  vector<char> images;
  People first_people;

  int ret = sqlite3_prepare(db, sql, strlen(sql), &stmt, 0);
  if (ret == SQLITE_OK)
  {
    while (sqlite3_step(stmt) == SQLITE_ROW)
    {
      name = (char *)sqlite3_column_text(stmt, 0);
      printf("name = %s\n", name);
      feature_size = sqlite3_column_int(stmt, 1);
      printf("feature_size = %d\n", feature_size);

      const void *feature = sqlite3_column_blob(stmt, 2);
      memset(rockx_face_feature.feature, 0, feature_size);
      memcpy(rockx_face_feature.feature, feature, feature_size);
      rockx_face_feature.len = feature_size;

      image_size = sqlite3_column_int(stmt, 3);
      printf("image_size = %d\n", image_size);

      const char *image_data = (const char *)sqlite3_column_blob(stmt, 4);
      for (int i = 0; i < image_size; i++)
      {
        images.push_back(image_data[i]);
      }

      first_people.people_name = string(name);
      first_people.images = images;

      string str(name);
      // people_map.insert(pair<const People, rockx_face_feature_t>(first_people, rockx_face_feature));
      people_map.insert(make_pair(first_people, rockx_face_feature));
      // people_map.insert(first_people, rockx_face_feature);
    }
  }

数据提详细流程看扩2

1. sqlite3_prepare()
cs 复制代码
sqlite3_stmt *stmt;
char *sql = "select name, feature_size, face_feature, image_size, image_data from face_data_table";
int sqlite3_prepare(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail)

|----------------|------------------|
| 功能 | 将SQL语句编译成字节码 |
| 参数1 db | 数据库句柄 |
| 参数2 zSql | SQL语句字符串 |
| 参数3 nByte | SQL长度,-1表示自动计算 |
| 参数4 ppStmt | 输出参数,返回预处理语句对象 |
| 参数5 pzTail | 未使用部分指针 |
| 返回值 | SQLITE_OK(0)表示成功 |

2. sqlite3_step()

int sqlite3_step(sqlite3_stmt *pStmt)

项目 说明
功能 执行预处理语句,移动到下一行结果
参数 预处理语句句柄
返回值 SQLITE_ROW - 还有一行数据
SQLITE_DONE - 没有更多数据
3. sqlite3_column_text()

const unsigned char *sqlite3_column_text(sqlite3_stmt *pStmt, int iCol)

|--------------|------------------------------------|
| 功能 | 获取当前行的文本列数据 |
| 参数1 | 预处理语句句柄 |
| 参数2 iCol | 列索引,0表示第1列(name字段) |
| 返回值 | const unsigned char* - 指向文本数据的指针 |

4. sqlite3_column_int()

int sqlite3_column_int(sqlite3_stmt *pStmt, int iCol)

|--------------|-------------|
| 功能 | 获取当前行的整数列数据 |
| 参数1 | 预处理语句句柄 |
| 参数2 iCol | 列索引 |
| 返回值 | 整数值 |

5. sqlite3_column_blob()

const void *sqlite3_column_blob(sqlite3_stmt *pStmt, int iCol)

|--------------|----------------------------|
| 功能 | 获取当前行的二进制数据 |
| 参数1 | 预处理语句句柄 |
| 参数2 iCol | 列索引 |
| 返回值 | const void* - 指向二进制数据的指针 |

6. memcpy()

void *memcpy(void *dest, const void *src, size_t n)

|--------------|--------|
| 功能 | 拷贝内存区域 |
| 参数1 dest | 目标地址 |
| 参数2 src | 源地址 |
| 参数3 n | 拷贝的字节数 |
| 返回值 | 目标地址 |

7.vector::push_back()
cs 复制代码
const char *image_data = (const char *)sqlite3_column_blob(stmt, 4);
for (int i = 0; i < image_size; i++)
{
    images.push_back(image_data[i]);
}

从数据库读取二进制的图片数据,逐字节复制到 vector<char> 容器中。

void push_back(const T& value)

项目 说明
功能 在vector末尾添加一个元素
参数 要添加的元素值
返回值
vector的长度自动增加1

图示

执行前

执行中(逐字节复制)

执行后

8.make_pair()

pair<People, rockx_face_feature_t> make_pair(People first, rockx_face_feature_t second)

项目 说明
功能 创建一对键值对
参数1 键(People对象)
参数2 值(特征向量)
返回值 pair对象
9.map::insert()

pair<iterator, bool> insert(const value_type& val)

项目 说明
功能 向map中插入键值对
参数 pair对象
返回值 pair<迭代器, 是否成功>

QueryPeopleData() 开始

┌─────────────────────────────┐

│ sqlite3_prepare() │

│ 编译SQL语句 │

└─────────────────────────────┘

┌─────────────────────────────┐

│ while(sqlite3_step()) │

│ 遍历每一行数据 │

└─────────────────────────────┘

┌─────────────────────────────┐

│ sqlite3_column_text(stmt,0) │

│ 提取数据库信息 │

└─────────────────────────────┘

┌─────────────────────────────┐

│ sqlite3_column_blob(stmt,4) │

│ 提取图片数据 │

│ for循环 push_back() │

│ 拷贝到 vector<char> │

└─────────────────────────────┘

┌─────────────────────────────┐

│ 构建 People 对象 │

│ people_name = name │

│ images = 图片数据 │

└─────────────────────────────┘

┌─────────────────────────────┐

│ make_pair() + insert() │

│ 存入 people_map │

└─────────────────────────────┘

┌─────────────────────────────┐

│ 返回 people_map │

└─────────────────────────────┘

返回值 maps 的内容

maps (map<People, rockx_face_feature_t>)

├── 键: People1 (姓名="张三", 图片数据)

│ 值: rockx_face_feature (512维特征)

├── 键: People2 (姓名="李四", 图片数据)

│ 值: rockx_face_feature (512维特征)

└── 键: People3 (姓名="王五", 图片数据)

值: rockx_face_feature (512维特征)

4.5 赋值给 thread_map 的成员

thread_map.thread_people_map = maps;

项目 说明
左操作数 thread_map.thread_people_map - S_THREAD_MAP对象的成员
右操作数 maps - QueryPeopleData()返回的map
操作 将maps赋值给thread_people_map

赋值后的状态

thread_map (对象)

├── map_id = ? (仍是垃圾值)

├── thread_map = {} (仍然是空map)

└── thread_people_map = maps (包含数据库所有人脸数据)

4.6 注册到全局

S_THREAD_MAP g_thread_maps[MAX_MAP_NUM]; //类对象数组,全局存储

set_thread_map(task_id, &thread_map);

int set_thread_map(unsigned int map_id, S_THREAD_MAP *map)

{

pthread_mutex_lock(&g_thread_maps_mutex);

g_thread_maps[map_id] = *map;

pthread_mutex_unlock(&g_thread_maps_mutex);

return 0;

}

项目 说明
功能 将线程数据注册到全局管理器中
参数1 task_id 任务ID,这里是0
参数2 thread_map 指向S_THREAD_MAP对象的指针

4.7 完整执行流程图

扩展

1.map容器

1.1 查找相关函数

函数 功能 参数 返回值
find(key) 根据键查找元素位置 key:要查找的键 找到返回指向该元素的迭代器,没找到返回 end()
count(key) 统计键出现的次数 key:要统计的键 map中返回0或1(因为键不能重复)
lower_bound(key) 查找第一个不小于key的位置 key:比较的键 返回指向第一个 >= key 的迭代器
upper_bound(key) 查找第一个大于key的位置 key:比较的键 返回指向第一个 > key 的迭代器
equal_range(key) 获取等于key的范围 key:要查找的键 返回一个pair,包含lower_bound和upper_bound

1.2 插入相关函数

函数 功能 参数 返回值
insert(pair) 插入一个键值对 pair<Key,Value> 对象 返回pair<迭代器,bool>,bool表示是否插入成功
insert(init_list) 插入多个元素 初始化列表
emplace(args) 原地构造并插入 构造键值对的参数 返回pair<迭代器,bool>
emplace_hint(pos, args) 带提示位置的插入 pos:插入位置提示,args:构造参数 返回迭代器

m["key"] = 100; // 方式1:下标(最常用)

m.insert({"key", 100}); // 方式2:初始化列表

m.insert(make_pair("key", 100)); // 方式3:make_pair

m.insert(pair<string,int>("key",100)); // 方式4:pair

1.3 删除相关函数

函数 功能 参数 返回值
erase(key) 按键删除元素 key:要删除的键 返回删除的元素个数(0或1)
erase(iterator) 按迭代器位置删除 iterator:指向要删除元素的迭代器 返回被删除元素的下一个迭代器
erase(first, last) 删除范围内的元素 first:起始位置,last:结束位置
clear() 清空所有元素

1.4 容量相关函数

函数 功能 参数 返回值
size() 获取元素个数 map中键值对的数量
empty() 判断是否为空 true表示空,false表示非空
max_size() 获取最大容量 map能容纳的最大元素个数

1.5 迭代器相关函数

函数 功能 参数 返回值
begin() 获取第一个元素的迭代器 指向第一个元素的迭代器
end() 获取尾后迭代器 指向最后一个元素之后位置的迭代器
rbegin() 获取反向第一个元素的迭代器 指向最后一个元素的反向迭代器
rend() 获取反向尾后迭代器 指向第一个元素之前的反向迭代器
cbegin() 获取常量正向迭代器 不能修改元素的begin()
cend() 获取常量尾后迭代器 不能修改元素的end()

1.6 修改相关函数

函数 功能 参数 返回值
at(key) 访问指定键的值(带边界检查) key:要访问的键 返回值的引用,不存在抛异常
operator[] 下标访问运算符 key:要访问的键 返回值的引用,不存在则自动创建
swap(map) 交换两个map的内容 map:要交换的另一个map对象

1.7 查找操作详解

find(key) 是最常用的

项目 说明
功能 在map中查找指定的键
参数 key - 要查找的键值
返回值 找到 → 返回指向该元素的迭代器(可以访问键和值)
没找到 → 返回 end()(尾后迭代器)
时间复杂度 O(log n)

count(key) 用于快速判断存在

项目 说明
功能 统计指定键出现的次数
参数 key - 要统计的键
返回值 0 - 键不存在
1 - 键存在
时间复杂度 O(log n)

at(key) 安全的访问方式

项目 说明
功能 访问指定键对应的值
参数 key - 要访问的键
返回值 键存在 → 返回值的引用
键不存在 → 抛出 out_of_range 异常
时间复杂度 O(log n)

operator[] 最方便但有风险

项目 说明
功能 访问或创建指定键对应的值
参数 key - 要访问/创建的键
返回值 键存在 → 返回已有值的引用
键不存在 → 自动创建并返回默认值的引用
时间复杂度 O(log n)
注意 即使只读取,如果键不存在也会自动创建!

2.stmt和从数据库提取数据

2.1、stmt 的本质

sqlite3_stmt *stmt;

项目 说明
stmt 是什么 预处理语句对象(Prepared Statement)
它保存什么 SQL语句的编译结果 (字节码)
它不保存什么 不保存查询结果数据
工作原理 像是一个"迭代器",逐行移动获取数据

2.2、实际执行过程详解

2.2.1 准备SQL语句

sqlite3_prepare(db, sql, strlen(sql), &stmt, 0); // 编译SQL语句

SELECT name, feature_size, face_feature, image_size, image_data

FROM face_data_table

假设数据库中有3条记录:

行号 name feature_size face_feature image_size image_data
1 张三 512 [0.1,0.2,...] 102400 [0xFF,0xD8,...]
2 李四 512 [0.3,0.4,...] 98000 [0xFF,0xD8,...]
3 王五 512 [0.5,0.6,...] 105000 [0xFF,0xD8,...]

此时 stmt 的状态:

2.2.2 第一次调用 sqlite3_step()

while (sqlite3_step(stmt) == SQLITE_ROW)

sqlite3_step() 函数详解

项目 说明
功能 执行预处理语句,并移动到下一行结果
参数 stmt - 预处理语句句柄
返回值 SQLITE_ROW - 成功移动到一行数据
SQLITE_DONE - 没有更多数据
SQLITE_ERROR - 执行出错

执行过程:

第1次调用 sqlite3_step(stmt)

返回 SQLITE_ROW,指向第1行数据

执行循环体内的代码(处理第1行)

第2次调用 sqlite3_step(stmt)

返回 SQLITE_ROW,指向第2行数据

执行循环体内的代码(处理第2行)

第3次调用 sqlite3_step(stmt)

返回 SQLITE_ROW,指向第3行数据

执行循环体内的代码(处理第3行)

第4次调用 sqlite3_step(stmt)

返回 SQLITE_DONE(没有第4行了)

退出 while 循环

此时:

stmt 的游标向下移动一行

┌─────────────────────────────────────────┐

│ 第1行: 张三,512,[特征],102400,[图片] ← 现在指向这行 │

│ 第2行: 李四,512,[特征],98000,[图片] │

│ 第3行: 王五,512,[特征],105000,[图片] │

└─────────────────────────────────────────┘

stmt 内部保存了当前行的"位置"

但数据还在数据库里,没有复制到 stmt 中!

2.2.3 提取数据

1.提取姓名(第0列)

name = (char *)sqlite3_column_text(stmt, 0);

printf("name = %s\n", name);

|---------|------------------------------------|
| 功能 | 获取当前行指定列的文本类型数据 |
| 参数1 | stmt - 预处理语句句柄 |
| 参数2 | 0 - 列索引,0表示第1列(name字段) |
| 返回值 | const unsigned char* - 指向文本数据的指针 |

name 的类型是 char*,需要强制类型转换

此时:

  1. SQLite 根据 stmt 保存的位置,去数据库找到第1行

  2. 取出第1行的第0列数据

  3. 把数据复制到内存中

  4. 返回指向这个内存的指针

stmt 本身仍然不保存数据,只是知道去哪里取!
当前指向第1行:name列的值是 "张三"

sqlite3_column_text(stmt, 0) 返回 → 指向 "张三" 的指针

name = 那个指针

printf("%s", name) 输出 → 张三

2.提取特征大小(第1列)

feature_size = sqlite3_column_int(stmt, 1);

printf("feature_size = %d\n", feature_size);
当前指向第1行:feature_size列的值是 512

sqlite3_column_int(stmt, 1) 返回 → 512

feature_size = 512

printf 输出 → feature_size = 512

3.提取特征数据(第2列)

const void *feature = sqlite3_column_blob(stmt, 2); //获取数据指针

memset(rockx_face_feature.feature, 0, feature_size); //清空目标缓冲区

memcpy(rockx_face_feature.feature, feature, feature_size); //拷贝数据

rockx_face_feature.len = feature_size; //设置长度

sqlite3_column_blob() 函数详解

项目 说明
功能 获取当前行指定列的二进制数据
参数1 stmt - 预处理语句句柄
参数2 2 - 列索引,2表示第3列(face_feature字段)
返回值 const void* - 指向二进制数据的指针

人脸特征是一个 512维的float数组,占用 512 × 4 = 2048 字节,是二进制数据,不能用文本存储。

这里有问题

memset(rockx_face_feature.feature, 0, feature_size);

memcpy(rockx_face_feature.feature, feature, feature_size);

feature_size 的值是 512 (特征维度数),但实际字节数应该是 512 × sizeof(float) = 2048

正确写法:

int feature_bytes = feature_size * sizeof(float); // 512 * 4 = 2048

memset(rockx_face_feature.feature, 0, feature_bytes);

memcpy(rockx_face_feature.feature, feature, feature_bytes);

4.提取图片大小(第3列)

image_size = sqlite3_column_int(stmt, 3);

printf("image_size = %d\n", image_size);

|----------|-----------------------|
| 列索引 | 3 - 第4列(image_size字段) |
| 数据类型 | INTEGER |
| 存储内容 | 图片文件的字节数(如 102400) |

5.提取图片数据(第4列)

const char *image_data = (const char *)sqlite3_column_blob(stmt, 4);

数据库中的 image_data 字段存储的是原始图片文件的完整内容

2.2.4 继续下一行

sqlite3_step(stmt); // 移动到第2行

此时:

stmt 的游标移动到第2行

┌─────────────────────────────────────────┐

│ 第1行: 张三,512,[特征],102400,[图片] │

│ 第2行: 李四,512,[特征],98000,[图片] ← 现在指向这行 │

│ 第3行: 王五,512,[特征],105000,[图片] │

└─────────────────────────────────────────┘

之前第1行的数据?已经被释放或覆盖了!

2.3完整数据提取流程图

3.string str(name)

项目 说明
string C++标准库中的字符串类
str 定义了一个string类型的变量,变量名是str
name 一个char*类型的C风格字符串指针
整体作用 将C风格字符串(char*)转换为C++ string对象

3.1.内存对比

转换前 (name):

name (char*指针)

┌────┬────┬────┬────┬────┬────┬────┐

│ '张' │ '三' │ '\0' │ │ │ │ │

└────┴────┴────┴────┴────┴────┴────┘

C风格:以\0结尾的字符数组

3.转换后 (str):

str (string对象)

┌─────────────────────────────┐

│ 内部包含: │

│ - 指向堆内存的指针 │

│ - 字符串长度 (2) │

│ - 容量大小 │

└─────────────────────────────┘

┌────┬────┐

│ '张' │ '三' │

└────┴────┘

C++风格:知道长度,不依赖\0

3.2.原因

场景1:存入map(最常见原因)

cs 复制代码
map<string, rockx_face_feature_t> rockx_face_feature_map;

// ❌ 不能直接用 char* 作为map的键
// map 要求键是 string 类型

// ✅ 需要转换为 string
string str(name);
rockx_face_feature_map[str] = rockx_face_feature;
// 或者
rockx_face_feature_map.insert(pair<string, rockx_face_feature_t>(str, rockx_face_feature));

场景2:赋值给其他string变量

cs 复制代码
People first_people;
first_people.people_name = string(name);  // 直接转换并赋值
// 等价于:
string str(name);
first_people.people_name = str;
相关推荐
原来是猿1 小时前
TCP Echo Server 深度解析:从单进程到线程池的演进之路(下)
linux·服务器·数据库
2301_812539671 小时前
mysql如何限制用户连接数_使用MAX_USER_CONNECTIONS优化并发
jvm·数据库·python
MongoDB 数据平台1 小时前
MongoDB 驱动效能革新:盖雅工场报表查询效率跃升8倍
数据库·mongodb
X56611 小时前
Python Django怎么处理404_关闭DEBUG模式并自定义配置全局404与500友好错误重定向页面
jvm·数据库·python
m0_748554811 小时前
golang如何集成Etcd配置中心_golang Etcd配置中心集成方法
jvm·数据库·python
qwert10371 小时前
深入解析Python标识符:定义、规则、规范与实践指南
开发语言·数据库·python
Jetev1 小时前
Golang怎么做API网关_Golang API网关教程【总结】
jvm·数据库·python
m0_690825821 小时前
Go语言如何发GET请求_Go语言HTTP GET请求教程【总结】
jvm·数据库·python
2301_783848651 小时前
HTML怎么处理右键菜单_HTML contextmenu自定义(已废弃)替代方案【指南】
jvm·数据库·python