3

牛刀小试基本语法,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang基本语法和变量的...

 1 year ago
source link: https://v3u.cn/a_id_224
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
牛刀小试基本语法,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang基本语法和变量的使用EP02

    书接上回,Go lang1.18首个程序的运行犹如一声悠扬的长笛,标志着并发编程的Go lang巨轮正式开始起航。那么,在这艘巨轮之上,我们首先该做些什么呢?当然需要了解最基本的语法,那就是基础变量的声明与使用。

    变量的声明与使用

    变量是什么玩意?是具体的数据被内存存储之后内存地址的名称。说白了就是内存中的门牌号,在go lang中声明变量有很多种方式,相对严谨的:

package main // 声明 main 包

import f "fmt" // 导入 fmt 包,打印字符串时需要用到

func main() { // 声明 main 主函数入口

//声明变量

var name int
name = 1

f.Println(name)

} 1

    这里用var关键字声明变量name,变量名称可以是字母或下划线开头,由一个或多个字母、数字、下划线组成。随后指定数据类型,这里是整形,接着进行赋值操作,如果没有赋值动作,go lang会自动填充一个默认值:

package main // 声明 main 包

import f "fmt" // 导入 fmt 包,打印字符串时需要用到

func main() { // 声明 main 主函数入口

//声明变量

var name int

f.Println(name)

} 0

    相对简单一点的声明方式:

package main // 声明 main 包

import f "fmt" // 导入 fmt 包,打印字符串时需要用到

func main() { // 声明 main 主函数入口

//声明变量

var name = 1

f.Println(name)

}

    如果一个变量有一个初始值,go lang将自动能够使用初始值来推断该变量的类型。因此,如果变量具有初始值,则可以省略变量声明中的类型,也就是说一个,你得提前让go lang知道这个变量的数据类型,无论是通过那种方式。

    最后,类似Python中海象操作符的声明方式:

package main // 声明 main 包

import f "fmt" // 导入 fmt 包,打印字符串时需要用到

func main() { // 声明 main 主函数入口

//声明变量

name := 1

f.Println(name)

}

    海象操作符这样的声明方式可以不使用var关键字,事实上,它更像是一个连贯操作,既声明又赋值,算得上是赋值表达式。

    但需要注意已经声明过的(多个变量同时声明时,至少保证一个是新变量),否则会导致编译出错:

package main // 声明 main 包

import f "fmt" // 导入 fmt 包,打印字符串时需要用到

func main() { // 声明 main 主函数入口

//声明变量

name = 1

//重复赋值

name := 2

f.Println(name)

}

    程序返回:

command-line-arguments
# command-line-arguments
.\test.go:9:2: undefined: name
> Elapsed: 1.097s
> Result: Error

    另外,海象操作符声明只能被用在方法里面,而不可以用于全局变量的声明与赋值。

    如果不想手动一个一个赋值,也可以进行多变量赋值的操作:

package main // 声明 main 包

import f "fmt" // 导入 fmt 包,打印字符串时需要用到

func main() { // 声明 main 主函数入口

//声明变量

var name1, name2, name3 = 1, "2", false

f.Println(name1)
f.Println(name2)
f.Println(name3)

}

1
2
false

    变量永远都必须先声明才能使用,这是放之四海而皆准的原则,不同于Python或者Ruby,go lang是静态语言,要求变量的类型和赋值的类型必须一致:

package main // 声明 main 包

import f "fmt" // 导入 fmt 包,打印字符串时需要用到

func main() { // 声明 main 主函数入口

//声明变量

var name = "你好"

name = 1

f.Println(name)

}

    这里会报类型异常的错误:

command-line-arguments
# command-line-arguments
.\test.go:11:9: cannot use 1 (untyped int constant) as string value in assignment
> Elapsed: 0.561s
> Result: Error

    最后,声明了变量就需要使用,如果不用,那么声明的意义在哪儿呢?

func main() {
var a string = "abc"
fmt.Println("hello, go lang")
}

    编译后会报异常:

a declared and not used

    变量内存地址、占用空间大小和交换赋值

    任何声明的变量都在内存中有自己的地址,我们可以通过&关键字将其获取出来:

package main // 声明 main 包

import f "fmt" // 导入 fmt 包,打印字符串时需要用到

func main() { // 声明 main 主函数入口

//声明变量

var name = "你好"

f.Println("name的内存地址是", &name)

}

    程序返回:

name的内存地址是 0xc00003c250

    和Python的内存管理机制不同,go lang会将相同值的变量指向不同的内存地址:

package main // 声明 main 包

import f "fmt" // 导入 fmt 包,打印字符串时需要用到

func main() { // 声明 main 主函数入口

//声明变量

var name = "你好"

a := name

f.Println("name的内存地址是", &name)

f.Println("a的内存地址是", &a)

}

    程序返回:

name的内存地址是 0xc00003c230
a的内存地址是 0xc00003c240

    但地址的范围是相似的,这样更方便同类型同值的变量回收,有点类似“网段”的概念。

    我们也可以通过unsafe包的Sizeof方法来获取变量具体在内存中占用多少空间:

package main // 声明 main 包

import f "fmt" // 导入 fmt 包,打印字符串时需要用到

import u "unsafe"

func main() { // 声明 main 主函数入口

a := 100
f.Println("a的地址:", &a)

f.Println("占用内存:", u.Sizeof(a))

}

    程序返回:

a的地址: 0xc0000aa058
占用内存: 8

    如果我们想交换两个变量的值,则可以简单地使用 a, b = b, a,两个变量的类型必须是相同:

