【golang】库源码文件

库源码文件是不能被直接运行的源码文件,它仅用于存放程序实体,这些程序实体可以被其他代码使用(只要遵从Go语言规范的话)。

这里的“其他代码”可以与被使用的程序实体在同一个源码文件内,也可以在其他源码文件,甚至其他代码包中。

那么程序实体是什么呢?在 Go 语言中,程序实体是变量、常量、函数、结
构体和接口的统称。

我们总是会先声明(或者说定义)程序实体,然后再去使用。比如在上一篇
的例子中,我们先定义了变量name,然后在main函数中调用fmt.Printf函
数的时候用到了它。

再多说一点,程序实体的名字被统称为标识符。标识符可以是任何 Unicode
编码可以表示的字母字符、数字以及下划线“_”,但是其首字母不能是数字。

从规则上说,我们可以用中文作为变量的名字。但是,我觉得这种命名方式非常不好,自己也会在开发团队中明令禁止这种做法。作为一名合格的程序员,我们应该向着编写国际水准的程序无限逼近。

怎样把命令源码文件中的代码拆分到其他库源码文件?

如果在某个目录下有一个命令源码文件 demo4.go,如下:

package main
import (
"flag"
)
var name string
func init() {flag.StringVar(&name, "name", "everyone", "The greeting object.")
}
func main() {flag.Parse()hello(name)
}

其中的代码你应该比较眼熟了。我在讲命令源码文件的时候贴过很相似的代码,那个源码文件名为 demo2.go。

这两个文件的不同之处在于,demo2.go 直接通过调用fmt.Printf函数打印问候语,而当前的 demo4.go 在同样位置调用了一个叫作hello的函数。

函数hello被声明在了另外一个源码文件中,我把它命名为 demo4_lib.go,并且放在与demo4.go 相同的目录下。如下:

// 需在此处添加代码。[1]
import "fmt"
func hello(name string) {fmt.Printf("Hello, %s!\n", name)
}

那么问题来了:注释 1 处应该填入什么代码?

典型回答:

答案很简单,填入代码包声明语句package main。为什么?我之前说过,在同一个目录下的源码文件都需要被声明为属于同一个代码包。

如果该目录下有一个命令源码文件,那么为了让同在一个目录下的文件都通过编译,其他源码文件应该也声明属于main包

如此一来,我们就可以运行它们了。比如,我们可以在这些文件所在的目录下运行如下命令并得到相应的结果。

$ go run demo4.go demo4_lib.go
Hello, everyone!

或者,像下面这样先构建当前的代码包再运行。

$ go build puzzlers/article3/q1
$ ./q1 
Hello, everyone!

在这里,我把 demo4.godemo4_lib.go 都放在了一个相对路径为puzzlers/article3/q1的目录中。

在默认情况下,相应的代码包的导入路径会与此一致。我们可以通过代码包的导入路径引用其中声明的程序实体。但是,这里的情况是不同的。

注意,demo4.go 和 demo4_lib.go 都声明自己属于main包。我在前面讲 Go 语言源码的组织方式的时候提到过这种用法,即:源码文件声明的包名可以与其所在目录的名称不同,只要这些文件声明的包名一致就可以。

也就是说,正确的用法是,你需要把该项目的打包文件下载到本地的任意目录下,然后经解压缩后把“Golang_Puzzlers”目录加入到环境变量GOPATH中。还记得吗?这会使“Golang_Puzzlers”目录成为工作区之一。

这个问题考察的是代码包声明的基本规则。这里再总结一下。

第一条规则,同目录下的源码文件的代码包声明语句要一致。也就是说,它们要同属于一个代码包。这对于所有源码文件都是适用的。

如果目录中有命令源码文件,那么其他种类的源码文件也应该声明属于main包。这也是我们能够成功构建和运行它们的前提。

第二条规则,源码文件声明的代码包的名称可以与其所在的目录的名称不同。在针对代码包进行构建时,生成的结果文件的主名称与其父目录的名称一致。

