当前位置: 首页 > article >正文

【go】数组与切片

数组Array

重点:

数组是值类型

数组的定义

var 数组名 [数组大小] 数据类型,例如var intArr [5] int,定义完数组后数组里的元素有默认值。、

数组的地址&intArr、&intArr[0]。

数组占据连续的内存。

int通常是4字节(32位),而在64位系统上,int通常是8字节(64位)。

注意点:

    1. 数组:是同一种数据类型的固定长度的序列。2. 数组定义:var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。3. 长度是数组类型的一部分,因此,var a[5] intvar a[10]int是不同的类型。4. 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1for i := 0; i < len(a); i++ {}for index, v := range a {}5. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic6. 数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。7.支持 "==""!=" 操作符,因为内存总是被初始化过的。8.指针数组 [n]*T,数组指针 *[n]T。

一维数组定义:

   全局:var arr0 [5]int = [5]int{1, 2, 3}var arr1 = [5]int{1, 2, 3, 4, 5}// 通过初始化值确定数组长度。var arr2 = [...]int{1, 2, 3, 4, 5, 6}var str = [5]string{3: "hello world", 4: "tom"}局部:a := [3]int{1, 2}           // 未初始化元素值为 0。b := [...]int{1, 2, 3, 4}   // 通过初始化值确定数组长度。c := [5]int{2: 100, 4: 200} // 使用索引号初始化元素。d := [...]struct {			// 结构体数组,前段是结构定义,后段是初始化name stringage  uint8}{{"user1", 10}, // 可省略元素类型。{"user2", 20}, // 别忘了最后一行的逗号。}
package mainimport ("fmt"
)var arr0 [5]int = [5]int{1, 2, 3}
var arr1 = [5]int{1, 2, 3, 4, 5}
var arr2 = [...]int{1, 2, 3, 4, 5, 6}
var str = [5]string{3: "hello world", 4: "tom"}func main(){a := [3]int{1, 2}           // 未初始化元素值为 0。b := [...]int{1, 2, 3, 4}   // 通过初始化值确定数组长度。c := [5]int{2: 100, 4: 200} // 使用引号初始化元素。d := [...]struct {name stringage  uint8}{{"user1", 10}, // 可省略元素类型。{"user2", 20},{"刘小琦",21}, // 别忘了最后一行的逗号。}fmt.Printf("arr2的长度为:%d,str的长度为:%d\n", len(arr2), len(str))fmt.Println(arr0, arr1, arr2, str)fmt.Printf("b的长度为:%d,d的长度为:%d\n", len(a), len(d))fmt.Println(a, b, c, d)
}

多维数组:

 全局var arr0 [5][3]intvar arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}局部:a := [2][3]int{{1, 2, 3}, {4, 5, 6}}b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "..."。第1维度可以用。
package mainimport("fmt"
)
// 全局var arr0 [5][3]int // 定义了就有默认值0var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
func main(){// 局部:a := [2][3]int{{1, 2, 3}, {4, 5, 6}}  // 初始化时就赋值b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "..."。// 打印数组fmt.Println("arr0:", arr0)fmt.Println("arr1:", arr1)fmt.Println("a:", a)fmt.Println("b:", b)
}

数组是值传递

package mainimport ("fmt"
)func test(x [2]int) {fmt.Printf("x: %p\n", &x)x[1] = 1000
}func main() {a := [2]int{}fmt.Printf("a: %p\n", &a)test(a)fmt.Println(a)
}

内置函数 len 和 cap 都返回数组长度 (元素数量)。

内置函数 lencap 在处理数组、切片和映射(map)时有所不同。

  • len 函数用于获取数组、切片、映射、字符串等的长度。对于数组和切片,len 返回的是元素的数量。对于映射,len 返回的是键值对的数量。
  • cap 函数用于获取数组、切片的容量。容量指的是底层数组可以容纳的元素数量。对于数组,其容量和长度是相同的,都是数组定义时的大小。而对于切片,容量是从切片的起始位置到底层数组的结束位置之间的元素数量,这可能比切片的长度要大。