package main // 声明 main 包

import f "fmt" // 导入 fmt 包,打印字符串时需要用到

func main() { // 声明 main 主函数入口

b, a := 1, 2

f.Println(&b, &a)

b, a = a, b

f.Println(b, a)

f.Println(&b, &a)

}

    程序返回:

0xc00012c058 0xc00012c070
2 1
0xc00012c058 0xc00012c070

    由此我们可以发现,值交换了,但内存地址并未改变,可以理解为旧瓶装新酒。交换赋值的底层逻辑也和Python一样,需要有第三个隐藏变量来做值的传递。

    另外,golang当中也支持匿名变量,也就是说对于我们不需要的返回值或者变量,我们可以不用额外定义一个变量去接收。否则没有用处,还会报错:

package main // 声明 main 包

import f "fmt" // 导入 fmt 包,打印字符串时需要用到

func main() { // 声明 main 主函数入口

a, _ := 1, 2

f.Println("a = ", a) // 1

}

    常量constant

    常量(constant)表示不变的值。在程序运行时,不会被代码逻辑修改。比如数学上的圆周率、自然常数e等等:

package main // 声明 main 包

import f "fmt" // 导入 fmt 包,打印字符串时需要用到

func main() { // 声明 main 主函数入口

const value int = 1
// value = 100 // 常量是不允许被修改的
f.Println("value = ", value)

}

    这里通过const关键字来代替var关键字来声明常量,和JavaScript语法一致。

    常量声明也可以被用于枚举场景,就是所谓的常量组:

const (
Unknown = 0
Female = 1
Male = 2
)

    在常量声明表达式中,我们也可以用iota关键字进行动态声明:

package main // 声明 main 包

import f "fmt" // 导入 fmt 包,打印字符串时需要用到

func main() { // 声明 main 主函数入口

// const来定义枚举类型
const (
// 可以在const()中添加一个关键字iota, 每行的iota都会累加1, 第一行的iota默认是0
a = iota + 1 // iota = 0
b // iota = 1
c // iota = 2
)

f.Println("a = ", a) // 1
f.Println("b = ", b) // 2
f.Println("c = ", c) // 3

}

    变量作用域

    变量的作用域可以理解为可访问指定变量的程序的某个范围。可以在类,方法,循环等中定义变量。像C / C ++一样,在Golang中,所有的标识符都是词法(或静态)作用域,即变量的作用域可以在编译时确定,也就是说,和Python不一样的是,Go lang是具备块作用域的:

//局部变量
package main
import "fmt"

//主函数
func main() {

//从这里开始主函数的局部作用域
//主函数内的局部变量
var myvariable1, myvariable2 int = 69, 145

// 显示变量的值
fmt.Printf("myvariable1 变量的值 : %d\n", myvariable1)

fmt.Printf("myvariable2 变量的值 : %d\n", myvariable2)

} // 此处主要函数的局部作用域结束

    在方法或块中声明的变量称为局部变量,这些不能在函数或块之外访问。这些变量也可以在函数内的for,while语句等内部声明,但是,这些变量可以由函数内的嵌套代码块访问,这些变量也称为块变量。

如果在同一作用域中用相同的名称声明两次这些变量,则会出现编译时错误。函数执行结束后,这些变量将不存在。在循环外声明的变量也可以在嵌套循环内访问。这意味着方法和所有循环都可以访问全局变量。局部变量可被循环访问,并在该函数内执行函数。在循环体内声明的变量对循环体外不可见。

    除此以外,我们可以在程序内定义全局变量:

//全局变量
package main
import "fmt"

// 全局变量声明
var myvariable1 int = 100

func main() {

// 主函数内部的局部变量
var myvariable2 int = 200

//显示全局变量
fmt.Printf("全局变量 myvariable1 的值是 : %d\n", myvariable1)

//显示局部变量
fmt.Printf("局部变量 myvariable2 的值是 : %d\n", myvariable2)

//调用函数
display()

}

func display() {
// 显示全局变量
fmt.Printf("全局变量 myvariable1 的值是 : %d\n", myvariable1)

}

    在函数或块之外定义的变量称为全局变量,这些变量在程序的整个生命周期中都可用。

    最后,go lang也有系统的内置作用域,也就是内置的关键字变量,所以我们声明变量的时候,不能和系统关键字重名,否则系统就不知道到底该调用那个作用域的变量了:

var和const :变量和常量的声明
var varName type 或者 varName : = value
package and import: 导入
func: 用于定义函数和方法
return :用于从函数返回
defer someCode :在函数退出之前执行
go : 用于并行
select 用于选择不同类型的通讯
interface 用于定义接口
struct 用于定义抽象数据类型
break、case、continue、for、fallthrough、else、if、switch、goto、default 流程控制
chan用于channel通讯
type用于声明自定义类型
map用于声明map类型数据
range用于读取slice、map、channel数据

    变量是一切逻辑的基础,没有变量就不可能有运算、判断以及相关业务逻辑。如果进行类比的话,变量操作就是一门功夫的内功心法,只有掌握了内功才能用内力催动招式,同样地,掌握一门内功就可以举一反三,触类旁通,君不见go lang中使用的系统关键字也都会出现在诸如Python、Javascript等脚本语言中,所以说白了,天下武功,殊途同归,原理上大同小异,只是运使法门上略有不同,却还是有相互映照之处,下一回我们将进入到具体变量类型的修炼,欲知更多,敬请期待。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK