简述

sqlite3 接口的核心元素: 两大对象,八大函数;

其中两个对象指的是:
sqlite3 数据库连接对象         数据库的连接句柄(数据库的文件描述符)  代表你打开的那个 sqlite3
的数据库文件,后序对数据库的操作都需要用到这个对象
sqlite3_stmt SQL 语句对象
        关系型数据库的标准的操作语言  我们操作数据库必须通过 SQL 语句
编程流程
1.建立连接 2.打开数据库         sqlite3_open();//打开或者创建一个数据库,返回一个数据库的连接对象 3.操作数据库(必须通过
SQL 语句)         //准备一条 sql 语句                 sqlite3_prepare_v2();
        //绑定参数                 sqlite3_bind_*();         //执行准备好的 sql 语句
                sqlite3_step();                 sqlite3_column();         //释放
sql 语句资源,销毁 sql 语句对象                 sqlite3_finalize();         或
                sqlite3_exec() 4.关闭数据库         sqlite3_close(); //关闭一个 sqlite3
数据库 备注 :在对 sqlite3 代码进行编译是,需要链接 sqlite3 库文件 -lsqlite3
打开或创建一个数据库函数(sqlite3_open)
【头文件】 #include <sqlite3.h> 【函数原型】 SQLITE_API int sqlite3_open(const char
*filename,sqlite3 **ppDb); 【函数作用】         该函数将打开名字为 filename
数据库文件,如果不存在则创建,打开后的文件使用数据库连接对象 ppDb 表示。 【参数含义】         [filename]:
数据库名字,可以是绝对路径,也可以是相对路径         [ppDb]:sqlite3 类型的指针地址,用来操作数据库 【返回值】
        成功返回 SQLITE_OK         失败返回其他一些宏,具体查看 sqlite3.h 文件描述( sqlite3
的代码注释率自称是非常高的 ) 【示例】 sqlite3 * pDB = NULL; //用来表示你打开的数据库 //打开数据库 int ret =
sqlite3_open(argv[1],&pDB);//打开或者创建一个数据库,返回一个数据库的连接对象 if(ret != SQLITE_OK) {
printf("open sqlite3 database error\n"); return 0; }
关闭数据库函数(sqlite3_close)
【头文件】 #include <sqlite3.h> 【函数原型】 SQLITE_API int sqlite3_close(sqlite3* ppDb);
【函数作用】         关闭 ppDb 操作的数据库文件 【参数含义】         [ppDb]:sqlite3 类型的指针,指向要关闭的数据库
【返回值】         成功返回 SQLITE_OK         失败返回其他一些宏 【示例】 //关闭数据库 sqlite3_close(pDB);
//关闭一个 sqlite3 数据库
准备一条 SQL 语句对象(sqlite3_prepare_v2)
【头文件】 #include <sqlite3.h> 【函数原型】 SQLITE_API int sqlite3_prepare_v2( sqlite3
*db, /* 数据库句柄 */ const char *zSql, /* SQL 语句,UTF-8 编码 */ int nByte, /* zSql
的最大长度,以字节为单位。 */ sqlite3_stmt **ppStmt, /* OUT:语句句柄 */ const char **pzTail /*
OUT:指向 zSql 中未使用部分的指针,一般给 NULL*/ ); 【函数作用】         为 db 指向的数据库准备一条 sql
语句,语句的字符形式使用 zSql 参数保存,转换后的 sql语句使用 ppStmt 保存。 【参数含义】         [db]:数据库连接句柄
        [zSql]: 要执行的原始的 SQL 语句的字符串表示形式         [nByte]:用来指定需要转换 zSql 的字节数,当为小于