简单来说,对于数组,lencap 返回的值是相同的。而对于切片,len 返回的是切片中实际包含的元素数量,而 cap 返回的是切片可以增长到的最大元素数量,直到需要分配新的底层数组。

数组指针

使用这种方式进行数组拷贝和传参,是可以改变原数组的

package mainimport "fmt"func printArr(arr *[5]int) {fmt.Println(arr)arr[0] = 10for i, v := range arr {fmt.Println(i, v)}
}func main() {var arr1 [5]intprintArr(&arr1)fmt.Println(arr1)arr2 := [...]int{2, 4, 6, 8, 10}printArr(&arr2)fmt.Println(arr2)
}

输出:但是我们注意我们传进去的是数组的地址,传参时使用&取地址符

&[0 0 0 0 0]
0 10
1 0
2 0
3 0
4 0
[10 0 0 0 0]
&[2 4 6 8 10]
0 10
1 4
2 6
3 8
4 10
[10 4 6 8 10]

练习:

生成十个随机数,求和,求平均值

package mainimport ("fmt""math/rand"
)func main() {var arr [10]int;num:=0for i := 0; i < 10; i++ {// 生成0到99的随机数arr[i]=rand.Intn(100)num+=arr[i]}fmt.Println(arr)fmt.Println("数组元素之和为:",num)fmt.Printf("数组元素平均值为:%.2f\n",float64(num)/10)
}

求两数之和是否存在

package mainimport("fmt"
)func Test(a [5]int,target int) (index1 int,index2 int){for i := 0; i < len(a); i++ {if a[i]>target{return -1,-1}else{for j := i+1; j < len(a); j++ {if a[j] == target-a[i] {return i,j}}}}return -1,-1
}func main() {b := [5]int{1, 3, 5, 8, 7}index1,index2 := Test(b,10)fmt.Println(index1,index2)
}

切片Slice

底层

在Go语言中,切片(slice)是对数组的一个动态、灵活的视图。切片本身并不存储数据,它只是封装了对底层数组的引用。一个切片的主要组成部分包括:

  1. 指针(Pointer):指向底层数组中切片开始位置的指针。
  2. 长度(Length):切片中元素的数量。
  3. 容量(Capacity):从切片开始位置到底层数组末尾可以容纳的元素数量。

注意点

需要说明,slice 并不是数组或数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。是引用数据类型。

    1. 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。2. 切片的长度可以改变,因此,切片是一个可变的数组。3. 切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。 4. cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。5. 切片的定义:var 变量名 []类型,比如 var str []string  var arr []int(长度没有确定的数组)6. 如果 slice == nil,那么 lencap 结果都等于 0

切片的定义

package mainimport "fmt"func main() {//1.声明切片var s1 []intif s1 == nil {fmt.Println("是空")} else {fmt.Println("不是空")}// 2.:=s2 := []int{}// 3.make()var s3 []int = make([]int, 0)fmt.Println(s1, s2, s3)// 4.初始化赋值var s4 []int = make([]int, 0, 0)fmt.Println(s4)s5 := []int{1, 2, 3}fmt.Println(s5)// 5.从数组切片arr := [5]int{1, 2, 3, 4, 5}var s6 []int// 前包后不包,左闭右开s6 = arr[1:4]fmt.Println(s6)
}

输出结果:

是空
[] [] []
[]
[1 2 3]
[2 3 4]

关于var s4 []int = make([]int, 0, 0)解释:

在Go语言中,切片是一个动态数组,它由三个部分组成:指向底层数组的指针、长度和容量。在你提供的代码中,var s4 []int = make([]int, 0, 0) 这一行创建了一个切片 s4,其长度和容量都是0。

  • 长度为0意味着切片中没有元素。
  • 容量为0意味着底层数组没有空间来存储元素,因此无法直接向这个切片追加元素而不需要重新分配内存。

当你打印切片时,Go会输出切片的长度和底层数组的内容。由于 s4 的长度为0,所以打印出来的切片内容看起来是空的,即 []

需要注意的是,虽然 s4 的长度和容量都是0,但它并不是 nil。一个 nil 切片在Go中表示它没有指向任何底层数组,而这里的 s4 是一个已经分配了内存(尽管长度和容量为0)的切片。这就是为什么在前面的代码中,s1 == nil 的判断为 true,而这里的判断为 false

如果你想要一个 nil 切片,可以这样声明:

var s4 []int

这样声明的切片 s4 将会是 nil,打印出来的结果也会是 [],但是它的内部状态与使用 make([]int, 0, 0) 创建的切片不同。

关于var s3 []int = make([]int, 0)解释:

这段代码 var s3 []int = make([]int, 0) 的意思是使用 Go 语言中的 make 函数来创建一个长度为 0 的整数切片 s3

具体来说:

  • make([]int, 0) 创建了一个切片,其类型为 []int(整数切片)。
  • 切片的长度(len)为 0,这意味着切片中不包含任何元素。
  • 由于没有指定容量(cap),切片的容量也会默认为 0。

因此,当你打印 s3 时,它会显示长度为 0,容量也为 0,内容为空。这与 s4 类似,只不过 s4 明确地指定了容量为 0。

切片初始化

左闭右开2:8,即[2,8)即2,3,4,5,6,7

var slice3 []int = arr[0:len(arr)] //var slice []int = arr[:],可以读到全部

package mainimport ("fmt"
)var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[2:8]
var slice1 []int = arr[0:6]        //可以简写为 var slice []int = arr[:end]
var slice2 []int = arr[5:10]       //可以简写为 var slice[]int = arr[start:]
var slice3 []int = arr[0:len(arr)] //var slice []int = arr[:],可以读到全部
var slice4 = arr[:len(arr)-1]      //去掉切片的最后一个元素
func main() {fmt.Printf("全局变量:arr %v\n", arr)fmt.Printf("全局变量:slice0 %v\n", slice0)fmt.Printf("全局变量:slice1 %v\n", slice1)fmt.Printf("全局变量:slice2 %v\n", slice2)fmt.Printf("全局变量:slice3 %v\n", slice3)fmt.Printf("全局变量:slice4 %v\n", slice4)fmt.Printf("-----------------------------------\n")arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}slice5 := arr[2:8]slice6 := arr[0:6]         //可以简写为 slice := arr[:end]slice7 := arr[5:10]        //可以简写为 slice := arr[start:]slice8 := arr[0:len(arr)]  //slice := arr[:]slice9 := arr[:len(arr)-1] //去掉切片的最后一个元素fmt.Printf("局部变量: arr2 %v\n", arr2)fmt.Printf("局部变量: slice5 %v\n", slice5)fmt.Printf("局部变量: slice6 %v\n", slice6)fmt.Printf("局部变量: slice7 %v\n", slice7)fmt.Printf("局部变量: slice8 %v\n", slice8)fmt.Printf("局部变量: slice9 %v\n", slice9)fmt.Printf("局部变量: slice10 %v\n", slice10)
}

