使用boost库

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、boost实例代码

参考这个帖子编写实例代码:C++ Boost主要特性介绍以及示例代码 - 知乎

该代码使用的是boost库的filesystem

#include <iostream>

int main() {

	boost::filesystem::path path("test.txt");

	if (boost::filesystem::exists(path)) {

		std::cout << "File exists." << std::endl;

	}

	else {

		std::cout << "File does not exist." << std::endl;

	}

	return 0;
}

新建vs工程,创建控制台程序,报错,下面查找资料怎么在vs配置boost库

二、vs配置boost库

1、linux环境

1.1源码编译

2023年12月6日14:01:50 前言

今天帮忙编译compiler_wrapper.so文件的时候,研究里面的脚本和makefile文件。突然意识到一件事情,那就是比如对于一个工程,在linux环境下需要编译成.so文件。那么,必定的,对于香味大点的工程,肯定有第三方开源代码。那么是这样操作的。他是先写一个build_3d的脚本,脚本中主要执行这样的操作。进入每个开源代码下,编译开源代码。即调用开源代码的build_3d.sh。当然这个脚本是可以在开源仓查到的。编译成静态库.a供工程调用。

然后比如一个工程又依赖好几个子文件夹,再写一个build.sh脚本,调用每个文件夹的makefile文件,将子文件夹编译成静态库文件,供调用。

最后当时是调用需要生成的so文件的工程执行makefile生成.so文件,当然makefile就依赖上述静态库文件。

但是实际操作过程中,我是直接先执行的build.sh脚本,目前没有报错,可理解是因为子文件夹并没有直接调用第三方开源库静态库。而是最后编译studio_compiler.so一起调用的。


linux环境下编译boost库的看这个文档:

linux编译boost库并执行程序-CSDN博客

1.2build.sh脚本编写

1.2.1有MakeFileList.txt文件

1.2.2没有MakefileList文件

1.3 Makefile编写

在帮忙编译compiler_wrapper.so动态库的时候,顺带熟悉了下Makefile。其中还遇到一个很奇怪的问题就是其中一个子文件夹的Makefile时,不报错不成功,卡着卡着network就超时了。然后试了好几次又突然好了。其中正式因为这个问题,反复研究makefile,对于Makefile的写法了解了些。下面主要针对这个Makefile分析。

这个子文件夹的Makefile写法如下:

CXXFLAGS = -Werror=return-type -std=c++0x -Wl,-z,relro,-z,now,-z,noexecstack -D_FORTIFY_SOURCE=2 -O2 -s -ftrapv -fPIC -fstack-protector-all -w -c -shared -o -D__STDC_LIMIT_MACROS -Dlinux -DUSE_PGL -I. \
-I./include \
-I../../../3rd/boost/include \
-I../../../platform/HuaweiSecureC/include \
-I../configurator/include \
-I../model/include \
-I../common/include \
-I../operation/include \
-I../../../3rd/gdal/include/linux


CXX = g++
C = gcc
LIB = ar cr
SRC_DIR = ./src
TARGET_DIR = ../../../build/lib/linux
TARGET_FILE = libpreprocessor.a

SRC_CXX=${wildcard $(SRC_DIR)/*.cpp}

OBJ=$(SRC_CXX:.cpp=.o)

all:	$(OBJ)
	ar r $(TARGET_FILE) $(OBJ)                                                                  
	cp $(TARGET_FILE) $(TARGET_DIR)/$(TARGET_FILE)
	 
clean:
	rm -f $(SRC_DIR)/*.o
	rm -f $(TARGET_FILE)
	rm -f $(TARGET_DIR)/$(TARGET_FILE)

%.o: %.cpp
	$(CXX) $(CXXFLAGS) $*.cpp -o $@

然后实际的编译过程是这样的,

[root@ncn4a-mapopenservice-34-12-242 preprocessor]# ll
total 16
drwxrwxr-x 2 root root 4096 Apr 27  2023 include
-rw-rw-r-- 1 root root  894 Apr 27  2023 Makefile
drwxrwxr-x 2 root root 4096 Dec  6 15:26 src
drwxrwxr-x 2 root root 4096 Apr 27  2023 vs
[root@ncn4a-mapopenservice-34-12-242 preprocessor]# make
g++ -Werror=return-type -std=c++0x -Wl,-z,relro,-z,now,-z,noexecstack -D_FORTIFY_SOURCE=2 -O2 -s -ftrapv -fPIC -fstack-protector-all -w -c -shared -o -D__STDC_LIMIT_MACROS -Dlinux -DUSE_PGL -I. -I./include -I../../../3rd/boost/include -I../../../platform/HuaweiSecureC/include -I../configurator/include -I../model/include -I../common/include -I../operation/include -I../../../3rd/gdal/include/linux src/BaseDataLoader.cpp -o src/BaseDataLoader.o
g++ -Werror=return-type -std=c++0x -Wl,-z,relro,-z,now,-z,noexecstack -D_FORTIFY_SOURCE=2 -O2 -s -ftrapv -fPIC -fstack-protector-all -w -c -shared -o -D__STDC_LIMIT_MACROS -Dlinux -DUSE_PGL -I. -I./include -I../../../3rd/boost/include -I../../../platform/HuaweiSecureC/include -I../configurator/include -I../model/include -I../common/include -I../operation/include -I../../../3rd/gdal/include/linux src/DataLoaderUtil.cpp -o src/DataLoaderUtil.o
g++ -Werror=return-type -std=c++0x -Wl,-z,relro,-z,now,-z,noexecstack -D_FORTIFY_SOURCE=2 -O2 -s -ftrapv -fPIC -fstack-protector-all -w -c -shared -o -D__STDC_LIMIT_MACROS -Dlinux -DUSE_PGL -I. -I./include -I../../../3rd/boost/include -I../../../platform/HuaweiSecureC/include -I../configurator/include -I../model/include -I../common/include -I../operation/include -I../../../3rd/gdal/include/linux src/PolygonHandler.cpp -o src/PolygonHandler.o
g++ -Werror=return-type -std=c++0x -Wl,-z,relro,-z,now,-z,noexecstack -D_FORTIFY_SOURCE=2 -O2 -s -ftrapv -fPIC -fstack-protector-all -w -c -shared -o -D__STDC_LIMIT_MACROS -Dlinux -DUSE_PGL -I. -I./include -I../../../3rd/boost/include -I../../../platform/HuaweiSecureC/include -I../configurator/include -I../model/include -I../common/include -I../operation/include -I../../../3rd/gdal/include/linux src/Preprocessor.cpp -o src/Preprocessor.o
g++ -Werror=return-type -std=c++0x -Wl,-z,relro,-z,now,-z,noexecstack -D_FORTIFY_SOURCE=2 -O2 -s -ftrapv -fPIC -fstack-protector-all -w -c -shared -o -D__STDC_LIMIT_MACROS -Dlinux -DUSE_PGL -I. -I./include -I../../../3rd/boost/include -I../../../platform/HuaweiSecureC/include -I../configurator/include -I../model/include -I../common/include -I../operation/include -I../../../3rd/gdal/include/linux src/RoadHandler.cpp -o src/RoadHandler.o
g++ -Werror=return-type -std=c++0x -Wl,-z,relro,-z,now,-z,noexecstack -D_FORTIFY_SOURCE=2 -O2 -s -ftrapv -fPIC -fstack-protector-all -w -c -shared -o -D__STDC_LIMIT_MACROS -Dlinux -DUSE_PGL -I. -I./include -I../../../3rd/boost/include -I../../../platform/HuaweiSecureC/include -I../configurator/include -I../model/include -I../common/include -I../operation/include -I../../../3rd/gdal/include/linux src/PointHandler.cpp -o src/PointHandler.o
g++ -Werror=return-type -std=c++0x -Wl,-z,relro,-z,now,-z,noexecstack -D_FORTIFY_SOURCE=2 -O2 -s -ftrapv -fPIC -fstack-protector-all -w -c -shared -o -D__STDC_LIMIT_MACROS -Dlinux -DUSE_PGL -I. -I./include -I../../../3rd/boost/include -I../../../platform/HuaweiSecureC/include -I../configurator/include -I../model/include -I../common/include -I../operation/include -I../../../3rd/gdal/include/linux src/ObjDataLoader.cpp -o src/ObjDataLoader.o
ar r libpreprocessor.a ./src/BaseDataLoader.o ./src/DataLoaderUtil.o ./src/PolygonHandler.o ./src/FeatureObject.o ./src/Preprocessor.o ./src/RoadHandler.o ./src/PointHandler.o ./src/ObjDataLoader.o
ar: creating libpreprocessor.a
cp libpreprocessor.a ../../../build/lib/linux/libpreprocessor.a

所以由此可见,主要是这一句

%.o: %.cpp
    $(CXX) $(CXXFLAGS) $*.cpp -o $@

 那么CXX就是g++, CXXFLAGS就是上面写的带头文件的一长条。这句话的功能就是将.cpp文件编译成.o文件。所以就了解了Makefile其中这条语句的写法。

1.3.1 报错处理

在实际执行Makefile报了一个错误,

报错: 

/usr/bin/ld:../../../3rd/gdal/lib/linux/libgdal.a: file format not recognized; treating as linker script
/usr/bin/ld:../../../3rd/gdal/lib/linux/libgdal.a:1: syntax error

这个帖子正好说的就是这个问题:

❤️ file format not recognized; treating as linker script 【莫名奇妙的bug系列】❤️_file format not recognized; treating as linker scr-CSDN博客

按照这个帖子查看,确实如帖子所述,libgdal.a文件的格式不对

按照他的说法,需要从linux拉取代码,但是机器上没有git软件,所以需要安装。 

突然发现我的机器,没有联网,就灵机一动准备安装git,突然发现就算我安装好了也拉取不了代码啊。死胡同了啊!

2、windows环境

vs配置boost库即是在windows平台使用boost,可以编译成静态库,也可以编译成动态库吧

在vs配置boost库有直接用安装包安装源码编译安装两种。

2.1.源码编译

源码编译安装需要自行编译

源码编译安装参考此帖子:开源库windows平台编译-CSDN博客

2.1.1下载boost库

下载链接:Boost C++ Libraries - Browse Files at SourceForge.netFree peer-reviewed portable C++ source librariesicon-default.png?t=N7T8https://sourceforge.net/projects/boost/files/

进入下载页面https://sourceforge.net/projects/boost/files/boost/1.82.0/

 解压后,双击bootstrap.bat文件,等待生成b2.exe文件,生成完成

 双击b2.exe文件,生成静态库和动态库。等待大概十分钟,窗口会自动消失。

确认是否生成bin.v2文件夹和stage文件夹,生成即成功。

 2.2vs配置

在vs中配置如下,将包含目录、库目录和附加库目录指向boost安装目录

包含目录:指向开源代码../boost-1.79.0文件夹

库目录:指向开源代码../stage/lib文件夹

附加库目录:指向开源代码../stage/lib文件夹

如果程序目录结构是这样,需要在包含目录中引入头文件路径

修改好后程序还是会报错

一番琢磨,发现是配置管理器没有改成x64,修改好编译就ok了,执行程序!

2.3程序运行

 成功!!

2.4将程序编译成静态库

增加工程代码量,增加智能指针相关代码,参考此贴:C++ Boost主要特性介绍以及示例代码 - 知乎

2.4.1静态库和动态库知识点

链接库概念详解
前言
首先,编程时,我们将存储可以重复使用的代码块文件称为库文件。比如c++常常导入的<stdio.h>,<match.h>等,这些都简称为库。根据应用程序使用库时的链接方式被划分为静态链接库和动态链接库两种。都符合PE文件格式下面为大家详细介绍。

1,静态链接库
(一)库格式
静态链接库(Static-Link Library)通常是以".lib"作为文件拓展名的二进制文件, 也可以是以".a"等;

(二)库内容
静态链接库在生成可执行文件前,将目标文件(.obj),运行时的函数库(.lib),已编译的资源文件(.res)等全部链接到了一起,打包成二进制文件。该文件包含运行时所需的所有代码。因此静态链接库是可以独立运行的(编译时加载);
包含文件:
xxx.h:生成静态链接库时的头文件,包含方法的声明;
xxx.lib :静态链接库,包含了一个或多个方法的具体实现代码的二进制文件;

(三)优,缺点
优点: 包含了全部运行时所需代码,可以独立运行移植性性强;
缺点: 若程序多次调用静态库中的同一代码块,则生成的可执行文件会重复加载此代码段,造成代码冗余,程序占用内存增大;

2,动态链接库
(一)库格式
动态链接库(Dynamic-Link Library)通常是以".dll"作为文件拓展名的二进文件,也可以是以".so",“.sys”,".drv"等;

(二)库内容
动态链接库在生成可执行文件前,将目标文件(.obj),运行时的函数库(.lib),已编译的资源文件(.res)等地址信息全部记录到文件中,打包成二进制文件。该文件包含运行时所需的代码块位置信息。因此动态链接库是不可以独立运行的(运行时加载);
包含
xxx.h:生成动态链接库时的头文件,包含方法的声明;
xxx.lib:动态链接库的导入库;包含了各个函数的地址信息;没有实现代码;
xxx.dll:动态链接库,包含了一个或多个方法的具体实现代码的二进制文件;

(三)优,缺点
优点:
1,若程序多次调用动态库中的同一代码块地址,则生成的可执行文件会加载相同地址的代码块,即使用的是同一块代码(共享代码);真正实现了运行时加载;同时避免了空间上的浪费;
缺点:
1,仅包含了全部运行时所需代码块地址,需要搭配相应的库文件。不可以独立运行,即移植性性强;
2,运行时加载代码块,需要通过动态库记录的地址去寻找相应地代码块,会使得程序性能下降(约%5);
参考文档:链接库dll,lib的创建及使用(超级详解)_lib文件生成_修道-0323的博客-CSDN博客

2.4.2编写代码

其中因为代码问题出现报错如下

boostTest.cpp

#include "boost/filesystem.hpp"
#include <iostream>

#include "smartPointer.h"

int smartPointerFunc() {

	boost::shared_ptr<MyClass> ptr(new MyClass);

	ptr->sayHello();

	return 0;
}

int main() {

	boost::filesystem::path path("test.txt");

	if (boost::filesystem::exists(path)) {

		std::cout << "File exists." << std::endl;

	}

	else {

		std::cout << "File does not exist." << std::endl;

	}

	smartPointerFunc();

	return 0;
}

smartPointer.cpp

//未引入smartPointer.h头文件

#include <iostream>

class MyClass {

public:

	void sayHello() {

		std::cout << "Hello, World!" << std::endl;
	}

};

smartPointer.h

#include <boost/shared_ptr.hpp>

#include <iostream>

class MyClass {

public:

	void sayHello();
};

报错如下:

严重性	代码	说明	项目	文件	行	禁止显示状态
错误	LNK2019	无法解析的外部符号 "public: void __cdecl MyClass::sayHello(void)" (?sayHello@MyClass@@QEAAXXZ),该符号在函数 "int __cdecl smartPointerFunc(void)" (?smartPointerFunc@@YAHXZ) 中被引用	boostTest	C:\Users\source\repos\boostTest\boostTest\boostTest.obj	1	

修改smartPointer.cpp文件如下后编译正常。

#include <smartPointer.h>

#include <iostream>

void MyClass::sayHello() 
{
	
		std::cout << "Hello, World!" << std::endl;
}

目前,创建了一个较为复杂的工程。

2.4.3修改代码

因为静态库链接库没有main函数,所以需要修改代码。同时将工程名改成MyFirstLib。

同时将工程目录改成这样,

代码如下

//boostTest.cpp

#include <iostream>
#include "boostTest.h"
#include "smartPointer.h"

int boostTest() {

	boost::filesystem::path path("test.txt");

	if (boost::filesystem::exists(path)) {

		std::cout << "File exists." << std::endl;

	}

	else {

		std::cout << "File does not exist." << std::endl;

	}

	return 0;
}
//boostTest.h

#include "boost/filesystem.hpp"
#include <iostream>

int boostTest();
//smartPointer.cpp

#include <smartPointer.h>

#include <iostream>

void MyClass::sayHello() 
{
	std::cout << "smart pointer say: hello, World!" << std::endl;
}
//smartPointer.h

#include <boost/shared_ptr.hpp>

#include <iostream>

class MyClass {

public:

	void sayHello();
};

下面主要是参考此贴:https://blog.csdn.net/weixin_43821643/article/details/129415871

选择下述配置,编译器编译出来的项目结果就是链接库;

静态库创建:在项目MyLib属性页中,点击常规-》配置类型-》静态库(.lib)
动态库创建:在项目MyLib属性页中,点击常规-》配置类型-》动态库(.dll)

下面以静态库创建操作为例,如下图所示:

配置完成,点击生成,

打开文件夹

2.5使用静态库.lib

2.5.1 使用方式一:代码配置后使用(隐式加载)

创建vs工程名为libTest1项目,并编写代码

// libTest1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>

#include "boostTest.h"				//步骤一:导入静态库对应的.h文件(相对于libTest1.cpp路径)
#include "smartPointer.h"			//步骤二:导入静态库(相对于libTest1.cpp路径)

#pragma comment(lib, "MyFirstLib")

int main()
{
	//smartPointerFunc2();
	//smartPointerFunc();
	boostTest();
    std::cout << "libTest1 test!\n";
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件

需要单独指出的是,如果不在libTest1项目中配置boost库的路径,会报错。

但是刚才又试了一下好像又不需要了。看下图

运行情况如下

还遗留两个问题:

一、为什么smartPointerFunc()函数调用会失败。

二、新工程是否需要引入lib链接库以来的boost?

紧接上文,第二天过来重新研究了下。因为突然发现工程里有两个.lib文件和两个libTest1.cpp文件,删掉多余的,编译时又提示找不到boost/filesystem.hpp文件,就是还要添加boost库路径。

添加后依然报错

不添加①(指向myFirstLib.lib路径),会报②错 ,添加①路径后,编译无报错。执行成功,结果如下

所以上面遗留的两个问题就迎刃而解。

2.5.2 使用方法二

参考此帖子:VS中lib库的创建和使用_vs生成lib库_Pygmalionn的博客-CSDN博客

 对代码进行了目录结构的调整,新增了include和lib文件夹,可以使结构更加清楚。下面配置环境

1、c/c++常规——附件包含目录

2、链接器常规——附件包含目录 

3、链接器输入——附加依赖项

 编译重新生成解决方案——执行——成功


2024年1月6日15:07:44

这边插入一个理解。在做xxxView的时候,xxxView用了DataCompile的代码。反正直觉就是是联合工程。一直不太懂是什么原理,感觉很牛很神奇。

今天搞需求调试的时候。发现一个string的成员变量的值,我在调试时复制成“hello”,看一下是成员没有被赋值,还是复制后弄丢了。经过复制成hello,发现xxxView中可以读到hello,然后我修改代码后,就注释掉string eleventStr = "hello"的测试代码。看下程序正常了没。但是奇怪的事情就发生了,我竟然在xxxView调试中还能看到hello字段。然后我就思考,难道是内存中数据没有释放。但是不对啊,这是new出来的数据,结束调试数据就没了啊。真是离谱啊。而且我还发现不了原因。

然后同时代码也是离谱,一直调试不出正确的结果,调试看内存中的值也不对。在次纠结了一个中午。下午的时候又在调代码。这时候我突然发现,

是不是datacompile的代码需要重新编译一下 ?!

感觉这个想法很有道理,然后我就试着把datacompilede的代码编译了一下。然后重新试了下。调试字段hello不见了。原来真是这个原因!

所以就总结一下。我现在就知道为什么了。xxxView使用的是dataCompile的动态库dll文件。他们也不是联合工程。所以当我不重新编译datacompile的代码时,xxxView用的一直是旧的dll文件。所以就出现了我之前遇到的奇怪事。

所以动态库文件就是这么用的呀,记录一下。

2.6使用动态库.dll

前言,网上百度到的使用dll文件有好几种方法,但是我看了下mapview代码,好像使用的是_declspec(dllexport)这种方法,将要编译成动态库的源码单独编译成dll链接库,所以之前编译的时候要先编译其他工程。

3、动态和静态链接库的使用方法

在学习使用动态库(dll)和使用静态库(lib)时,学习到动态库要使用.dll和.lib、.h文件。就在想为什么也要使用静态链接.lib文件。查到了这篇文章,感觉解释的比较清楚。

C++ 静态链接库与动态链接库_c++静态链接和动态链接-CSDN博客

文章说明到其实动态链接库里面的.lib文件其实是叫引入库,并不是静态链接库。动态链接库的引入文件.lib和静态链接库.lib文件有着本质的区别。

原文如下:

一个引入库(.lib)文件(也称“导入库文件”)和一个DLL(.dll)文件。虽然引入库的后缀名也是“lib”,但是,动态库的引入库文件和静态库文件有着本质的区别,对一个DLL文件来说,其引入库文件(.lib)包含该DLL导出的函数和变量的符号名,而.dll文件包含该DLL实际的函数和数据。

原文的详细介绍如下:

3.1 定义

1. 静态库(.lib / .a)

.a Archive文件
函数和数据被编译进一个二进制文件,在编译可执行文件的时候,链接器从库(.lib/.a)中复制这些函数和数据并把他们和其他模块组合起来创建最终的可执行文件(.exe)。当发布程序时,只需可执行文件并不需要发布静态库
2. 动态库(.dll / .so)

dll全称:Dynamic Link Library(动态链接库)

shared object全程:shared object(共享动态库)
函数所在的DLL文件和文件中函数位置的信息(入口),代码由运行时加载在进程空间中的DLL提供
一个引入库(.lib)文件(也称“导入库文件”)和一个DLL(.dll)文件。虽然引入库的后缀名也是“lib”,但是,动态库的引入库文件和静态库文件有着本质的区别,对一个DLL文件来说,其引入库文件(.lib)包含该DLL导出的函数和变量的符号名,而.dll文件包含该DLL实际的函数和数据。

3.2 区别和使用

3.2.1. 动态库的导入库和 静态库的区别

动态库的导入库包含地址符号表等信息(属性-》配置属性-》链接器-》高级-》导入库中可设置生成的导入库名称)
静态库包含实际执行代码、符号表等

3.2.2. 使用区别
3.2.2.1. 使用静态库

需要的文件:.h 头文件、.lib静态库文件


头文件:属性-》配置属性-》C/C+±》常规-》附加包含目录
.lib静态库目录:属性-》配置属性-》链接器-》常规-》附加库目录
.lib静态库文件名:属性-》配置属性-》链接器-》输入-》附加依赖项
代码中手动引入静态库:pragma comment(lib,"xxx.lib")

3.2.2.2 使用动态库

需要文件 .h 头文件、 .lib 、 .dll 或者只是 .dll
动态调用/显示调用
创建一个函数指针,其指针数据类型要与调用的 DLL 引出函数一致
通过 Win32 API 函数LoadLibrary()显式的调用DLL,此函数返回 DLL 的实例句柄
通过 Win32 API 函数GetProcAddress()获取要调用的DLL 的函数地 址,把结果赋给自定义函数的指针类型。
使用函数指针来调用 DLL函数。
最后调用完成后,通过 Win32 API 函数FreeLibrary()释放DLL 函数。
注意:显式调用的前提是使用者需要知道想调用的函数的名字、参数、返回值信息,也就是说虽然编译链接用不上.h头文件,但是调用者编程时可能还是要看.h文件作参考来知道函数名字、参数、返回值信息

静态调用/隐式调用
同使用静态库一样引入 .h 、.lib
.dll 文件与可执行文件放到同一目录


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

  • 32
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值