0 的值时表示转换 zSql 字符串到‘\0’为 止,当为 0 时,表示不需要转换,当大于 0 时则转换相应的字节数         [ppStmt]
:转换后的 sql 语句对象。         [db]: 指向元素 SQL 语句中未使用的部分,一般给 NULL 【返回值】          成功返回
SQLITE_OK         失败返回其他一些宏 【示例】 int num; //存放 ID char name[20] = {0}; //存放
name char Tel[20] = {0}; //存放电话号码 char sql[256] = {0}; //存放 sql 语句
memset(name,0,20); memset(Tel,0,20); scanf("%d%s%s",&num,name,Tel); if(num ==
0) //输入 0 结束 { break; } memset(sql,0,20); sprintf(sql,"INSERT INTO STU
VALUES(%d,'%s','%s');",num,name,Tel); sqlite3_stmt *stmt = NULL;//指针,指向一条语句对象
ret = sqlite3_prepare_v2(pDB,sql,-1,&stmt,NULL); //准备一个语句对象 if(ret !=
SQLITE_OK) { perror("sqlite3_prepare_v2 failed"); sqlite3_close(pDB); return
-1; }
销毁 SQL 语句对象(sqlite3_finalize)
【头文件】 #include <sqlite3.h> 【函数原型】 SQLITE_API int sqlite3_finalize(sqlite3_stmt
*pStmt); 【函数作用】         销毁 pStmt 指向的 SQL 语句对象。 【参数含义】         [pStmt]:SQL 语句对象。
【返回值】         成功返回 SQLITE_OK         失败返回其他一些宏
执行一条 SQL 语句对象(sqlite3_step)
【头文件】 #include <sqlite3.h> 【函数原型】 SQLITE_API int sqlite3_step(sqlite3_stmt*
pStmt); 【函数作用】         执行一条 pStmt 语句对象 【参数含义】         [pStmt]:SQL 语句对象。 【返回值】
        执行成功返回SQLITE_DOME , 一旦执行成功后,sqlite3_step()就不应该被再次调用执行, 除非我们使用