输出结果:

全局变量:arr [0 1 2 3 4 5 6 7 8 9]
全局变量:slice0 [2 3 4 5 6 7]
全局变量:slice1 [0 1 2 3 4 5]
全局变量:slice2 [5 6 7 8 9]
全局变量:slice3 [0 1 2 3 4 5 6 7 8 9]
全局变量:slice4 [0 1 2 3 4 5 6 7 8]
-----------------------------------
局部变量: arr2 [9 8 7 6 5 4 3 2 1 0]
局部变量: slice5 [2 3 4 5 6 7]
局部变量: slice6 [0 1 2 3 4 5]
局部变量: slice7 [5 6 7 8 9]
局部变量: slice8 [0 1 2 3 4 5 6 7 8 9]
局部变量: slice9 [0 1 2 3 4 5 6 7 8]
局部变量: slice10 [0 1 2 3 4 5 6 7 8 9]

相关文章:

【go】数组与切片

数组Array 重点&#xff1a; 数组是值类型 数组的定义 var 数组名 [数组大小] 数据类型,例如var intArr [5] int&#xff0c;定义完数组后数组里的元素有默认值。、 数组的地址&intArr、&intArr[0]。 数组占据连续的内存。 int通常是4字节&#xff08;32位&…...

hadoop集群配置-scp命令

