pe管网站建设 中企动力,ui培训班学费多少钱,东山县城乡规划建设局网站,wordpress输出标签下文章1. 计算机中的值 在百万年的演化历史中#xff0c;人类对事物的属性进行了抽象#xff0c;有了数量、精度、信息等概念的表示#xff0c;对应的我们称之为整数、小数、文本文字等。计算机出现后#xff0c;我们使用计算机对真实世界的问题进行建模#xff0c;通过计算机的… 1. 计算机中的值 在百万年的演化历史中人类对事物的属性进行了抽象有了数量、精度、信息等概念的表示对应的我们称之为整数、小数、文本文字等。计算机出现后我们使用计算机对真实世界的问题进行建模通过计算机的高效计算解决这些问题并输出答案。为了建模计算机需要建立对上述基本概念的抽象和表示于是有了类型与值的概念。 计算机中所有数据都存储在内存中并参与问题解决的计算真实世界的概念表示与内存中的数据的转换关系如下图 图中的**有界比特序列(bounded bit sequence)就是真实世界概念表示在计算机内存中的存储形式我们可以统称它为一个值(value)**。这个值的比特序列形式由类型决定。举个例子一个公司的员工数量为1000人这个真实世界的概念在计算机中的表示过程如下 我们用uint16类型来表示员工数量这样它在内存存储形式为0000 0011 1110 1000。如果你用不同的类型来表示员工数量那么在内存中表示员工数量的值的比特序列将是不同的。 反之对于内存中的一段有界比特序列在不同类型guided的decode下得到的结果也是不同的如下图。 我们看到在uint64的guided下0000 0011 1110 1000这个比特序列被解释为1000而在[2]byte的guided下0000 0011 1110 1000这个同样的比特序列则被解释成了2个数字。 计算机中的值不仅仅可以表示一个数字也可以表示一个字符串甚至是像结构体这样的复合类型它本质上就是一块儿连续的内存内存单元是有地址的通过该地址访问和更新内存单元中的值。 但在编程过程中直接使用内存地址是十分不便的因此在高级编程语言[1]中编程语言通过具名的标识符与内存单元建立“绑定”关系就得到了我们通常说的常量和变量而内存单元中存储的数据(即值)也可说成是常量持有的数据和变量持有的数据。 当然也有一些不和任何标识符“绑定”的值我们称之为**字面值(literal value)**。我们通常用字面值为变量和常量赋[初]值 var a int 17
s : hello
const f float64 3.1415926 原生类型的字面值可以简单理解为汇编中的立即数而复杂类型(比如结构体)的字面值则一般是临时存储在栈上的有界比特序列。 2. 一切皆是值 根据上一节关于值的定义我们可以认为在Go语言中所有东西都是以值的形式存在的。在Go语言中不仅仅是基本类型如整数、浮点数、布尔值等就连复杂的数据结构如结构体、数组、切片、map、channel等都以值的形式存在。 到这里有小伙伴可能会问“不对啊map、channel等应该是指针吧”。别急要解答这个问题我们就要来看看值的分类。 2.1 值的分类 在Go中值可分为以下几种类型 基本类型值 基本类型是Go语言中最基础的数据类型它们是直接由语言定义的。基本类型的值通常是简单的值比如整数、浮点数、布尔值等。在Go语言中基本类型的值可以进行各种运算和比较操作。 复合类型值 复合类型则是由基本类型组成的更复杂的数据类型。它们的值由多个基本类型值组合而成并且可以使用结构化的方式进行访问和操作。在Go语言中复合类型包括分为数组、切片、map、结构体、接口、channel等多种类型。这些复合类型在不同的场景下都有不同的用途可以用于表示不同的数据结构或者实现不同的算法。 字符串在Go中是一个特殊的存在从Go类型角度来看它应该属于原生内置的基本类型但从值的角度考虑由于在运行时字符串类型表示为一个两字段的结构(如下) type StringHeader struct {Data uintptrLen int
} 因此我们将其归为复合类型值范畴。 指针类型值 有一类值十分特殊它自身是一个基本类型值更准确的说是一个整型值但这个整型值的含义却是另外一个值所在内存单元的地址。如下图所示 我们看到指针类型值为0x12345678这个值是另外一个内存块(值为0x17)的地址。指针类型值在Go语言以及C、C这一的静态语言中扮演着极其重要的角色。 回答前面小伙伴的问题map、channel是不是值? 是值只不过是指针类型值。从Go语法上来说map、channel是某个runtime指针类型的实例。 2.2 值的可变性 在继续深入指针之前我们先来插播一个内容值的可变性。 前面说过值是一段连续内存是一个有界比特序列。原理上来说内存中的值都是可变的。但现实中考虑到操作系统管理以及应用安全的需要暴露给开发人员的值被做了限定即有些值(内存单元中的数据)是可变的而有一些值是不可变的。 首先操作系统负责物理内存与虚拟内存的映射应用开发人员面对的是平坦的虚拟内存。这部分平坦的虚拟内存也被分为了几个段(segment)比如BSS段、数据段、代码段、堆栈等有些segment上的值是只读的不可变的比如代码段有些则是可读写的可变的比如堆栈。 此外Go在编程语言层面也对值做了限制常量值是不可变的字符串类型值是不可变的其他则为可变值。 2.3 指针类型 针对指针这类值编程语言抽象出了一种类型指针类型指针类型的变量与指针类型值绑定它内部存储的是另外一个内存单元的地址。这样就衍生出通过指针读取和更新指针指向的值的操作方法 var a int 5 // 基础类型值
var p a // p为指针类型变量(*int)其值为变量a的地址。println(*p) // 通过指针读取其指向的变量a的值
*p 15 // 通过指针更新其指向的变量a的值 不过指针更大的好处在于传递开销低且传递后接收指针的函数/方法体中依然可以修改指针指向的内存单元的值。 接下来我们来详细说一下值的传递。 2.4. 值的传递 无论是赋值还是传参Go语言中的所有值的传递的方法都是值拷贝也称为**逐位拷贝(bitwise copy)**。 不过即便是值拷贝也会带来三种不同效果 传值你是你我是我 效果传递前后的变量各自独立更新互不影响。 示例传整型、浮点型、布尔值等。 传指针你是你我是我但我们共同指向他 效果传递前后的指针变量拥有相同的指针值因此共同指向同一个内存对象(d)。通过其中一个指针变量对指向的内存对象进行更新后(e)另一个指针变量可以感知到相同的变化。 示例传*T指针类型变量。包括在Go runtime层面本质是一个指针的类型比如map、channel等。 传“引用”你是你我是我但我们有一部分共同指向他 首先要注意Go语言规范中没有“引用类型”这一表述。其次也不要将这里的“引用”与其他语言的“引用类型”相提并论。 这里传“引用”的效果是传递前后的变量一部分是独立更新互不影响的一部分则是有共同指向相互影响的。最典型的例子就是切片。当我们将切片传入函数后函数内对切片的更新操作会影响到原切片包括更新切片元素的值、向切片追加元素等。尤其是向切片追加(append)元素后会导致传递前后的两个切片出现“不一致”详情可以参考我之前写的一篇文章《当函数设计遇到切片》[2]。 这里之所以使用的“引用”来形容这种效果主要是像slice这样的类型与我们熟知的其他语言中的引用(reference)很像都是它们以“值”的形态传递但却能干着“指针”的活儿。 3. 关于值的一些tips 3.1 零值 在Go语言中每个变量都有一个默认的零值即在变量未被初始化时的默认值。这个默认值取决于变量的类型可以是一个数字、布尔值、字符串、指针、数组、结构体等等。 在Go语言中零值可以用来初始化变量的默认值也可以用来清空变量的值。 var i int // i的零值为0
var s string // s的零值为
var p *int // p的零值为nil
var a [3]int // a的零值为[0 0 0]
var b struct { x int; y float64 } // b的零值为{0 0.0} 在这个例子中我们使用var关键字声明了5个变量并使用它们的零值来初始化这些变量的值。 另外我们可以使用零值来清空变量的值例如 var i int 10 // 初始化i的值为10
i 0 // 使用i的零值来清空它的值 在使用零值时需要注意以下两个问题 指针类型的零值为nil不能直接使用nil指针来访问变量的值否则会导致panic。可声明零长度数组类型这样的类型的实例不占用内存空间这在一些特殊场合下会很有用。 3.2 值的比较 Go语言的值比较是通过比较两个值的二进制表示来实现的。在Go语言中值比较主要用于判断两个值是否相等。下面是Go语言值比较的场景、规则和注意事项 场景 判断两个值是否相等判断两个值是否不相等判断一个值是否为nil判断两个指针是否指向同一个对象。 规则 对于基本类型如int、float、bool等只需要比较它们的值就可以了对于复合类型如数组、切片、map等需要递归比较它们的元素或键值对对于结构体类型需要递归比较它们的字段对于接口类型需要判断它们是否指向同一个动态类型以及动态值是否相等。 注意事项 对于浮点数类型不能使用“”运算符进行比较因为浮点数的精度问题可能导致比较结果不正确应该使用math包中的函数进行比较对于切片类型Go不支持直接使用“”运算符进行比较因为它们的底层数据结构可能不同应该使用reflect包中的函数DeepEqual进行比较对于结构体类型如果其中包含不可比较的字段如切片、映射、函数等则整个结构体类型也是不可比较的对于指针类型需要注意空指针的情况应该先判断指针是否为nil再进行比较。 3.3 method receiver的值与指针类型的选择 在Go语言中method receiver可以是值类型或指针类型。这个选择可能会影响代码的性能、正确性和可读性等方面。 当一个方法的receiver是一个值类型时receiver的传递会出现“传值”效果方法体中对这个值的修改不会影响原来的值。但是如果这个值类型的对象非常大每次调用方法都需要进行复制这会导致一定的性能损失。 当一个方法的receiver是一个指针类型时这个方法操作的就是原来的对象并且可以修改原来的对象。这种方式可以避免复制对象的开销并且可以访问和修改对象的内部状态。但是如果多个goroutine同时访问同一个对象时就会发生竞争条件导致程序出现不可预料的行为。 在选择method receiver的类型时可考虑以下几个因素 对象的大小如果对象很小可以选择值类型的method receiver避免复制对象的开销如果对象很大可以选择指针类型的method receiver避免复制整个对象的开销。对象的可变性如果对象需要被修改应该选择指针类型的method receiver如果对象不需要被修改可以选择值类型的method receiver保证代码的可预测性和可读性。对象类型或对象的指针类型是否需要实现特定的接口。 注关于method receiver的类型选择问题在《Go语言第一课》专栏[3]的第25讲有系统的讲解。 3.4 使用unsafe.Pointer进行不同type guided的值decode 前面说过值是一个“有界比特序列”在不同类型guided的decode下得到的结果也是不同的。我们可以通过unsafe.Pointer来进行不同的decode比如下面例子将一个uint32的值重新分别decode为一个[2]uint16和[4]uint8数组 package mainimport (fmtunsafe
)func main() {var a uint32 0x12345678b : (*[2]uint16)(unsafe.Pointer(a))c : (*[4]uint8)(unsafe.Pointer(a))fmt.Println(*b) // [22136 4660]fmt.Println(*c) // [120 86 52 18]
} 4. 小结 本文对Go语言中值做了重新解读我们认为Go中的值就是一个**有界比特序列(bounded bit sequence)**是真实世界概念表示在计算机内存中的存储形式。 围绕着值这个概念我们指出Go中一切皆是值。在这一观点的基础上重新了解了值的分类、值的可变性、指针类型以及重要的值的传递学习了值的传递的本质bitwise-copy以及这个传递过程针对不同类型值所取得的不同效果。 最后我们了解了一些与值有关的tips包括零值、值比较、method receiver类型选择以及值decode。 “Gopher部落”知识星球[4]旨在打造一个精品Go学习和进阶社群高品质首发Go技术文章“三天”首发阅读权每年两期Go语言发展现状分析每天提前1小时阅读到新鲜的Gopher日报网课、技术专栏、图书内容前瞻六小时内必答保证等满足你关于Go语言生态的所有需求2023年Gopher部落将进一步聚焦于如何编写雅、地道、可读、可测试的Go代码关注代码质量并深入理解Go核心技术并继续加强与星友的互动。欢迎大家加入 著名云主机服务厂商DigitalOcean发布最新的主机计划入门级Droplet配置升级为1 core CPU、1G内存、25G高速SSD价格5$/月。有使用DigitalOcean需求的朋友可以打开这个链接地址[5]https://m.do.co/c/bff6eed92687 开启你的DO主机之路。 Gopher Daily(Gopher每日新闻)归档仓库 - https://github.com/bigwhite/gopherdaily 我的联系方式 微博(暂不可用)https://weibo.com/bigwhite20xx微博2https://weibo.com/u/6484441286博客tonybai.comgithub: https://github.com/bigwhite 商务合作方式撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。 参考资料 [1] 高级编程语言: https://en.wikipedia.org/wiki/High-level_programming_language [2] 《当函数设计遇到切片》: https://tonybai.com/2022/10/27/when-encountering-slice-during-function-design [3] 《Go语言第一课》专栏: http://gk.link/a/10AVZ [4] “Gopher部落”知识星球: https://wx.zsxq.com/dweb2/index/group/51284458844544 [5] 链接地址: https://m.do.co/c/bff6eed92687