sqlite3_reset()重置 sqlite3_stmt 数据。         当我们的 sql 语句是 读命令,比如"SELECT*
FROM...",返回的数据必然是很多行,而 sqlite3_step 调用每次只返回一行,并且函数返回值为SQLITE_ROW ,所以我们也需要重复调用
sqlite3_step 函数,当所有参数返回完成后,sqlite3_step 返回 SQLITE_DONE. 失败返回其他一些宏 【示例】 //执行准备好的
sql 语句 ret = sqlite3_step(stmt); if(ret == SQLITE_DONE) { printf("sqlite3_step
success\n"); }else { printf("sqlite3_step failed,%d\n",ret); }
普通编程流程示例
#include<stdio.h> #include<sqlite3.h> #include<stdlib.h> #include<string.h>
int main(int argc, char const *argv[]) { sqlite3 * pDB = NULL; //用来表示表示你打开的数据库
//打开数据库 int ret = sqlite3_open("1.db",&pDB);//打开或者创建一个数据库,返回一个数据库的连接对象 if(ret
!= SQLITE_OK) { printf("open sqlite3 database error\n"); return 0; } //准备一条 sql
语句对象,表的列有 ID,NAME,TEL int num; //存放 ID char name[20] = {0}; //存放 name char
Tel[20] = {0}; //存放电话号码 char sql[256] = {0}; //存放 sql 语句 while(1) { //输入插入表中的数据
memset(name,0,20); memset(Tel,0,20); scanf("%d%s%s",&num,name,Tel); if(num ==
0) //输入 0 结束 { break; } memset(sql,0,20); sprintf(sql,"INSERT INTO JIUYUE
VALUES(%d,'%s','%s');",num,name,Tel); sqlite3_stmt *stmt = NULL;//指针,指向一条语句对象
ret = sqlite3_prepare_v2(pDB,sql,-1,&stmt,NULL); //准备一个语句对象 if(ret !=
SQLITE_OK) { perror("sqlite3_prepare_v2 failed"); sqlite3_close(pDB); return
-1; } //执行准备好的 sql 语句 ret = sqlite3_step(stmt); if(ret == SQLITE_DONE) {
printf("sqlite3_step success\n"); }else { printf("sqlite3_step
failed,%d\n",ret); } //释放 sql 语句资源,销毁 sql 语句对象 sqlite3_finalize(stmt); }
//关闭数据库 sqlite3_close(pDB); //关闭一个 sqlite3 数据库 return 0; }
添加数据前表中内容:

执行函数后:

回调函数执行接口(sqlite3_exec)
【头文件】 #include <sqlite3.h> 【函数原型】 SQLITE_API int sqlite3_exec( sqlite3* ppDb,
/* 数据库句柄 */ const char *sql, /* SQL 语句 */ int
(*callback)(void*,int,char**,char**), /* 回调函数 */ void *arg1, /* 回调的第一个参数 */
char **errmsg /* 保存错误信息*/ ); 【函数作用】         执行 sql 语句,将一些处理给到回调函数去处理 【参数含义】
        [ppDb]:SQL 数据库句柄。 [sql] :SQL 语句字符串形式(可以包含多条命令,用;隔开)。         [callback]
:回调函数,通常用来做显示操作。         [arg1]:传递给回调函数的第一个参数,可以是一些控制开关         [errmsg]
:用来保存错误信息。 【回调函数解释】         回调函数的作用是执行我们需要的一些操作,常用的是用回调来打印我们的数据表,我们先来看看回调函数的参数
typedef int(*sqlite_callback)(void* para, int columenCount, char** columnValue,
char**columnName);         [para]:是通过 sqlite3_exec 函数传递过来的一个参数。
        [columenCount]:查询到的这一张表的列数         [columenValue]
:可以看作是一个保存了字符串的一维数组,保存了每条记录的内容         [columenName]:保存列名 备注 :
        1.create,drop,insert,delete,update 这些操作的时候不会调用回调函数,也就是说只有查询操作会调用回调函数。
        2.回调函数不是只执行一次,而是执行 n 次,取决于 select 中产生了多少条结果,也就是需要显示多少列数据。 【返回值】
                执行成功返回 SQLITE_OK                 失败返回其他一些宏 【示例】
#include<stdio.h> #include<sqlite3.h> int my_callback(void* arg,int
ncols,char*col_values[],char*col_names[]) { //打印表头 int i; if(*((int *)arg) ==
1) { for(i = 0;i<ncols;i++) //打印表头 { printf("%s\t",col_names[i]); }
printf("\n"); *((int *)arg) = 0; //表头只打印一次即可 } //打印这一条记录的值 for(i =
0;i<ncols;i++) { printf("%s\t",col_values[i]); //打印表中一行数据 } printf("\n");
return 0; } int main(int argc, char const *argv[]) { sqlite3 * pDB = NULL;
//打开数据库 int ret = sqlite3_open("1.db",&pDB);//打开或者创建一个数据库,返回一个数据库的连接对象 if(ret
!= SQLITE_OK) { printf("open sqlite3 database error\n"); return 0; } const char
*sql = "INSERT INTO JIUYUE VALUES(9,'lishi','17654329987');SELECT * FROM
JIUYUE;"; int flag = 1; //表头打印标志,为 1 打印,为 0 不打印 char *ermsg = NULL; //用来保存出错信息
ret = sqlite3_exec(pDB,sql,my_callback,(void *)&flag,&ermsg); if(ret !=
SQLITE_OK) { printf("sqlite3_exec failed:%s\n",ermsg); } //关闭数据库
sqlite3_close(pDB); //关闭一个 sqlite3 数据库 return 0; } 执行前表中内容: 执行后结果:
获取结果集的列数(sqlite3_column_count)
【头文件】 #include <sqlite3.h> 【函数原型】 SQLITE_API int
sqlite3_column_count(sqlite3_stmt *pStmt); 【函数作用】         获取 pStmt 结果集中的列数
【参数含义】         [pStmt]:SQL 语句对象。 【返回值】         返回语句对象 select 结果集中的列数,没有则返回 0
获取表头信息(sqlite3_column_name)
【头文件】 #include <sqlite3.h> 【函数原型】 SQLITE_API const char
*sqlite3_column_name(sqlite3_stmt *pStmt, int N); 【函数作用】         获取第 N 列的列名
【参数含义】         [pStmt]:SQL 语句对象。         [N]:列序号 【返回值】         成功返回列名字符串地址,失败返回
NULL 【示例】 //获取结果集的列数 int nCols = sqlite3_column_count(stmt); int i = 0; if(flag
== 1) { for(i = 0;i<nCols;i++) //打印一行记录的表头 {
printf("%s\t",sqlite3_column_name(stmt,i)); } printf("\n"); flag = 0; }
获取指定列数据类型(sqlite3_column_type)
【头文件】 #include <sqlite3.h> 【函数原型】 SQLITE_API int
sqlite3_column_type(sqlite3_stmt *pStmt, int iCol); 【函数作用】         获取第 iCol
列数据的数据类型 【参数含义】         [pStmt]:SQL 语句对象。         [N]:列序号 【返回值】
        SQLITE_FLOAT:表示该列数据类型为浮点型         SQLITE_INTEGER: 表示该列数据类型为整型
        SQLITE_TEXT:表示该列数据类型为字符型         SQLITE_BLOB: 表示该列数据类型为 bool
        SQLITE_NULL:表示该列数据类型为空类型