scp 命令用于在不同主机之间复制文件或目录&#xff0c;在Hadoop集群配置中常用于将配置文件或相关资源分发到各个节点。以下是 scp 命令的基本用法和在Hadoop集群配置中的示例&#xff1a; 基本语法 scp [-r] [源文件或目录] [目标用户目标主机:目标路径] - -r &#xff1a;…...

闪记(FlashNote):让灵感快速成文的轻量级笔记工具

闪记&#xff08;FlashNote&#xff09;&#xff1a;让灵感快速成文的轻量级笔记工具 你是否经常遇到这样的情况&#xff1a;桌面上放了一大堆的新建123.txt&#xff0c;想记录一个想法&#xff0c;应该是一键开个一个快捷键然后瞬间记录就自动保存了&#xff0c;现在的很多笔记…...

打车APP订单系统逻辑梳理与实现

一、逻辑分析 打车 APP 订单系统是整个打车业务的核心&#xff0c;负责处理从乘客下单到行程结束的一系列流程&#xff0c;涉及乘客、司机和平台三方的交互。 乘客端 下单&#xff1a;乘客打开 APP&#xff0c;输入上车地点、目的地&#xff0c;选择车型等信息后提交订单。此时…...

《大模型部署》——ollama下载及大模型本地部署(详细快速部署)

ollama Ollama 是一款开源跨平台的大语言模型&#xff08;LLM&#xff09;运行工具&#xff0c;旨在简化本地部署和管理 AI 模型的流程。 下载ollama 进入官网下载https://ollama.com/ 选择需要的系统下载 下载完成后直接进行安装 下载大模型 选择想要部署的模型&#…...

【蓝桥杯速成】| 17.完全背包(一维easy版)

题目一&#xff1a;爬楼梯 问题描述 57. 爬楼梯&#xff08;第八期模拟笔试&#xff09; 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬至多m (1 < m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 注意&#xff1a;给定 n 是一个正整…...

移动端六大语言速记:第4部分 - 数据结构

移动端六大语言速记&#xff1a;第4部分 - 数据结构 本文对比Java、Kotlin、Flutter(Dart)、Python、ArkTS和Swift这六种移动端开发语言的数据结构特性&#xff0c;帮助开发者快速掌握各语言的语法差异。 4. 数据结构 4.1 数组与列表 各语言数组与列表的语法对比&#xff1…...

开源鸿蒙分布式软总线技术研究报告

引言 在现代计算环境中&#xff0c;分布式系统的重要性日益凸显&#xff0c;尤其是在物联网&#xff08;IoT&#xff09;和无处不在的连接的背景下。各种智能设备数量的爆炸式增长以及用户对跨设备无缝体验的需求&#xff0c;推动了分布式操作系统的发展。开源鸿蒙正是在这样的…...

Geotools结合SLD实现矢量中文标注下的乱码和可用字体解析

目录 前言 一、需求溯源 1、原始的SLD渲染 2、最初的效果 二、问题修复 1、还是字符编码 2、如何选择可用的字体 3、如何查看支持的字体库 三、总结 前言 随着地理信息系统&#xff08;GIS&#xff09;技术的不断发展&#xff0c;矢量数据的可视化和标注成为了地理信息展…...

linux 服务器创建服务器启动后服务自启动

1、在/etc/systemd/system/下touch一个文件&#xff1a; touch /etc/systemd/system/your_application.service 2、在文件中写入&#xff1a; [Unit] Descriptionmodules-system Aftersyslog.target[Service] Typeforking Userroot Grouproot ExecStart/bin/bash /usr/loca…...

基于Python与CATIA V5的斐波那契螺旋线自动化建模技术解析