对于命令源码文件而言,构建生成的可执行文件的主名称会与其父目录的名称相同,这在我前面的回答中也验证过了。

1.怎样把命令源码文件中的代码拆分到其他代码包?

我们先不用关注拆分代码的技巧。我在这里仍然依从前面的拆分方法。我把 demo4.go 另存为 demo5.go,并放到一个相对路径为puzzlers/article3/q2的目录中。

然后我再创建一个相对路径为puzzlers/article3/q2/lib的目录,再把demo4_lib.go 复制一份并改名为 demo5_lib.go 放到该目录中。

现在,为了让它们通过编译,我们应该怎样修改代码?你可以先思考一下。我在这里给出一部分答案,我们一起来看看已经过修改的 demo5_lib.go 文件。

package lib5
import "fmt"
func Hello(name string) {fmt.Printf("Hello, %s!\n", name)
}

可以看到,我在这里修改了两个地方。第一个改动是,我把代码包声明语句由package main改为了package lib5。注意,我故意让声明的包名与其所在的目录的名称不同。第二个改动是,我把全小写的函数名hello改为首字母大写的Hello。

2.代码包的导入路径总会与其所在目录的相对路径一致吗?

库源码文件 demo5_lib.go 所在目录的相对路径是puzzlers/article3/q2/lib,而它却声明自己属于lib5包。在这种情况下,该包的导入路径是puzzlers/article3/q2/lib,还是puzzlers/article3/q2/lib5?

这个问题往往会让 Go 语言的初学者们困惑,就算是用 Go 开发过程序的人也不一定清楚。
我们一起来看看。

首先,我们在构建或者安装这个代码包的时候,提供给go命令的路径应该是目录的相对路径,就像这样:

go install puzzlers/article3/q2/lib

该命令会成功完成。之后,当前工作区的 pkg 子目录下会产生相应的归档文件,具体的相对路径是:

pkg/darwin_amd64/puzzlers/article3/q2/lib.a

其中的darwin_amd64就是我在讲工作区时提到的平台相关目录。可以看到,这里与源码文件所在目录的相对路径是对应的。

为了进一步说明问题,我需要先对 demo5.go 做两个改动。第一个改动是,在以import为前导的代码包导入语句中加入puzzlers/article3/q2/lib,也就是试图导入这个代码包。

第二个改动是,把对hello函数的调用改为对lib.Hello函数的调用。其中的lib.叫做限定符,旨在指明右边的程序实体所在的代码包。不过这里与代码包导入路径的完整写法不同,只包含了路径中的最后一级lib,这与代码包声明语句中的规则一致。

现在,我们可以通过运行go run demo5.go命令试一试。错误提示会类似于下面这种。

./demo5.go:5:2: imported and not used: "puzzlers/article3/q2/lib" as lib5
./demo5.go:16:2: undefined: lib

第一个错误提示的意思是,我们导入了puzzlers/article3/q2/lib包,但没有实际使用其中的任何程序实体。这在 Go 语言中是不被允许的,在编译时就会导致失败。

注意,这里还有另外一个线索,那就是“as lib5”。这说明虽然导入了代码包puzzlers/article3/q2/lib,但是使用其中的程序实体的时候应该以lib5.为限定符。这也就是第二个错误提示的原因了。Go 命令找不到lib.这个限定符对应的代码包。

为什么会是这样?根本原因就是,我们在源码文件中声明所属的代码包与其所在目录的名称不同。请记住,源码文件所在的目录相对于 src 目录的相对路径就是它的代码包导入路径,而实际使用其程序实体时给定的限定符要与它声明所属的代码包名称对应。

有两个方式可以使上述构建成功完成。我在这里选择把 demo5_lib.go 文件中的代码包声明语句改为package lib。理由是,为了不让该代码包的使用者产生困惑,我们总是应该让声明的包名与其父目录的名称一致。