获取指定列数据(sqlite3_column_类型名)
【头文件】 #include <sqlite3.h> 【函数原型】 SQLITE_API const void
*sqlite3_column_blob(sqlite3_stmt*, int iCol); SQLITE_API double
sqlite3_column_double(sqlite3_stmt*, int iCol); SQLITE_API int
sqlite3_column_int(sqlite3_stmt*, int iCol);SQLITE_API sqlite3_int64
sqlite3_column_int64(sqlite3_stmt*, int iCol); SQLITE_API const unsigned char
*sqlite3_column_text(sqlite3_stmt*, int iCol); SQLITE_API const void
*sqlite3_column_text16(sqlite3_stmt*, int iCol); SQLITE_API sqlite3_value
*sqlite3_column_value(sqlite3_stmt*, int iCol); SQLITE_API int
sqlite3_column_bytes(sqlite3_stmt*, int iCol); SQLITE_API int
sqlite3_column_bytes16(sqlite3_stmt*, int iCol); 【函数作用】         获取第 iCol 列的数据元素
【参数含义】         [pStmt]:SQL 语句对象。         [N]:列序号 【返回值】         返回相应类型的数据 【示例】
for(i = 0;i<nCols;i++) //打印一行记录的所有的列 { //获取结果行中第 i 列的类型 int vtype =
sqlite3_column_type(stmt,i); if(vtype == SQLITE_INTEGER) //如果当前列为整型 { int val =
sqlite3_column_int(stmt,i); printf("%d\t",val); }else if(vtype == SQLITE_FLOAT)
//如果当前列为浮点型 { double val = sqlite3_column_double(stmt,i); printf("%f\t",val);
}else if(vtype == SQLITE_TEXT) //如果当前列为字符串 { const char *val =
sqlite3_column_text(stmt,i); printf("%s\t",val); }else { printf("\t\t"); } }
采用 column 类函数打印表示例程序
#include<stdio.h> #include<sqlite3.h> int main(int argc, char const *argv[]) {
sqlite3 * pDB = NULL; //打开数据库 int ret =
sqlite3_open("1.db",&pDB);//打开或者创建一个数据库,返回一个数据库的连接对象 if(ret != SQLITE_OK) {
printf("open sqlite3 database error\n"); return 0; } //准备一条 sql 语句对象
sqlite3_stmt *stmt = NULL;//指针,指向一条语句对象 const char *sql = "SELECT * FROM
JIUYUE;"; ret = sqlite3_prepare_v2( pDB, /* 数据库的连接句柄,打开的数据库对象 */ sql, /*
要执行的原始的 SQL 语句 */ -1, /*zSql 指向的 SQL 语句的长度 */ &stmt, /* OUT:准备之后的 SQL
语句对象,把准备好的语句使用 ppstmt 指向它 */ NULL /*指针,指向元素 SQL 语句中未使用的部分,一般给 NULL */ ); if(ret
!= SQLITE_OK) { perror("sqlite3_prepare_v2 failed"); sqlite3_close(pDB); return
-1; } int r; int flag = 1; //表头打印标志,为 1 打印,为 0 不打印 do { //3.执行 sql 语句,select
有多少条结果,就可以执行多少次语句 r = sqlite3_step(stmt); if(r == SQLITE_DONE) {
printf("sqlite3_step success\n"); break; } //打印 select 语句产生的结果集 //获取结果集的列数 int
nCols = sqlite3_column_count(stmt); int i = 0; if(flag == 1) { for(i =
0;i<nCols;i++) //打印一行记录的表头 { printf("%s\t",sqlite3_column_name(stmt,i)); }
printf("\n"); flag = 0; } for(i = 0;i<nCols;i++) //打印一行记录的所有的列 { //获取结果行中第 i
列的类型 int vtype = sqlite3_column_type(stmt,i); if(vtype == SQLITE_INTEGER)
//如果当前列为整型 { int val = sqlite3_column_int(stmt,i); printf("%d\t",val); }else
if(vtype == SQLITE_FLOAT) //如果当前列为浮点型 { double val =
sqlite3_column_double(stmt,i); printf("%f\t",val); }else if(vtype ==
SQLITE_TEXT) //如果当前列为字符串 { const char *val = sqlite3_column_text(stmt,i);
printf("%s\t",val); }else { printf("\t\t"); } } printf("\n"); } while (r ==
SQLITE_ROW);//表示 sqlite3_step 产生了一行数据 //释放 sql 语句资源,销毁 sql 语句对象
sqlite3_finalize(stmt); //关闭数据库 sqlite3_close(pDB); //关闭一个 sqlite3 数据库 return
0; }