引言 斐波那契螺旋线&#xff08;Fibonacci Spiral&#xff09;作为自然界广泛存在的黄金比例曲线&#xff0c;在工业设计、产品造型、机械工程等领域具有重要应用价值。本文将以Python控制CATIA V5进行参数化建模为例&#xff0c;深入解析三维CAD环境中复杂数学曲线的自动化生…...

动态规划(11.按摩师)

题目链接&#xff1a;面试题 17.16. 按摩师 - 力扣&#xff08;LeetCode&#xff09; 解法&#xff1a; 状态表示&#xff1a; 对于简单的线性 dp &#xff0c;我们可以⽤「经验 题⽬要求」来定义状态表⽰&#xff1a; 以某个位置为结尾&#xff0c;巴拉巴拉&#xff1b;…...

CentOS下安装Docker,Docker下安装JDK\MYSQL\REDIS\NGINX

先用VM安装好Centos8.5&#xff0c;可以选择安装迷你版&#xff0c;我安装的是UI版。 然后用MobaXterm_Portable_v23.0_cn连上去&#xff0c;互访成功就可以往下操作。 1. 修改文件&#xff1a;就是要把之前的mirror替换成现在的vault cd /etc/yum.repos.d/sed -i s/mirrorl…...

demo.launch(inbrowser=True, share=True)无法生成共享网址

Gradio 的共享功能无法正常工作&#xff0c;原因是缺少一个名为 frpc_windows_amd64_v0.3 用到代码 app.demo.launch(show_errorTrue, inbrowserTrue, shareTrue) show_errorTrue&#xff1a;这个参数的作用是当应用在启动过程中出现错误时&#xff0c;会显示错误信息。这对于调…...

翻译: 人工智能如何让世界变得更美好二

Basic assumptions and framework 基本假设和框架 To make this whole essay more precise and grounded, it’s helpful to specify clearly what we mean by powerful AI (i.e. the threshold at which the 5-10 year clock starts counting), as well as laying out a fram…...

【vue】editor富文本输入全英文,谷歌浏览器:元素不会自动换行bug

【vue】editor富文本输入全英文&#xff0c;谷歌浏览器&#xff1a;元素不会自动换行bug 解决方案&#xff1a;给元素一个宽度 100% .editor {width: 100%; }...

XML标签格式转换为YOLO TXT格式

针对的是多边形&#xff08;<polygon>&#xff09;来描述对象的边界&#xff0c;而不是传统的矩形框&#xff08;<bndbox>&#xff09; import xml.etree.ElementTree as ET import os from pathlib import Path# 解析VOC格式的XML文件&#xff0c;提取目标框的标…...

# OpenCV实现人脸与微笑检测:从图像到视频的实战应用

OpenCV实现人脸与微笑检测&#xff1a;从图像到视频的实战应用 在计算机视觉领域&#xff0c;人脸检测和微笑检测是两个非常有趣且实用的任务。它们广泛应用于智能监控、社交媒体分析、人机交互等多个场景。本文将通过两个代码示例&#xff0c;详细介绍如何使用OpenCV实现人脸…...

【ubuntu24.04】挂载windows的共享文件夹

挂载windows的共享文件夹 ubutnu直接挂载windows共享文件夹&#xff0c;这样就能直接访问到windows里下载的文件了。 在 Ubuntu 中挂载 Windows 共享文件夹通常使用 CIFS 协议&#xff0c;下面给出一个常用的方法&#xff1a; 1. 安装 cifs-utils 首先&#xff0c;确保系统…...

基于Python的Django框架的个人博客管理系统

标题:基于Python的Django框架的个人博客管理系统 内容:1.摘要 本文围绕基于Python的Django框架构建个人博客管理系统展开。背景方面&#xff0c;随着互联网发展&#xff0c;个人博客成为信息分享与交流重要平台&#xff0c;传统博客管理系统在功能与灵活性上存在不足。目的是开…...

Kubernetes可视化面板——KubePi(Kubernetes Visualization Panel - kubepi)

