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

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

书接上回,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等脚本语言中,所以说白了,天下武功,殊途同归,原理上大同小异,只是运使法门上略有不同,却还是有相互映照之处,下一回我们将进入到具体变量类型的修炼,欲知更多,敬请期待。

相关文章
|
13天前
|
监控 算法 Go
Golang深入浅出之-Go语言中的服务熔断、降级与限流策略
【5月更文挑战第4天】本文探讨了分布式系统中保障稳定性的重要策略:服务熔断、降级和限流。服务熔断通过快速失败和暂停故障服务调用来保护系统;服务降级在压力大时提供有限功能以保持整体可用性;限流控制访问频率,防止过载。文中列举了常见问题、解决方案,并提供了Go语言实现示例。合理应用这些策略能增强系统韧性和可用性。
70 0
|
4天前
|
Go
Go 语言教程
Go 语言教程
13 3
|
13天前
|
分布式计算 Java Go
Golang深入浅出之-Go语言中的分布式计算框架Apache Beam
【5月更文挑战第6天】Apache Beam是一个统一的编程模型,适用于批处理和流处理,主要支持Java和Python,但也提供实验性的Go SDK。Go SDK的基本概念包括`PTransform`、`PCollection`和`Pipeline`。在使用中,需注意类型转换、窗口和触发器配置、资源管理和错误处理。尽管Go SDK文档有限,生态系统尚不成熟,且性能可能不高,但它仍为分布式计算提供了可移植的解决方案。通过理解和掌握Beam模型,开发者能编写高效的数据处理程序。
143 1
|
13天前
|
缓存 测试技术 持续交付
Golang深入浅出之-Go语言中的持续集成与持续部署(CI/CD)
【5月更文挑战第5天】本文介绍了Go语言项目中的CI/CD实践,包括持续集成与持续部署的基础知识,常见问题及解决策略。测试覆盖不足、版本不一致和构建时间过长是主要问题,可通过全面测试、统一依赖管理和利用缓存优化。文中还提供了使用GitHub Actions进行自动化测试和部署的示例,强调了持续优化CI/CD流程以适应项目需求的重要性。
59 1
|
13天前
|
Kubernetes Cloud Native Go
Golang深入浅出之-Go语言中的云原生开发:Kubernetes与Docker
【5月更文挑战第5天】本文探讨了Go语言在云原生开发中的应用,特别是在Kubernetes和Docker中的使用。Docker利用Go语言的性能和跨平台能力编写Dockerfile和构建镜像。Kubernetes,主要由Go语言编写,提供了方便的客户端库与集群交互。文章列举了Dockerfile编写、Kubernetes资源定义和服务发现的常见问题及解决方案,并给出了Go语言构建Docker镜像和与Kubernetes交互的代码示例。通过掌握这些技巧,开发者能更高效地进行云原生应用开发。
64 1
|
13天前
|
负载均衡 监控 Go
Golang深入浅出之-Go语言中的服务网格(Service Mesh)原理与应用
【5月更文挑战第5天】服务网格是处理服务间通信的基础设施层,常由数据平面(代理,如Envoy)和控制平面(管理配置)组成。本文讨论了服务发现、负载均衡和追踪等常见问题及其解决方案,并展示了使用Go语言实现Envoy sidecar配置的例子,强调Go语言在构建服务网格中的优势。服务网格能提升微服务的管理和可观测性,正确应对问题能构建更健壮的分布式系统。
32 1
|
13天前
|
消息中间件 Go API
Golang深入浅出之-Go语言中的微服务架构设计与实践
【5月更文挑战第4天】本文探讨了Go语言在微服务架构中的应用,强调了单一职责、标准化API、服务自治和容错设计等原则。同时,指出了过度拆分、服务通信复杂性、数据一致性和部署复杂性等常见问题,并提出了DDD拆分、使用成熟框架、事件驱动和配置管理与CI/CD的解决方案。文中还提供了使用Gin构建HTTP服务和gRPC进行服务间通信的示例。
39 0
|
13天前
|
Prometheus 监控 Cloud Native
Golang深入浅出之-Go语言中的分布式追踪与监控系统集成
【5月更文挑战第4天】本文探讨了Go语言中分布式追踪与监控的重要性,包括追踪的三个核心组件和监控系统集成。常见问题有追踪数据丢失、性能开销和监控指标不当。解决策略涉及使用OpenTracing或OpenTelemetry协议、采样策略以及聚焦关键指标。文中提供了OpenTelemetry和Prometheus的Go代码示例,强调全面可观测性对微服务架构的意义,并提示选择合适工具和策略以确保系统稳定高效。
149 5
【Go语言入门100题】026 I Love GPLT (5 分) Go语言 | Golang
L1-026 I Love GPLT (5 分) Go语言|Golang 这道超级简单的题目没有任何输入。 你只需要把这句很重要的话 —— “I Love GPLT”——竖着输出就可以了。 所谓“竖着输出”,是指每个字符占一行(包括空格),即每行只能有1个字符和回车。
516 0
|
存储 Go
【Go语言入门100题】023 输出GPLT (20 分) Go语言 | Golang
L1-023 输出GPLT (20 分) Go语言|Golang 给定一个长度不超过10000的、仅由英文字母构成的字符串。请将字符重新调整顺序,按GPLTGPLT....这样的顺序输出,并忽略其它字符。当然,四种字符(不区分大小写)的个数不一定是一样多的,若某种字符已经输出完,则余下的字符仍按GPLT的顺序打印,直到所有字符都被输出。 下面给出甲、乙两人的酒量(最多能喝多少杯不倒)和划拳记录,请你判断两个人谁先倒。
142 0