GDB 的简单使用

news2025/2/27 17:33:02

GDB 的简单使用

  • 一、启动调试
  • 二、常用调试命令
    • 1.list(显示程序源代码)
    • 2.break、tbreak、delete、disable、enable 和 info break(断点操作)
    • 3.run(运行源程序)
    • 4.next 和 step(单步调试)
    • 5.finish 和 return(函数返回)
    • 6.print、display 和 ptype(查看变量信息)
  • 附、调试代码


一、启动调试

直接使用gcc或g++命令编译生成的可执行文件无法直接用于调试,因为它们缺少必要的调试信息(代码行号、符号表等)。因此需要使用 -g 选项编译源文件,生成满足GDB要求的可调试的可执行文件,然后通过 gdb 可执行文件名 启动调试。

atreus@iZwz9fsfltolu74amg1v0rZ:~/Code$ g++ -g remove.cpp -o remove
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code$ gdb remove
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from remove...
(gdb) quit
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code$ 

二、常用调试命令

1.list(显示程序源代码)

list 命令(可以用 l 代替)用于显示程序源代码,默认只显示十行,此默认行数值可以通过 show listsize 查看,并通过 set listsize 一次显示的行数 修改。list 的主要用法如下:

  • list:显示当前行后面的代码,默认从头开始打印。
  • list -:显示当前行前面的代码。
  • list 文件名:行号:打印指定文件指定行号附近的代码。
  • list 文件名:函数名:打印指定文件指定函数起始处附近的代码。
  • list 文件名:起始位置,结束位置:打印从起始位置到结束位置的代码。

需要注意的是,文件名参数可以省略,且省略时GDB会按照函数名自动查找或优先处理当前文件。此外在存在函数重载函数重写时函数名并不唯一,此时可以指定函数的参数类型函数所属的类类型以区分不同函数,这两点也适用于其他GDB命令

