经常做飞机网站,rp网站自动跳转图片怎么做,上传完wordpress程序不知道后台,建设彩票网站犯法吗变量
在Go语言中#xff0c;变量的定义和初始化是编程的基础部分。Go提供了多种方式来声明和初始化变量#xff0c;以适应不同的使用场景。
基本变量声明 使用var关键字#xff1a; 使用var关键字可以在函数内部或外部声明变量。如果在函数外部声明#xff0c;该变量为全…变量
在Go语言中变量的定义和初始化是编程的基础部分。Go提供了多种方式来声明和初始化变量以适应不同的使用场景。
基本变量声明 使用var关键字 使用var关键字可以在函数内部或外部声明变量。如果在函数外部声明该变量为全局变量。 var str1 string 变量1
var int1 int 123
fmt.Println(str1, int1)如果已经明确了初始值则可以省略类型信息让编译器自动推断类型 var str2 变量2
var int2 123
fmt.Println(str2, int2)短变量声明 在函数内部你可以使用更简洁的短变量声明Short variable declarations方式 :。这种方式同时进行了声明和初始化并且类型由右侧表达式推断得出。 func main() {str3 : 变量3int3 : 123fmt.Println(str3, int3)
}多重赋值
Go支持多个变量同时赋值 //var a, b, c 1, 2, 3.55a, b, c : 1, 2, 3.55// 使用 reflect 包中的 .TypeOf 函数。返回变量的类型信息fmt.Println(a的类型为, reflect.TypeOf(a))fmt.Println(b的类型为, reflect.TypeOf(b))fmt.Println(c的类型为, reflect.TypeOf(c))// 为相同类型的变量在同一行上声明和赋值//var a, b, c int 1, 2, 3 // 错误不能将字符串赋值给整数变量。 Go 不支持在同一行中为不同类型变量分配类型。var a, b, c int 1, 2, 3fmt.Println(a的类型为, reflect.TypeOf(a))fmt.Println(b的类型为, reflect.TypeOf(b))fmt.Println(c的类型为, reflect.TypeOf(c))块声明block declaration语法。这种方式允许在一个 var 语句中声明多个变量并为它们赋初始值。 var (a 1111b bbbc bool)fmt.Println(a, b, c)零值Zero Values
在Go中如果你只是声明一个变量而没有给它赋初值则会自动被赋予该类型的零值
数字类型整型、浮点型等的零值是 0布尔型的零值是 false字符串的零值是空字符串 指针、切片、映射、通道、函数以及接口的零值都是 nil
例如 var a stringvar b intvar c float64var d boolfmt.Printf(a的值【%v】\n, a)fmt.Printf(b的值【%v】\n, b)fmt.Printf(c的值【%v】\n, c)fmt.Printf(d的值【%v】\n, d)/*输出结果为a的值【】b的值【0】c的值【0】d的值【false】*/作用域
在Go语言中作用域Scope指的是一个变量或者函数名字在程序中可以被访问的区域。 全局作用域 如果一个变量是在所有函数之外定义的则它被认为是全局变量可以在整个包内部任何位置使用。如果需要在其他包中使用这些全局变量或函数则它们必须首字母大写。 test1.go package mainimport fmtvar name 全局变量func main() {// 在同一包下的不同文件中可以访问并且修改全局变量printGlobalVar()fmt.Println(name)
}test2.go package mainimport fmtfunc printGlobalVar() {fmt.Println(name)name 修改后的全局变量
}包级别作用域 在同一个包内部当变量或者常数被定义在所有函数之外时但不一定非得在文件最顶端这些标识符将对整个包内部可见。这意味着同一包下不同文件中的代码也可以访问这些变量。全局作用域的变量首字母小写其实就相当于包级别作用域的变量 文件级别作用域 这个概念在 Go 语言中并不存在。Go 语言中最小的作用域单位是包级别作用域。 局部作用域 在函数内定义的变量只能在该函数内部访问并且这种限制也适应于该函数内定义的其他嵌套块结构如if条件、循环等。 func printLocalVar() {var localVar 局部变量fmt.Println(localVar)
}块级别作用域 Go 语言支持诸如 if、for、switch 等控制结构并且每个结构都会引入新的块范围。例如在 if 语句中声明并初始化一个新变量在该 if 块及其子块中该变量是可见和可访问的但超出此范围后就不再可见。 func printBlockVar() {var localVar 局部变量// 代码块{var blockVar 块级变量fmt.Println(blockVar)fmt.Println(localVar) // 可以访问局部变量}//fmt.Println(blockVar) // 报错无法访问块级变量
}赋值过程
赋值过程及内存相关的知识通过以下代码做分析 name : 这是一段字符串nickname : name声明并初始化 name name 被声明为一个新的局部变量并且类型被推断为字符串string。它指向一个内存位置该位置存储着字符串 这是一段字符串。 声明并初始化 nickname 接下来另一个新变量 nickname 被声明并使用短变量声明方式同时进行初始化。在这个赋值操作中由于字符串在Go中是不可变的并且赋值操作只是复制了引用而非数据本身因此 nickname 也会指向同一块内存地址即指向 这是一段字符串 的那块内存。注意name 和 nickname 都指向同一个字符串字面值 这是一段字符串 的实际数据。这意味着他们共享相同的底层字节数组。然而每个字符串变量如 name 和 nickname本身也是一个独立的实体拥有自己的内存地址。 内存共享 重要的是理解在这种情况下没有新的字符串数据被创建。两个变量 name 和 nickname 实际上共享相同的底层数据。这意味着对于只读数据如字符串来说非常高效。 垃圾回收考虑 在Go语言中当没有任何引用指向某块内存时即不再有任何方式访问该内存这块内存就可能被标记为可回收。只要任何一个变量仍然引用着那块内存即使其中一个已经超出了作用域那么该内存就不会被垃圾回收器回收。只有当所有引用name和nickname都消失时才会释放相关资源。
结合代码说明
package mainimport (fmtunsafe // 代码使用 unsafe 包将变量的地址转换为 stringHeader 类型的指针以便访问字符串的底层数据指针。
)func main() {// 情况1 变量地址不同指向的值地址相同name : 这是一段字符串nickname : namefmt.Println(打印变量的地址信息)fmt.Printf(变量name 地址【%p】\n, name)fmt.Printf(变量nickname 地址【%p】\n, nickname)// 使用unsafe包来获取字符串底层数据指针nameDataPointer1 : (*stringHeader)(unsafe.Pointer(name)).DatanicknameDataPointer1 : (*stringHeader)(unsafe.Pointer(nickname)).Datafmt.Printf(name数据的指向地址: %x\n, nameDataPointer1)fmt.Printf(nickname数据的指向地址: %x\n\n, nicknameDataPointer1)// 情况2 变量地址不同指向的值地址也相同因为字符串是不可变的nickname 这是一段字符串fmt.Println(打印变量的地址信息)fmt.Printf(变量name 地址【%p】\n, name)fmt.Printf(变量nickname 地址【%p】\n, nickname)// 使用unsafe包来获取字符串底层数据指针nameDataPointer2 : (*stringHeader)(unsafe.Pointer(name)).DatanicknameDataPointer2 : (*stringHeader)(unsafe.Pointer(nickname)).Datafmt.Printf(name数据的指向地址: %x\n, nameDataPointer2)fmt.Printf(nickname数据的指向地址: %x\n\n, nicknameDataPointer2)// 情况3 变量地址不同指向的值地址不同nickname 这是一段字符串fmt.Println(打印变量的地址信息)fmt.Printf(变量name 地址【%p】\n, name)fmt.Printf(变量nickname 地址【%p】\n, nickname)// 使用unsafe包来获取字符串底层数据指针nameDataPointer3 : (*stringHeader)(unsafe.Pointer(name)).DatanicknameDataPointer3 : (*stringHeader)(unsafe.Pointer(nickname)).Datafmt.Printf(name数据的指向地址: %x\n, nameDataPointer3)fmt.Printf(nickname数据的指向地址: %x\n\n, nicknameDataPointer3)}/*
stringHeader用于获取字符串的底层数据指针这个结构体是 Go 语言中字符串的内部表示。
Data 是一个 uintptr 类型的字段它用来存储指向字符串数据的指针
Len 存储字符串的长度。
*/
type stringHeader struct {Data uintptr // 指向实际数据的指针Len int // 字符串的长度
}
打印结果
打印变量的地址信息
变量name 地址【0xc000014070】
变量nickname 地址【0xc000014080】
name数据的指向地址: 2f2a8fd
nickname数据的指向地址: 2f2a8fd打印变量的地址信息
变量name 地址【0xc000014070】
变量nickname 地址【0xc000014080】
name数据的指向地址: 2f2a8fd
nickname数据的指向地址: 2f2a8fd打印变量的地址信息
变量name 地址【0xc000014070】
变量nickname 地址【0xc000014080】
name数据的指向地址: 2f2a8fd
nickname数据的指向地址: 2f2b3eb
这段 Go 语言代码演示了字符串在不同情况下的内存地址和底层数据指针的变化。代码分为三个部分每部分都打印了变量的内存地址和它们指向的字符串数据的地址。这里使用了 unsafe 包来操作指针和内存这是 Go 语言中一个特殊的包允许程序绕过类型安全性的限制。 情况1创建了两个变量 name 和 nickname它们被赋予了同一个字符串字面量。由于字符串是不可变的nickname 实际上是对 name 所指向的字符串的一个别名。因此尽管 name 和 nickname 作为变量有不同的内存地址它们指向的字符串数据的地址是相同的。 情况2nickname 被重新赋值为与 name 相同的字符串字面量。由于字符串是不可变的这个新的字符串字面量可能会指向与 name 相同的内存地址或者可能会创建一个新的字符串实例这取决于 Go 运行时的优化。在这个例子中由于字符串内容相同nickname 可能指向了与 name 相同的内存地址。 情况3nickname 被重新赋值为一个不同的字符串字面量这次字符串内容与 name 不同。由于字符串是不可变的这个新的字符串字面量将创建一个新的内存地址来存储这个新的字符串内容。因此nickname 将指向一个新的内存地址与 name 所指向的地址不同。
待后面我们展开学习go中的 *(指针)与(地址)后再回头来理解上面的赋值和内存相关会容易理解很多。