获取 SQL 语句索引值(sqlite3_bind_parameter_index)
【头文件】 #include <sqlite3.h> 【函数原型】 SQLITE_API int
sqlite3_bind_parameter_index(sqlite3_stmt * pStmt, const char *zName); 【函数作用】
        获取 pStmt 语句对象中 SQL 语句中名称为 zName 的索引值 【参数含义】         [pStmt]:SQL 语句对象。
        [zName]:SQL 语句中的子字符串 【返回值】         成功返回该子字符串的索引值,失败返回 0 【示例】 char *sql
= "INSERT INTO STU(ID,NAME,TEL)VALUES(@ID,@NAME,@TEL);"; sqlite3_stmt *stmt =
NULL;//指针,指向一条语句对象 ret = sqlite3_prepare_v2( pDB, /* 数据库的连接句柄,打开的数据库对象 */ sql,
/* 要执行的原始的 SQL 语句 */ -1, /*zSql 指向的 SQL 语句的长度 */ &stmt, /* OUT:准备之后的 SQL
语句对象,把准备好的语句使用 ppstmt 指向它 */ NULL /*指针,指向元素 SQL 语句中未使用的部分,一般给 NULL */ ); //1.获取
sql 语句中占位符的索引值 int var_index[3] = {0};//保存@ID,@NAME,@TEL 在语句对象中的索引值
var_index[0] = sqlite3_bind_parameter_index(stmt, "@ID"); var_index[1] =
sqlite3_bind_parameter_index(stmt, "@NAME"); var_index[2] =
sqlite3_bind_parameter_index(stmt, "@TEL");
绑定索引值(sqlite3_bind_类型名)
【头文件】 #include <sqlite3.h> 【函数原型】 //常用类型 SQLITE_API int
sqlite3_bind_int(sqlite3_stmt*, int, int); SQLITE_API int
sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); SQLITE_API
int sqlite3_bind_double(sqlite3_stmt*, int, double); //其他类型 SQLITE_API int
sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
SQLITE_API int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*,
sqlite3_uint64,void(*)(void*)); SQLITE_API int
sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); SQLITE_API int
sqlite3_bind_null(sqlite3_stmt*, int); SQLITE_API int
sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
SQLITE_API int sqlite3_bind_text64(sqlite3_stmt*, int, const char*,
sqlite3_uint64,void(*)(void*), unsigned char encoding); SQLITE_API int
sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); SQLITE_API int
sqlite3_bind_pointer(sqlite3_stmt*, int, void*, constchar*,void(*)(void*));
SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); SQLITE_API int
sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64); 【函数作用】
        绑定索引值为指定的值 【参数含义】         [第一个参数]:SQL 语句对象。         [第二个参数]:要绑定的索引值
        [第三个参数]:示要绑定的值         [第四个参数]:表示第三个参数的字节大小         [第五个参数]:BLOB