3.什么样的程序实体才可以被当前包外的代码引用?

你可能会有疑问,我为什么要把 demo5_lib.go 文件中的那个函数名称hello的首字母大写?实际上这涉及了 Go 语言中对于程序实体访问权限的规则。

超级简单,名称的首字母为大写的程序实体才可以被当前包外的代码引用,否则它就只能被当前包内的其他代码引用。

通过名称,Go 语言自然地把程序实体的访问权限划分为了包级私有的和公开的。对于包级私有的程序实体,即使你导入了它所在的代码包也无法引用到它。

4.对于程序实体,还有其他的访问权限规则吗?

答案是肯定的。在 Go 1.5 及后续版本中,我们可以通过创建internal代码包让一些程序实体仅仅能被当前模块中的其他代码引用。这被称为 Go 程序实体的第三种访问权限:模块级私有。

具体规则是,internal代码包中声明的公开程序实体仅能被该代码包的直接父包及其子包中的代码引用。当然,引用前需要先导入这个internal包。对于其他代码包,导入该internal包都是非法的,无法通过编译。

“Golang_Puzzlers”项目的puzzlers/article3/q4包中有一个简单的示例,可供你查看。你可以改动其中的代码并体会internal包的作用。

文章来自郝林老师的《Go语言36讲》

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

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

相关文章

Android JNI3--JNI基础