(gdb) show listsize # 查看默认显示行数
Number of source lines gdb will list by default is 10.
(gdb) list main # 查看main函数附近的代码
1       #include "remove.h"
2
3       using namespace std;
4
5       int main() {
6           Solution solution;
7           vector<int> nums = {0, 1, 2, 2, 3, 0, 4, 2};
8
9           solution.removeElement(nums, 2);
10          for (auto num : nums) {
(gdb) list remove.h:6,23 # 查看remove.h文件中从第6行到第23行的代码
6       class Solution {
7       public:
8           /**
9            * 删除nums中值为val的元素并调整nums的大小
10           */
11          void removeElement(vector<int> &nums, int val) {
12              int slow = 0, fast = 0;
13
14              while (fast < nums.size()) {
15                  if (nums.at(fast) != val) {
16                      nums.at(slow++) = nums.at(fast);
17                  }
18                  fast++;
19              }
20
21              nums.resize(slow); // 根据慢指针位置调整大小
22          }
23      };
(gdb) list removeElement(std::vector<int, std::allocator<int>> &, int) # 指定函数的参数类型,可以解决函数重载
6       class Solution {
7       public:
8           /**
9            * 删除nums中值为val的元素并调整nums的大小
10           */
11          void removeElement(vector<int> &nums, int val) {
12              int slow = 0, fast = 0;
13
14              while (fast < nums.size()) {
15                  if (nums.at(fast) != val) {
(gdb) list Solution::removeElement # 指定函数的所属类,可以解决函数重写
6       class Solution {
7       public:
8           /**
9            * 删除nums中值为val的元素并调整nums的大小
10           */
11          void removeElement(vector<int> &nums, int val) {
12              int slow = 0, fast = 0;
13
14              while (fast < nums.size()) {
15                  if (nums.at(fast) != val) {
(gdb) list - # 向前查看代码
1       #include <iostream>
2       #include <vector>
3
4       using namespace std;
5
(gdb) 

2.break、tbreak、delete、disable、enable 和 info break(断点操作)

break 命令(可以用 b 代替)用于设置断点,它的主要用法如下:

  • break 文件名:行号:在指定文件指定行号设置断点。
  • break 文件名:函数名:在指定文件指定函数起始处设置断点。

tbreak 也用于设置断点,只不过它设置的断点为临时断点,所谓临时断点,就是指该断点触发一次后就会自动删除。

delete 命令(可以用 d 代替)用于删除断点,主要用法如下:

  • delete 断点编号:删除一个或多个指定编号的断点,指定多个时以空格分隔即可。
  • delete:不指定断点编号则会删除所有断点。

断点不但可以删除,也可以被禁用,所谓禁用就是使目标断点暂时失去作用,必要时可以再将其激活,恢复断点原有的功能。断点通过 disable 命令禁用,禁用后通过 enable 激活,二者用法与 delete 类似。

info break 命令用于查看当前程序中的断点信息。

(gdb) break remove.h:14 # 在remove.h的第14行处添加断点
Breakpoint 1 at 0x1558: file remove.h, line 14.
(gdb) break removeElement # 在removeElement函数起始处添加断点
Breakpoint 2 at 0x1532: file remove.h, line 11.
(gdb) break main # 在main函数起始处添加断点
Breakpoint 3 at 0x1309: file remove.cpp, line 5.
(gdb) info break # 查看当前所有的断点信息
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000001558 in Solution::removeElement(std::vector<int, std::allocator<int> >&, int) at remove.h:14
2       breakpoint     keep y   0x0000000000001532 in Solution::removeElement(std::vector<int, std::allocator<int> >&, int) at remove.h:11
3       breakpoint     keep y   0x0000000000001309 in main() at remove.cpp:5
(gdb) delete 3 # 删除编号为3的断点
(gdb) info break # 查看当前所有的断点信息
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000001558 in Solution::removeElement(std::vector<int, std::allocator<int> >&, int) at remove.h:14
2       breakpoint     keep y   0x0000000000001532 in Solution::removeElement(std::vector<int, std::allocator<int> >&, int) at remove.h:11
(gdb) tbreak main # 在main函数起始处添加临时断点
Temporary breakpoint 4 at 0x1309: file remove.cpp, line 5.
(gdb) info break # 查看当前所有的断点信息
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000001558 in Solution::removeElement(std::vector<int, std::allocator<int> >&, int) at remove.h:14
2       breakpoint     keep y   0x0000000000001532 in Solution::removeElement(std::vector<int, std::allocator<int> >&, int) at remove.h:11
4       breakpoint     del  y   0x0000000000001309 in main() at remove.cpp:5
(gdb) disable 4 # 禁用编号为4的断点
(gdb) info break # 查看当前所有的断点信息
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000001558 in Solution::removeElement(std::vector<int, std::allocator<int> >&, int) at remove.h:14
2       breakpoint     keep y   0x0000000000001532 in Solution::removeElement(std::vector<int, std::allocator<int> >&, int) at remove.h:11
4       breakpoint     del  n   0x0000000000001309 in main() at remove.cpp:5
(gdb) 

3.run(运行源程序)

run 命令(可以用 r 代替)用于启动源程序,默认会运行到第一个断点处,如果没有设置断点,会直接执行完可执行程序。

(gdb) tbreak main # 在main函数处设置断点
Temporary breakpoint 1 at 0x1309: file remove.cpp, line 5.
(gdb) break removeElement # 在removeElement函数处设置断点
Breakpoint 2 at 0x1532: file remove.h, line 11.
(gdb) info break
Num     Type           Disp Enb Address            What
1       breakpoint     del  y   0x0000000000001309 in main() at remove.cpp:5
2       breakpoint     keep y   0x0000000000001532 in Solution::removeElement(std::vector<int, std::allocator<int> >&, int) at remove.h:11
(gdb) run # 运行源程序
Starting program: /home/atreus/Code/remove 

Temporary breakpoint 1, main () at remove.cpp:5
5       int main() {
(gdb) info break # 查看当前所有的断点信息,可以看到main函数处的临时断点已经被删除
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x0000555555555532 in Solution::removeElement(std::vector<int, std::allocator<int> >&, int) at remove.h:11
(gdb) 

4.next 和 step(单步调试)

next 命令(可以用 n 代替)表示单步步过(stepping over)。

step 命令(可以用 s 代替)表示单步步入(stepping into),它会在遇到函数调用时进入到函数内部去执行。

(gdb) break main
Breakpoint 1 at 0x1309: file remove.cpp, line 5.
(gdb) list remove.cpp:1,15
1       #include "remove.h"
2
3       using namespace std;
4
5       int main() {
6           Solution solution;
7           vector<int> nums = {0, 1, 2, 2, 3, 0, 4, 2};
8
9           solution.removeElement(nums, 2);
10          for (auto num : nums) {
11              cout << num << endl;
12          }
13
14          return 0;
15      }
(gdb) run
Starting program: /home/atreus/Code/remove 

Breakpoint 1, main () at remove.cpp:5
5       int main() {
(gdb) next # 单步步过
7           vector<int> nums = {0, 1, 2, 2, 3, 0, 4, 2};
(gdb) 
9           solution.removeElement(nums, 2);
(gdb) step # 单步步入
Solution::removeElement (this=0x7fffffffe190, nums=std::vector of length -35184372086977, capacity -11728124003606 = {...}, val=0) at remove.h:11
11          void removeElement(vector<int> &nums, int val) {
(gdb) 

5.finish 和 return(函数返回)

finishreturn 均用于从当前函数中返回,二者主要区别如下:

  • finish 表示执行完当前函数并返回到函数调用处,即会一直执行到函数正常再退出。
  • return 表示直接结束当前函数执行并返回到函数调用处,还可以指定函数返回值,这也就意味着即使当前函数还有剩余代码,但也不会执行了。
(gdb) list 1,13
1       #include <stdio.h>
2
3       int func() {
4           int ret = 0;
5
6           return ret;
7       }
8
9       int main() {
10          printf("%d\n", func());
11
12          return 0;
13      }
(gdb) break 3
Breakpoint 1 at 0x1149: file main.c, line 3.
(gdb) run
Starting program: /home/atreus/Code/main 

Breakpoint 1, func () at main.c:3
3       int func() {
(gdb) next
4           int ret = 0;
(gdb) return -1
Make func return now? (y or n) y
#0  0x000055555555516f in main () at main.c:10
10          printf("%d\n", func());
(gdb) next
-1
12          return 0;
(gdb) 
(gdb) run
Starting program: /home/atreus/Code/main 

Breakpoint 1, func () at main.c:3
3       int func() {
(gdb) next
4           int ret = 0;
(gdb) finish
Run till exit from #0  func () at main.c:4
0x000055555555516f in main () at main.c:10
10          printf("%d\n", func());
Value returned is $1 = 0
(gdb) next
0
12          return 0;
(gdb) 

6.print、display 和 ptype(查看变量信息)

print(可以用 p 代替)用于在调试过程中查看变量的值或内存地址ptype 命令用于输出变量的类型

display 也可以查看监视的变量或者内存地址,而且会在每次程序中断下来时进行输出,通过 delete display 清除需要自动输出的变量。

(gdb) break remove.h:14
Breakpoint 1 at 0x1558: file remove.h, line 14.
(gdb) run
Starting program: /home/atreus/Code/remove 

Breakpoint 1, Solution::removeElement (this=0x7fffffffe183, nums=std::vector of length 8, capacity 8 = {...}, val=2) at remove.h:14
14              while (fast < nums.size()) {
(gdb) print this
$1 = (Solution * const) 0x7fffffffe183
(gdb) print fast
$2 = 0
(gdb) print &fast
$3 = (int *) 0x7fffffffe15c
(gdb) print nums.at(fast)
$4 = 0
(gdb) print nums.at(fast) == 0
$5 = true
(gdb) display slow
1: slow = 0
(gdb) display fast
2: fast = 0
(gdb) next
15                  if (nums.at(fast) != val) {
1: slow = 0
2: fast = 0
(gdb) 
16                      nums.at(slow++) = nums.at(fast);
1: slow = 0
2: fast = 0
(gdb) 
18                  fast++;
1: slow = 1
2: fast = 0
(gdb) delete display 1
(gdb) next
14              while (fast < nums.size()) {
2: fast = 1
(gdb) ptype fast
type = int
(gdb) ptype nums
type = class std::vector<int> [with _Tp = int, _Alloc = std::allocator<int>] : protected std::_Vector_base<_Tp, _Alloc> {
  private:
    static bool _S_nothrow_relocate(std::true_type);
    static bool _S_nothrow_relocate(std::false_type);
    static bool _S_use_relocate(void);
    static pointer _S_do_relocate(pointer, pointer, pointer, _Alloc &, std::true_type);
    static pointer _S_do_relocate(pointer, pointer, pointer, _Alloc &, std::false_type);
    static pointer _S_relocate(pointer, pointer, pointer, _Alloc &);
  public:
    vector(void);
    vector(const _Alloc &);
    vector(size_type, const _Alloc &);
    vector(size_type, reference, const _Alloc &);
    vector(const std::vector<int> &);
    vector(std::vector<int> &&);
    vector(const std::vector<int> &, const _Alloc &);
  private:
    vector(std::vector<int> &&, const _Alloc &, std::true_type);
    vector(std::vector<int> &&, const _Alloc &, std::false_type);
  public:
    vector(std::vector<int> &&, const _Alloc &);
    vector(std::initializer_list<_Tp>, const _Alloc &);
    ~vector();
    std::vector<int> & operator=(const std::vector<int> &);
    std::vector<int> & operator=(std::vector<int> &&);
    std::vector<int> & operator=(std::initializer_list<_Tp>);
    void assign(size_type, reference);
    void assign(std::initializer_list<_Tp>);
    iterator begin(void);
    const_iterator begin(void) const;
    iterator end(void);
    const_iterator end(void) const;
    reverse_iterator rbegin(void);
    const_reverse_iterator rbegin(void) const;
    reverse_iterator rend(void);
    const_reverse_iterator rend(void) const;
    const_iterator cbegin(void) const;
    const_iterator cend(void) const;
    const_reverse_iterator crbegin(void) const;
    const_reverse_iterator crend(void) const;
    size_type size(void) const;
    size_type max_size(void) const;
    void resize(size_type);
    void resize(size_type, reference);
    void shrink_to_fit(void);
    size_type capacity(void) const;
    bool empty(void) const;
    void reserve(size_type);
    reference operator[](size_type);
    reference operator[](size_type) const;
--Type <RET> for more, q to quit, c to continue without paging--q
Quit
(gdb) ptype nums.resize()
type = void
(gdb) 

附、调试代码

remove.h

#include <iostream>
#include <vector>

using namespace std;

class Solution {
public:
    /**
     * 删除nums中值为val的元素并调整nums的大小
     */
    void removeElement(vector<int> &nums, int val) {
        int slow = 0, fast = 0;

        while (fast < nums.size()) {
            if (nums.at(fast) != val) {
                nums.at(slow++) = nums.at(fast);
            }
            fast++;
        }

        nums.resize(slow); // 根据慢指针位置调整大小
    }
};

remove.cpp

#include "remove.h"

using namespace std;

int main() {
    Solution solution;
    vector<int> nums = {0, 1, 2, 2, 3, 0, 4, 2};

    solution.removeElement(nums, 2);
    for (auto num : nums) {
        cout << num << endl;
    }

    return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/520475.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大型语言模型 (LLM) 的两条发展路线:Finetune v.s. Prompt

前言 如果你对这篇文章感兴趣&#xff0c;可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」&#xff0c;查看完整博客分类与对应链接。 在大型语言模型的研究中&#xff0c;研究者对其有两种不同的期待&#xff0c;也可以理解为是两种不同的路线&#xff0c;具…

【LeetCode: 494. 目标和 | 暴力递归=>记忆化搜索=>动态规划 | 背包模型】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

【Spring框架三】——Spirng IOC和DI的实现

系列文章目录 【Spring框架一】——Spring框架简介 Spirng IOC和DI的实现 系列文章目录前言一、IDEA新建Spring 项目二、使用XML文件的方式实现IOC和DI1.创建XML文件2. 通过构造函数的方式进行注入运行结果3.setter方法进行注入 三、使用注解的方式实现IOC和DISpring 5中的常用…

Django SQL注入漏洞 CVE-2022-28346

漏洞简介 Django 在2022年发布的安全更新&#xff0c;修复了在 QuerySet 的 annotate()&#xff0c; aggregate()&#xff0c; extra() 等函数中存在的 SQL 注入漏洞。 影响版本 2.2< Django Django <2.2.283.2< Django Django <3.2.134.0< Django Django <4…

Hadoop 4:Hive

数据仓库概念 数据仓库&#xff08;英语&#xff1a;Data Warehouse&#xff0c;简称数仓、DW&#xff09;,是一个用于存储、分析、报告的数据系统。 数据仓库的目的是构建面向分析的集成化数据环境&#xff0c;分析结果为企业提供决策支持&#xff08;Decision Support&#x…

新版Mamba体验超快的软件安装

在一文掌握Conda软件安装&#xff1a;虚拟环境、软件通道、加速solving、跨服务器迁移中详细介绍的conda的基本使用和遇到问题的解决方式&#xff0c;也提到了mamba作为一个替代工具&#xff0c;可以很好的加速conda的solving environemnt过程。但有时也会遇到一个很尴尬的问题…

苹果笔要不要买?apple pencil的平替笔推荐

第一款ipad在10年前就已经出现了&#xff0c;它被用作平板电脑&#xff0c;功能非常强大。而现在&#xff0c;ipad的产品型号更新速度&#xff0c;也让ipad变得更加受欢迎。在这些设备中&#xff0c;ipad的辅助配件扮演着重要角色&#xff0c;比如今天我们要讲的这款电容笔&…

【LeetCode】《LeetCode 101》第八章:分治法

文章目录 8.1 算法解释8.2 表达式问题241. 为运算表达式设计优先级&#xff08;中等&#xff09;932. 漂亮数组&#xff08;中等&#xff09;312. 戳气球&#xff08;困难&#xff09; 8.1 算法解释 分治问题 通过「把原问题分为子问题&#xff0c;再将子问题进行合并处理」&a…

伙伴匹配推荐接口的优化策略【优先队列+多线程分批处理,java实现】

文章目录 接口背景接口问题说明优化策略用户匹配度计算接口改进与测试说明改进前改进一&#xff08;使用优先队列存储编辑距离较小的n个元素&#xff09;改进二&#xff08;使用优先队列存储编辑距离较小的n个元素数据分批查询、分批处理&#xff09;改进三&#xff08;使用优先…

算法修炼之练气篇——练气十九层

博主&#xff1a;命运之光 专栏&#xff1a;算法修炼之练气篇 前言&#xff1a;每天练习五道题&#xff0c;炼气篇大概会练习200道题左右&#xff0c;题目有C语言网上的题&#xff0c;也有洛谷上面的题&#xff0c;题目简单适合新手入门。&#xff08;代码都是命运之光自己写的…

互联网本来很简单,但为啥人们看的那么复杂

昨天有朋友问我互联网&#xff0c;说互联网怎么怎么创新。 我说你看到的都是像。佛说佛有十万身。这都是像&#xff0c;不是相。 &#xff08;1&#xff09; 500多年前&#xff0c;意大利美第奇家族为了给教皇运送全欧洲信民们的捐赠&#xff0c;所以建立了一张天网一张地网。天…

VMware 产品下载汇总 2023 持续更新中

本站 VMware 产品下载汇总&#xff1a;vSphere、NSX、Tanzu、Aria、Cloud… 请访问原文链接&#xff1a;https://sysin.org/blog/vmware/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 本站提供的 VMware 软件全部为 “试用版…

ENVI无缝镶嵌Seamless Mosaic工具镶嵌、拼接栅格遥感影像(所有图像需要含有地理信息)

本文介绍基于ENVI软件&#xff0c;利用“Seamless Mosaic”工具实现栅格遥感影像无缝镶嵌的操作。 在ENVI软件中通过“Pixel Based Mosaicking”工具实现栅格遥感影像的镶嵌的方法。这一工具虽然可以对不含有地理参考信息的遥感影像进行镶嵌&#xff0c;但是其镶嵌的整体效果并…

数据分析利器之python、IT应用架构规划详解(195页)、600多个人工智能AI工具汇总、营销革命5.0…| 本周精华...

▲点击上方卡片关注我&#xff0c;回复“8”&#xff0c;加入数据分析领地&#xff0c;一起学习数据分析&#xff0c;持续更新数据分析学习路径相关资料~&#xff08;精彩数据观点、学习资料、数据课程分享、读书会、分享会等你一起来乘风破浪~&#xff09;回复“小飞象”&…

Halcon 阈值分割(全局阈值threshold、binary_threshold、动态阈值 dyn_threshold)、直方图

文章目录 1 图像直方图2 全局阈值 threshold()3 binary_threshold()4 动态阈值mean_image() + dyn_threshold()5 代码和原图1 图像直方图 图像直方图(Image Histogram)是用以表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素个数。 这种直方图中,横坐标的左…

后端返回文件流时,前端如何处理并成功下载流文件以及解决下载后打开显示不支持此文件格式

一、文件和流的关系 文件&#xff08;File&#xff09;和流(Stream)是既有区别又有联系的两个概念。 文件 是计算机管理数据的基本单位&#xff0c;同时也是应用程序保存和读取数据的一个重要场所。 存储介质&#xff1a;文件是指在各种存储介质上&#xff08;如硬盘、可…

WPF 使用 MaterialDesignThemes 项目Demo

前言&#xff1a; 最近在学B站的WPF项目实战合集(2022终结版)&#xff0c;但是到22P时候发现UI框架 MaterialDesignThemes的Github上面的程序没办法正常运行&#xff0c;最后折腾了好久终于解决。 github地址 gitcode镜像地址 下载成功后 下载成功后是如下效果 打开这个文…

audioop.rms函数解读和代码例子

该audioop模块包含对声音片段的一些有用操作。它对由8,16或32位宽的有符号整数样本组成的声音片段进行操作&#xff0c;并以Python字符串存储。这与al和sunaudiodev模块使用的格式相同。所有标量项都是整数&#xff0c;除非另有规定。 audioop.rms 即 sqrt(sum(S_i^2)/n) 这个公…

Linux运维常用sed命令使用

sed 是一种流式文本编辑器&#xff0c;常用于文本替换、文本过滤、行选择等操作。 常见的 sed 使用方法 1、替换文本中的字符串 使用 sed 可以在文本中替换指定的字符串。例如&#xff0c;将文本中所有的 old_text 替换为 new_text&#xff0c;可以执行以下命令&#xff1a; …

面向国际市场:利用FaceBook实现外贸贸易突破

在全球化的商业环境下&#xff0c;利用社交媒体平台如FaceBook来推动外贸贸易已经成为许多企业的关注焦点。FaceBook作为全球最大的社交媒体平台之一&#xff0c;为企业提供了众多机会和工具&#xff0c;以扩大市场触达、建立品牌形象和跨文化沟通。 本文将介绍一些简单却有效…