和字符串绑定后的析构函数,一般指定为 NULL 【返回值】         成功返回 SQLITE_OK,失败返回其他宏值 【示例】 var_index[0]
= sqlite3_bind_parameter_index(stmt, "@ID"); var_index[1] =
sqlite3_bind_parameter_index(stmt, "@NAME"); var_index[2] =
sqlite3_bind_parameter_index(stmt, "@TEL"); //2.给索引值绑定自己的值
sqlite3_bind_int(stmt,var_index[0],num);
sqlite3_bind_text(stmt,var_index[1],name,strlen(name),NULL);
sqlite3_bind_text(stmt,var_index[2],tel,strlen(tel),NULL);
复位操作(sqlite3_reset)
【头文件】 #include <sqlite3.h> 【函数原型】 SQLITE_API int sqlite3_reset(sqlite3_stmt
*pStmt); 【函数作用】         调用 sqlite3_reset()函数将 pStmt 语句对象重置到初始状态,准备重新执行。任何使用
sqlite3_bind_*() API 绑定值的 SQL 语句变量在重新绑定时都要重置绑定 【参数含义】         [pStmt]:SQL 语句对象。
【返回值】         成功返回 SQLITE_OK,失败返回其他宏值
采用绑定的方式从文件中添加数据到数据库程序示例
#include<stdio.h> #include<sqlite3.h> int main(int argc, char const *argv[]) {
sqlite3 * pDB = NULL; //打开数据库 int ret =
sqlite3_open("1.db",&pDB);//打开或者创建一个数据库,返回一个数据库的连接对象 if(ret != SQLITE_OK) {
printf("open sqlite3 database error\n"); return 0; } //准备一条 sql 语句对象
sqlite3_stmt *stmt = NULL;//指针,指向一条语句对象 const char *sql = "INSERT INTO
JIUYUE(ID,NAME,TEL)VALUES(@ID,@NAME,@TEL);"; ret = sqlite3_prepare_v2( pDB, /*
数据库的连接句柄,打开的数据库对象 */ sql, /* 要执行的原始的 SQL 语句 */ -1, /*zSql 指向的 SQL 语句的长度 */
&stmt, /* OUT:准备之后的 SQL 语句对象,把准备好的语句使用 ppstmt 指向它 */ NULL /*指针,指向元素 SQL
语句中未使用的部分,一般给 NULL */ ); if(ret != SQLITE_OK) { perror("sqlite3_prepare_v2
failed"); sqlite3_close(pDB); return -1; } //打开文件 FILE * fp =
fopen("table.txt","r"); if(fp == NULL) //打开失败 { perror("fopen error");
sqlite3_finalize(stmt); //打开失败,释放语句对象 sqlite3_close(pDB); //关闭数据库 return -1; }
//获取 sql 语句中占位符的索引值 int var_index[3] = {0};//保存@ID,@NAME,@TEL 在语句对象中的索引值
var_index[0] = sqlite3_bind_parameter_index(stmt, "@ID"); var_index[1] =
sqlite3_bind_parameter_index(stmt, "@NAME"); var_index[2] =
sqlite3_bind_parameter_index(stmt, "@TEL"); while (1) { int num = 0; //保存 ID
char name[20] = {0}; //保存名字 char tel[20] = {0}; //保存电话 int r =
fscanf(fp,"%d%s%s",&num,name,tel); //从文件中获取数据 if(r!=3) { if(feof(fp))
//如果文件读到末尾 { fclose(fp); //关闭文件 break; } continue; } //给索引值绑定自己的值
sqlite3_bind_int(stmt,var_index[0],num);
sqlite3_bind_text(stmt,var_index[1],name,strlen(name),NULL);
sqlite3_bind_text(stmt,var_index[2],tel,strlen(tel),NULL); r =
sqlite3_step(stmt); if(r == SQLITE_DONE) { printf("sqlite3_step success\n");
}else { printf("sqlite3_step failed,%d\n",ret); } //复位 sql 语句对象,方便下一轮的绑定
sqlite3_reset(stmt); } //释放 sql 语句资源,销毁 sql 语句对象 sqlite3_finalize(stmt);
//关闭数据库 sqlite3_close(pDB); //关闭一个 sqlite3 数据库 return 0; } 执行前表中数据 文件中数据 执行后:   

技术
今日推荐
PPT
阅读数 135
下载桌面版
GitHub
百度网盘(提取码:draw)
Gitee
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:766591547
关注微信