Kubernetes可视化管理面板——KubePi 在云计算和容器化的大潮下&#xff0c;Kubernetes 已成为管理容器集群的事实标准。然而&#xff0c;面对复杂的集群管理和运维工作&#xff0c;一个直观、易用的可视化工具显得至关重要。KubePi 正是为此而生——一款专为简化 Kubernetes …...

【区块链安全 | 第二十三篇】单位和全局可用变量(一)

文章目录 单位和全局可用变量&#xff08;Units and Globally Available Variables&#xff09;以太单位&#xff08;Ether Units&#xff09;时间单位&#xff08;Time Units&#xff09;保留关键字 单位和全局可用变量&#xff08;Units and Globally Available Variables&am…...

6内存泄露问题的讨论

1.关注 内存泄露 是要融入到 DNA 中的事情 内存泄露是一个非常害怕, 非常严重的事情!! (不仅仅是内存泄露,包括文件描述符泄露等同类问题,都是非常严重的) 这种问题,不容易第一时间发现 2.实际场景中&#xff1a;特别是选择性关闭文件描述符 &#xff08;也是记得要关闭文件描…...

权重参数矩阵

目录 1. 权重参数矩阵的定义与作用 2. 权重矩阵的初始化与训练 3. 权重矩阵的解读与分析 (1) 可视化权重分布 (2) 统计指标分析 4. 权重矩阵的常见问题与优化 (1) 过拟合与欠拟合 (2) 梯度问题 (3) 权重对称性问题 5. 实际应用示例 案例1&#xff1a;全连接网络中的…...

Ludic:用Python构建HTML,告别JavaScript的繁琐开发

在现代Web开发中&#xff0c;构建动态网页和应用程序往往需要同时处理前端JavaScript和后端逻辑&#xff0c;这种复杂性让开发者倍感压力。Ludic框架的诞生&#xff0c;为开发者提供了一种全新的解决方案——通过Python的类型系统和组件化设计&#xff0c;让HTML生成变得简洁高…...

【现代深度学习技术】现代卷积神经网络06:残差网络(ResNet)

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上&#xff0c;结合当代大数据和大算力的发展而发展出来的。深度学习最重…...

SpringBoot分布式项目订单管理实战:Mybatis最佳实践全解

一、架构设计与技术选型 典型分布式订单系统架构&#xff1a; [网关层] → [订单服务] ←→ [分布式缓存]↑ ↓ [用户服务] [支付服务]↓ ↓ [MySQL集群] ← [分库分表中间件]技术栈组合&#xff1a; Spring Boot 3.xMybatis-Plus 3.5.xShardingSpher…...

《异常检测——从经典算法到深度学习》30. 在线服务系统中重复故障的可操作和可解释的故障定位

《异常检测——从经典算法到深度学习》 0 概论1 基于隔离森林的异常检测算法 2 基于LOF的异常检测算法3 基于One-Class SVM的异常检测算法4 基于高斯概率密度异常检测算法5 Opprentice——异常检测经典算法最终篇6 基于重构概率的 VAE 异常检测7 基于条件VAE异常检测8 Donut: …...

题解:蓝桥杯 2023 省 B 接龙数列 - dp + 哈希map

题解&#xff1a;蓝桥杯 2023 省 B 接龙数列 题目传送门 P9242 [蓝桥杯 2023 省 B] 接龙数列 一、题目描述 给定一个长度为N的整数数列&#xff0c;我们需要计算最少删除多少个数&#xff0c;可以使剩下的序列成为接龙序列。接龙序列的定义是&#xff1a;对于序列中相邻的两…...

RAG 优化:高效解析并接入图文、表格密集型文档

写在前面 检索增强生成 (Retrieval-Augmented Generation, RAG) 已成为构建智能问答、文档摘要、内容创作等应用的利器。然而,标准的 RAG 流程往往假设输入是纯文本。当我们面对现实世界中更常见的文档——那些充斥着大量图片、图表和表格的报告、手册、论文或网页时,传统的…...