1,C预处理器 C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器(C Preproc…

数据通信——VRRP

引言 之前把实验做了,结果发现我好像没有写过VRRP的文章,连笔记都没记过。可能是因为对STP的记忆,导致现在都没忘太多。 一,什么是VRRP VRRP全名是虚拟路由冗余协议,虚拟路由,看名字就知道这是运行在三层接…

Linux 基础(三)常用命令-文件目录

常用命令 man 和 --help文件(夹)文件补充命令 man 和 --help 查看命令帮助信息 man 命令命令 --helphelp 命令(适用于绑定在shell里面的一些基础命令) 文件(夹) mkdir -p dirrmdir -p dirrm -rf …cp […

Solr的入门使用

Solr是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展,并对索引、搜索性能进行了优化,被很多需要搜索的网站中广泛使用。…

人员数量统计和跟踪的实现原理详细介绍,并提供完整实现代码

人数统计和跟踪项目的方法 目标是构建一个具有以下功能的系统。 读取视频中的帧。在输入框架上绘制所需的参考线。使用对象检测模型检测人员。标记检测到的人的质心。跟踪该标记质心的运动。计算质心移动的方向(向上还是向下移动)。计算进入或离开参考线的人数。根据计数,递…

C语言三子棋小游戏--数组的应用

注:在最后面,完整源码会以两种形式展现。在讲解时,以三个源文件的形式。 前言:三子棋,顾名思义,就是三个子连在一起就可以胜出。在本节我们要介绍的三子棋模式是这样子的:在键盘输入坐标&#x…

HTML详解连载(3)

HTML详解连载(3) 专栏链接 [link](http://t.csdn.cn/xF0H3)下面进行专栏介绍 开始喽表单作用使用场景 input标签基本使用示例type属性值以及说明 input标签占位文本示例注意 单选框 radio代码示例 多选框-checkbox注意代码示例 文本域作用标签&#xff1…

【前端 | CSS】flex布局

基本概念 Flexible模型,通常被称为 flexbox,是一种一维的布局模型。它给 flexbox 的子元素之间提供了强大的空间分布和对齐能力 我们说 flexbox 是一种一维的布局,是因为一个 flexbox 一次只能处理一个维度上的元素布局,一行或者…

R语言4_安装BayesSpace

环境Ubuntu22/20, R4.1 你可能会报错说你的R语言版本没有这个库,但其实不然。这是一个在Bioconductor上的库。 同时我也碰到了这个问题,ERROR: configuration failed for package systemfonts’等诸多类似问题,下面的方法可以一并解决。 第…

Mr. Cappuccino的第61杯咖啡——Spring之BeanPostProcessor

Spring之BeanPostProcessor 概述基本使用项目结构项目代码运行结果源代码 常用处理器项目结构项目代码执行结果 概述 BeanPostProcessor:Bean对象的后置处理器,负责对已创建好的bean对象进行加工处理; BeanPostProcessor中的两个核心方法&am…

[内网渗透]CFS三层靶机渗透

文章目录 [内网渗透]CFS三层靶机渗透网络拓扑图靶机搭建Target10x01.nmap主机探活0x02.端口扫描0x03.ThinkPHP5 RCE漏洞拿shell0x04.上传msf后门(reverse_tcp)反向连接拿主机权限 内网渗透Target2(1)路由信息探测(2)msf代理配置&a…

LeetCode150道面试经典题--找出字符串中第一个匹配项的下标(简单)

1.题目 给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。 2.示例 3.思路 回溯算法:首先将…

LeetCode[1122]数组的相对排序

难度:Easy 题目: 给你两个数组,arr1 和 arr2,arr2 中的元素各不相同,arr2 中的每个元素都出现在 arr1 中。 对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现…

【mysql】MySQL CUP过高如何排查?

文章目录 一. 问题锁定二. QPS激增会导致CPU飘高三. 慢SQL会导致CPU飘高四. 大量空闲连接会导致CPU飘高五. MySQL问题排查常用命令 一. 问题锁定 通过top命令查看服务器CPU资源使用情况,明确CPU占用率较高的是否是mysqld进程,如果是则可以明确CUP飘高的原…

[ubuntu]创建root权限的用户 该用户登录后自动切换为root用户

一、创建新用户 1、创建新用户 sudo useradd -r -m -s /bin/bash 用户名 # -r:建立系统账号 -m:自动建立用户的登入目录 -s:指定用户登入后所使用的shell2、手动为用户设置密码 passwd 用户名 二、为用户增加root权限 1、添加写权限 ch…

【redis基础】

目录 一、概述 1.NoSQL 1.1 简述 1.2 类型 1.3 应用场景 1.3.1 缓存 1.3.2 分布式锁 1.3.3 计数器 1.3.4 会话管理 1.3.5 消息队列 2.Redis 2.1 简述 2.2 特性 2.3 监听端口号 2.4 数据类型 二、安装 1.编译安装 2.RPM安装 三、目录结构 1.查看 2.主配置文…

[保研/考研机试] KY163 素数判定 哈尔滨工业大学复试上机题 C++实现

题目链接: 素数判定https://www.nowcoder.com/share/jump/437195121691718831561 描述 给定一个数n,要求判断其是否为素数(0,1,负数都是非素数)。 输入描述: 测试数据有多组,每组输入一个数…

Nginx转发的原理和负载均衡

一、Nginx转发的原理 Nginx是一个高性能的反向代理服务器,它可以用于实现请求的转发和负载均衡。以下是Nginx转发的基本原理: 客户端发送请求:客户端向Nginx服务器发送HTTP请求。 Nginx接收请求:Nginx服务器接收到客户端的请求。…

[Docker实现测试部署CI/CD----构建成功后钉钉告警(7)]

目录 15、钉钉告警创建项目群,然后添加机器人添加机器人Jenkins 系统配置项目配置修改Jenkinsfile文件,添加钉钉提示信息测试 不修改Jenkinsfile文件,添加钉钉提示信息测试 15、钉钉告警 创建项目群,然后添加机器人 首先需要在钉…

搭建 Python 环境 | Python、PyCharm

计算机 计算机能完成的工作: 算术运算逻辑判断数据存储网络通信…更多的更复杂的任务 以下这些都可以称为 “计算机”: 一台计算机主要由以下这几个重要的组件构成 CPU 中央处理器:大脑,算术运算,逻辑判断 存储器&…