c++ crash日志

摘要: 在android上开发c++应用, crash日志都是汇编码, 很难对应到c++代码中去. 通过此文, 你可以定位到程序崩溃时的C++代码, 精确查找问题.



背景介绍

  1. 本文主要内容: 利用android的crash log来对c++开发的android应用进行错误定位.

  2. 容易稳定复现的BUG, 一般可以通过断点调试来解决. 如果测试人员也无法稳定复现, log就成了程序吊定位问题的救命稻草.

  3. 通用操作系统都有自己的日志系统, android也不例外. 救命稻草已经给你了~ ( 怎样查看android的系统日志 )

  4. 但是, android的系统日志在c++代码崩溃时, 打印的都是内存地址和寄存器. 比如, 这样:

06-20 15:54:35.331 23889 23889 I DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
06-20 15:54:35.331 23889 23889 I DEBUG   : Build fingerprint: 'google/razorg/deb:4.4.2/KOT49H/937116:user/release-keys'
06-20 15:54:35.331 23889 23889 I DEBUG   : Revision: '0'
06-20 15:54:35.331 23889 23889 I DEBUG   : pid: 1981, tid: 2020, name: Thread-3399  >>> com.guangyou.ddgame <<<
06-20 15:54:35.331 23889 23889 I DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000028
06-20 15:54:35.431   187   710 D audio_hw_primary: out_set_parameters: enter: usecase(0: deep-buffer-playback) kvpairs: routing=2
06-20 15:54:35.511 23889 23889 I DEBUG   :     r0 76d94458  r1 00000000  r2 00000000  r3 00000000
06-20 15:54:35.511 23889 23889 I DEBUG   :     r4 760c1a48  r5 751e2440  r6 00000001  r7 760c1a48
06-20 15:54:35.511 23889 23889 I DEBUG   :     r8 00000001  r9 76c96f3c  sl 76c861c0  fp 76d94444
06-20 15:54:35.511 23889 23889 I DEBUG   :     ip 00000001  sp 76d94430  lr 75a81bd8  pc 75a81bdc  cpsr 600f0010
06-20 15:54:35.511 23889 23889 I DEBUG   :     d0  746968775f327865  d1  6a6e6169642f675f
06-20 15:54:35.511 23889 23889 I DEBUG   :     d2  5f6f616978757169  d3  676e702e6e776f6d
06-20 15:54:35.511 23889 23889 I DEBUG   :     d4  0000000009000000  d5  0000000000000000
06-20 15:54:35.511 23889 23889 I DEBUG   :     d6  0000000000000000  d7  0000000000000000

这密密麻麻的都是些神马, 是人看的么?

饿. 这个麻… 谁让你当程序猿! 让你当! 活该要看天书!

硬着头皮也要来, 我们就来讲讲怎么消化天书吧~

怎样获取android的系统日志

假设你已经安装了 Android Develop Tools, 可以成功调用adb. 并打开android开发用机的调试模式, 连接到电脑.

打开命令行, 在命令行输入: adb logcat. 就可以看到满屏幕的日志啦.
输入adb logcat --help可以看到 logcat的用法提示.

这里有两个参数特别提醒一下, 比较常用:
1. -v XXXX: 用来选择log输出样式, 一般建议 threadtime, 更加详细.
2. -d: 让log一次性输出后马上完毕. 如果没有此命令, logcat 工具会一直输出, 即使更新在界面上.

如果需要保存log到文件, 方便以后查看. 可输入命令:
adb logcat -v threadtime -d > log.txt

理解NDK的crash log

如果你用c++开发的android应用在运行过程中, c++代码发生错误导致程序崩溃, 系统就会记录 crash log到上述的系统日志中.

下面是我正在开发的游戏一次崩溃后, 截取的日志( 插个广告, 全名斗地主下载地址: http://sj.ddwan.com )

06-20 15:54:35.331 23889 23889 I DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
06-20 15:54:35.331 23889 23889 I DEBUG   : Build fingerprint: 'google/razorg/deb:4.4.2/KOT49H/937116:user/release-keys'
06-20 15:54:35.331 23889 23889 I DEBUG   : Revision: '0'
06-20 15:54:35.331 23889 23889 I DEBUG   : pid: 1981, tid: 2020, name: Thread-3399  >>> com.guangyou.ddgame <<<
06-20 15:54:35.331 23889 23889 I DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000028
06-20 15:54:35.431   187   710 D audio_hw_primary: out_set_parameters: enter: usecase(0: deep-buffer-playback) kvpairs: routing=2
06-20 15:54:35.511 23889 23889 I DEBUG   :     r0 76d94458  r1 00000000  r2 00000000  r3 00000000
06-20 15:54:35.511 23889 23889 I DEBUG   :     r4 760c1a48  r5 751e2440  r6 00000001  r7 760c1a48
06-20 15:54:35.511 23889 23889 I DEBUG   :     r8 00000001  r9 76c96f3c  sl 76c861c0  fp 76d94444
06-20 15:54:35.511 23889 23889 I DEBUG   :     ip 00000001  sp 76d94430  lr 75a81bd8  pc 75a81bdc  cpsr 600f0010
06-20 15:54:35.511 23889 23889 I DEBUG   :     d0  746968775f327865  d1  6a6e6169642f675f
06-20 15:54:35.511 23889 23889 I DEBUG   :     d2  5f6f616978757169  d3  676e702e6e776f6d
06-20 15:54:35.511 23889 23889 I DEBUG   :     d4  0000000009000000  d5  0000000000000000
06-20 15:54:35.511 23889 23889 I DEBUG   :     d6  0000000000000000  d7  0000000000000000
06-20 15:54:35.511 23889 23889 I DEBUG   :     d8  0000000000000000  d9  0000000000000000
06-20 15:54:35.511 23889 23889 I DEBUG   :     d10 0000000000000000  d11 0000000000000000
06-20 15:54:35.511 23889 23889 I DEBUG   :     d12 0000000000000000  d13 0000000000000000
06-20 15:54:35.511 23889 23889 I DEBUG   :     d14 0000000000000000  d15 0000000000000000
06-20 15:54:35.511 23889 23889 I DEBUG   :     d16 c3c3c3c3c3c3c3c3  d17 c3c3c3c3c3c3c3c3
06-20 15:54:35.511 23889 23889 I DEBUG   :     d18 41c7ddc227000000  d19 3ff0000000000000
06-20 15:54:35.511 23889 23889 I DEBUG   :     d20 3f811110896efbb2  d21 3fd7096611460fdb
06-20 15:54:35.511 23889 23889 I DEBUG   :     d22 c0176a8ee0000000  d23 bfc5230c760b0605
06-20 15:54:35.511 23889 23889 I DEBUG   :     d24 0000000000000000  d25 3fc7922925a107e2
06-20 15:54:35.511 23889 23889 I DEBUG   :     d26 3fdaa0f8fab43e33  d27 3fb43ad076b251ab
06-20 15:54:35.511 23889 23889 I DEBUG   :     d28 3fa15cb6bdc3c156  d29 3ec6cd878c3b46a7
06-20 15:54:35.511 23889 23889 I DEBUG   :     d30 3f65f3b6b9b97e01  d31 3ef99342e0ee5069
06-20 15:54:35.511 23889 23889 I DEBUG   :     scr 20000012
06-20 15:54:35.511 23889 23889 I DEBUG   :
06-20 15:54:35.511 23889 23889 I DEBUG   : backtrace:
06-20 15:54:35.511 23889 23889 I DEBUG   :     #00  pc 0089cbdc  /data/app-lib/com.guangyou.ddgame-1/libcocos2dcpp.so (cocos2d::Texture2D::getContentSize() const+32)
06-20 15:54:35.511 23889 23889 I DEBUG   :     #01  pc 0088f8dc  /data/app-lib/com.guangyou.ddgame-1/libcocos2dcpp.so (cocos2d::Sprite::setTexture(std::string const&)+128)
06-20 15:54:35.511 23889 23889 I DEBUG   :     #02  pc 007863dc  /data/app-lib/com.guangyou.ddgame-1/libcocos2dcpp.so (cocos2d::ui::Button::loadTextureDisabled(std::string const&, cocos2d::ui::Widget::TextureResType)+336)
06-20 15:54:35.511 23889 23889 I DEBUG   :
06-20 15:54:35.511 23889 23889 I DEBUG   : stack:
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d943f0  00000001
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d943f4  4006bc0d  /system/lib/libc.so (free+12)
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d943f8  76a72c54
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d943fc  75eca614  /data/app-lib/com.guangyou.ddgame-1/libcocos2dcpp.so
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94400  751c23c8  [anon:libc_malloc]
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94404  751c23c8  [anon:libc_malloc]
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94408  751c23c8  [anon:libc_malloc]
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d9440c  75a749b4  /data/app-lib/com.guangyou.ddgame-1/libcocos2dcpp.so (cocos2d::Sprite::setTexture(cocos2d::Texture2D*)+128)
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94410  0000003d
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94414  00e8efc8
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94418  00000000
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d9441c  00000000
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94420  00000000
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94424  76d94458  [stack:2020]
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94428  00000020
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d9442c  76d94444  [stack:2020]
06-20 15:54:35.511 23889 23889 I DEBUG   :     #00  76d94430  00000000
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94434  76d94458  [stack:2020]
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94438  76a66184
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d9443c  760c1a48  /data/app-lib/com.guangyou.ddgame-1/libcocos2dcpp.so
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94440  76d9447c  [stack:2020]
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94444  75a748e0  /data/app-lib/com.guangyou.ddgame-1/libcocos2dcpp.so (cocos2d::Sprite::setTexture(std::string const&)+132)
06-20 15:54:35.511 23889 23889 I DEBUG   :     #01  76d94448  76d944ec  [stack:2020]
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d9444c  793ff0e8  [anon:libc_malloc]
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94450  76a72c54
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94454  00000000
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94458  00000000
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d9445c  00000000
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94460  00000000
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94464  00000000
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d94468  00000000
06-20 15:54:35.511 23889 23889 I DEBUG   :          76d9446c  00000000
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d94470  7b91dcf8  [anon:libc_malloc]
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d94474  78ce6c50  [anon:libc_malloc]
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d94478  76d944b4  [stack:2020]
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d9447c  7596b3e0  /data/app-lib/com.guangyou.ddgame-1/libcocos2dcpp.so (cocos2d::ui::Button::loadTextureDisabled(std::string const&, cocos2d::ui::Widget::TextureResType)+340)
06-20 15:54:35.521 23889 23889 I DEBUG   :     #02  76d94480  00000001
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d94484  00000000
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d94488  76d944ec  [stack:2020]
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d9448c  793fe780  [anon:libc_malloc]
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d94490  76d944f0  [stack:2020]
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d94494  793ff0e8  [anon:libc_malloc]
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d94498  00000001
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d9449c  4006bc0d  /system/lib/libc.so (free+12)
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d944a0  76a72c54
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d944a4  75eca614  /data/app-lib/com.guangyou.ddgame-1/libcocos2dcpp.so
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d944a8  78ce6c50  [anon:libc_malloc]
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d944ac  78ce6c50  [anon:libc_malloc]
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d944b0  76d9455c  [stack:2020]
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d944b4  75924e54  /data/app-lib/com.guangyou.ddgame-1/libcocos2dcpp.so (cocostudio::ButtonReader::setPropsFromJsonDictionary(cocos2d::ui::Widget*, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> > const&)+752)
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d944b8  00000000
06-20 15:54:35.521 23889 23889 I DEBUG   :          76d944bc  78ce6c50  [anon:libc_malloc]
06-20 15:54:35.521 23889 23889 I DEBUG   :
06-20 15:54:35.521 23889 23889 I DEBUG   : memory near r0:
06-20 15:54:35.521 23889 23889 I DEBUG   :     76d94438 76a66184 760c1a48 76d9447c 75a748e0
06-20 15:54:35.521 23889 23889 I DEBUG   :     76d94448 76d944ec 793ff0e8 76a72c54 00000000
...
06-20 15:54:35.521 23889 23889 I DEBUG   :
06-20 15:54:35.521 23889 23889 I DEBUG   : memory near r4:
06-20 15:54:35.521 23889 23889 I DEBUG   :     760c1a28 760811c8 75ee318c 75ee3194 75ee319c
06-20 15:54:35.521 23889 23889 I DEBUG   :     760c1a38 4006d091 75f9a1f4 75f4ee5c 75e8ea0c
...

下面来逐行解读:
1. ndk crash log以*** *** *** *** ***开始.
2. 第一行Build fingerprint: 'google/razorg/deb:4.4.2/KOT49H/937116:user/release-keys' 指明了运行的Android版本, 如果您有多份crash dump的话这个信息就比较有用了.
3. 接着一行显示的是当前的线程id(pid)和进程id(tid). 如果当前崩溃的线程是主线程的话, pid和tid会是一样的~
4. 第四行, 显示的是unix信号. 这里的signal 11, 即SIGSEGV, 表示段错误, 是最常见的信号.(什么是unix信号, 什么是SIGSEGV)
5. 接下来的部分是系统寄存器的dump信息.

符号解释
rX(X=[0~9])代表整数寄存器
dX(X=[0~31])是浮点指针寄存器
fp (or r11)指向当前正在执行的函数的堆栈底.
ip (or r12)一个寄存器, 我也没弄明白是干啥的.
sp (or r13)当前正在执行的函数的堆栈顶.(跟fp相对应)
lr (or r14)link register. 简单来说, 当当前指令执行完了,
就会从这个寄存器获取地址, 来知道需要返回
到哪里继续执行.
pc (or r15)program counter. 存放下一条指令的地址
cpsrCurrent Program Status Register. 表示当前
运行环境和状态的一些字节位.

6. Crash dump还包含PC之前和之后的一些内存字段.
7. 最后, 是崩溃时的调用堆栈. 如果你执行的是debug版本, 还能还原一些c++代码.

利用ndk-stack定位崩溃代码

上面的一些信息能简单的帮你定位以下问题. 如果信息量还不够大的话, 那就还有最后一招: 还原历史.

Android NDK自从版本R6开始, 提供了一个工具ndk-stack( 在目录{ndk_root}/中 ). 这个工具能自动分析dump下来的crash log, 将崩溃时的调用内存地址和c++代码一行一行对应起来.

我们先看一下用法, 执行命令ndk-stack --help

Usage:
   ndk-stack -sym <path> [-dump <path>]

      -sym  Contains full path to the root directory for symbols.
      -dump Contains full path to the file containing the crash dump.
            This is an optional parameter. If ommited, ndk-stack will
            read input data from stdin
  • -dump参数很容易理解, 即dump下来的log文本文件. ndk-stack会分析此文件.
  • -sym参数就是你android项目下,编译成功之后,obj目录下的文件.

下面我们就来示范一下:

$ adb logcat | ndk-stack -sym ./obj/local/armeabi
********** Crash dump: **********
Build fingerprint: 'htc_wwe/htc_bravo/bravo:2.3.3/
GRI40/96875.1:user/release-keys'
pid: 1723, tid: 1743  >>> com.packtpub.droidblaster <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0000000c
Stack frame #00  pc 00010a2c  /data/data/com.packtpub.droidblaster/lib/libdroidblaster.so: Routine update in /home/packt/Project/Chapter11/DroidBlaster_Part11/jni/TimeService.cpp:25
Stack frame #01  pc 00009fcc  /data/data/com.packtpub.droidblaster/lib/libdroidblaster.so: Routine onStep in /home/packt/Project/Chapter11/DroidBlaster_Part11/jni/DroidBlaster.cpp:53
Stack frame #02  pc 0000a348  /data/data/com.packtpub.droidblaster/lib/libdroidblaster.so: Routine run in /home/packt/Project/Chapter11/DroidBlaster_Part11/jni/EventLoop.cpp:49
Stack frame #03  pc 0000f994  /data/data/com.packtpub.droidblaster/lib/libdroidblaster.so: Routine android_main in /home/packt/Project/Chapter11/DroidBlaster_Part11/jni/Main.cpp:31
...

熟悉的代码出现啦~~


Written with StackEdit.


>
<think>好的,用户之前询问了关于C++项目文件夹命名的建议,现在他们又转向询问关于C++日志文件的英文命名。我需要先理解用户的需求是什么。可能他们在开发一个C++项目,需要遵循一定的命名规范,确保代码的可维护性和团队协作的效率。 首先,用户提到“C++日志文件英文”,这里的“日志文件”可能指的是程序运行时生成的日志文件,需要合适的英文名称。用户可能希望这些名称既符合惯例,又清晰易懂,方便管理和查找。 接下来,回顾之前的对话,用户之前得到了关于文件夹命名的详细建议,结构化的命名方式,包括语言标识、功能描述和组件类型。现在可能需要类似的指导,但针对的是具体的日志文件命名,而不仅仅是文件夹。所以需要将之前的思路应用到文件命名上,同时考虑日志文件的特殊性。 需要考虑日志文件的常见命名规范,比如是否包含时间戳、日志级别、模块名称等。同时,用户可能希望这些文件名既符合技术规范,又具备可读性,便于后续的日志分析和故障排查。 另外,用户可能希望了解不同场景下的命名方式,比如开发、生产环境,或者按功能模块划分日志。需要分情况讨论,给出示例,帮助用户根据具体情况选择合适的命名方式。 还要注意避免常见的错误,比如过于简略的名称导致难以理解,或者包含特殊字符可能引起的问题。同时,可能需要提到版本控制和滚动日志的处理,比如使用序号或日期来区分不同的日志文件。 最后,要确保回答结构清晰,逐步引导用户,涵盖核心规范、实际案例、注意事项等,确保信息全面且易于理解。可能还需要补充一些工具或库的推荐,比如使用log4cpp等库时如何配置日志文件名,但用户的问题主要集中在命名本身,所以可能不需要深入工具的具体使用,除非用户有后续问题。 总结起来,用户需要的是符合C++项目惯例、清晰且易于理解的日志文件英文命名方案,需要兼顾可读性和技术规范,可能涉及时间戳、日志级别、模块划分、环境区分等方面。需要将这些要素结构化,提供示例和注意事项,帮助用户制定合理的命名策略。</think>以下是为C++项目设计的**日志文件英文命名规范**,结合技术规范与可读性要求: --- ### **核心命名公式** $$ \text{LogFileName} = \text{项目标识} + \text{功能模块} + \text{日志级别} + \text{时间标识} $$ --- ### **1. 基础规范** #### **组成要素** | 要素 | 格式要求 | 示例 | 作用 | |----------------|-------------------------|--------------------------|----------------------| | **项目标识** | 全小写,连字符分隔 | `myapp`,`game-engine` | 快速识别所属系统 | | **功能模块** | 小写+连字符缩略 | `network`,`db-conn` | 定位问题模块 | | **日志级别** | 标准缩写后缀 | `_debug`,`_error` | 区分日志严重程度 | | **时间标识** | ISO8601日期格式 | `20230815`,`2023Q3` | 支持按时间归档 | --- ### **2. 实际应用案例** #### **场景1:开发调试日志** - **全功能日志** `myapp_full_debug_20230815.log` - `myapp`: 项目名称 - `full`: 完整功能覆盖 - `debug`: 调试级别 - `20230815`: 日志生成日期 #### **场景2:生产环境错误日志** - **按模块分离** `game-engine_physics_error_2023Q3.log` - `physics`: 物理引擎模块 - `error`: 仅记录错误信息 #### **场景3:滚动日志文件** - **时间+序号组合** `payment-service_transaction_info_20230815_001.log` - `transaction`: 支付事务模块 - `info`: 常规运行信息 - `001`: 当日第1个日志文件 --- ### **3. 进阶技巧** #### **动态参数扩展** ```cpp // 代码示例:生成带时间戳的日志名 std::string generate_log_name(const std::string& module) { auto now = std::chrono::system_clock::now(); std::time_t time = std::chrono::system_clock::to_time_t(now); std::tm tm = *std::localtime(&time); char buffer[80]; strftime(buffer, sizeof(buffer), "%Y%m%d", &tm); return "myapp_" + module + "_debug_" + buffer + ".log"; } ``` #### **特殊场景处理** | 场景 | 命名方案 | 优势 | |--------------------|-----------------------------------|--------------------------| | **崩溃日志** | `myapp_crashdump_20230815.dmp` | 与常规日志区分 | | **性能分析日志** | `myapp_perf_benchmark.log` | 专用数据分析 | | **审计日志** | `myapp_audit_[userID].log` | 关联具体用户操作 | --- ### **4. 禁忌与最佳实践** - **避免的命名方式** ❌ `log1.txt` (无意义序号) ❌ `123_error.log` (缺失模块标识) - **推荐工具集成** - **日志库兼容命名**:使用`spdlog`/`g3log`时配置自动滚动命名 - **监控系统对接**:`prometheus_myapp_metrics.log` --- ### **5. 行业参考模式** - **Google日志规范** `chrome_gpu_20230815_warning.log` - 层级:`产品_模块_日期_级别` - **Unreal Engine日志** `ue5_rendering_2023Q3_perf.csv` - 扩展名表示日志格式(CSV便于分析) 通过这种命名体系,开发者可以快速实现: $$ \text{日志定位效率} = \frac{\text{关键信息密度}}{\text{搜索时间}} $$ 满足故障排查、审计追踪和性能优化的工程需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值