golang笔记2_程序结构
golang程序结构
2.1 命名
Golang中的命名遵循这样一个简单原则,名字的开头必须是字母或者下划线,后面跟字母、数字或者下划线(这里与C语言中是一致的)。
在函数内部声明的实体,即局部变量,只在函数内部有效。在函数外定义的变量,在整个包内有效(注意是包,不是文件,多个文件可以属于同一个包)。
首字母的大小写决定了是否对其他包可见,首字母小写只对本包内有效,首字母大写对于其他包可见。比如,fmt.Println()
函数名字的首字母大写,可以在其他包内引用该函数。
名字中出现多个单词时,习惯上使用大写单词首字母以便于阅读(一般不用下划线分割),比如parseRequestLine()
。
2.2 声明
声明命名了一个程序实体,并制定了它的一些特性。有四种主要声明,分别是var
、const
、type
、func
。
go程序的结构遵循如下的结构:
// package name
package main
// import other packages
import (
"fmt"
)
type student struct {
name string
age int
}
var (
x int = 0
y float64 = 1.1
z bool = false
)
const (
dayOfWeek = 7
)
func main() {
gexin := student{name: "gexin", age: 27}
fmt.Println(gexin, x, y, z, dayOfWeek)
}
2.3 变量
一个变量声明创建了一个特定类型的变量,并给变量一个名字,给变量赋初值。
var name type = expression
var x int = 10
var x = 10
var x int
声明变量时,type
与expression
至少存在一个。如果不存在type,则根据expression的类型来确定变量类型;如果不存在expression,则将其初始化为0。
对于数值型变量其默认初始化为0,布尔变量默认初始化为false,字符串默认初始化为空字符串"",引用类型(slice, pointer, map, channel, function)默认初始化为nil,组合类型(array、struct)所有的成员都默认初始化为0.
0初始化机制使得任何变量始终都有一个正确的值(不像C语言中,对于未初始化的某些变量可能会造成问题,尤其是内存相关的)。
显式初始化的值可以是字符串值,亦可以是任意表达式。包级别的变量(在函数体外部声明的变量)实在main()函数开始之前初始化的,局部变量是在函数运行时在其声明的地方初始化的。
多个变量可以同时声明并用函数初始化,如下所示:
var f, err = os.Open(arg)
2.3.1 短变量声明
在函数体内部,可以使用另一种声明格式,称之为"短变量声明",采用如下的格式:
x := 10
y := 3.14
f, err := os.Open(arg)
多数情况下局部变量使用短变量声明,变量声明用在那些需要显示表明变量类型的地方,或者是变量初值不重要,只是需要一个类型的变量的地方。
短变量声明也支持多变量同时声明,如下所示:
i, j := 0, 1
短变量声明也可在函数调用时声明一个变量作为函数返回值,需要始终注意的是:=
是声明,其左侧的多个变量至少有一个是新声明的变量,对于已存在的变量其作用相当于赋值。
f, err := os.Open(fileName)
2.3.2 指针
变量是一段保存了一个值得存储空间,变量是在声明过程中创建的,并且给了一个名字用来访问变量。也有一些变量是通过表达式来访问的,比如x[i]
、x.f
,这类表达式读取变量的值,当出现在赋值号左边时,就是给变量赋值。
一个指针的值是一个变量的地址,指针是一个值在内存中存储的位置。不是每个值都有地址,但是每个变量都有地址,变量也可以成为可以被寻址的值。通过指针,我们可以访问或者修改某个变量的值(直接通过变量地址,不用知道变量名字)。
“变量”与“值”这俩概念有点拗口,听起来有点别扭。变量是一段存储空间,里面的实际内容就是值。变量也可以成为可以被寻址的值。变量的内容可以通过变量名字访问,亦可以直接通过指针访问,指针就是变量的存储地址。
对于一个已经声明的变量x int
,&x
表示地址可以赋值给指针,这个地址值得类型是*int
。
x := 1
p := &x
fmt.Println(*p) // 1
*p=2
fmt.Println(x) // 2
指针的默认初始化0值时nil
,指针时可以比较的,当两个指针指向同一个变量时,他们是相等的。
在golang中,函数返回局部变量的地址是安全的(这一点比C好),比如下面的代码的代码中,在函数f1返回后,变量v的地址仍然是有效的,其值为10;
func main(){
p := f1()
fmt.Println(*p) // 10
}
func f1() *int {
v := 10
return &v
}
在函数调用中,亦可用传地址方式修改参数,函数的参数设置为指针类型,就可以了。这点与C语言一致。
2.3.3 new()函数
new(T)会创建一个变量,0初始化并返回其地址,但是没有给这个变量名字。
p := new(int) // p是*int类型的指针,*p值初始化为0
fmt.Println(*p) // 0
*p = 2
fmt.Println(*p) // 2
需要注意的是,通过new来创建的变量与普通创建的变量并无什么不同,只是没有给变量起名字罢了,所以下面代码段中的两个函数本质是一样的。
func newInt() *int{
return new(int)
}
// **************************
func newInt() *int{
var dummy int
return &dummy
}
2.3.4 变量的生命周期
包级别变量存在于程序的整个执行期间,局部变量有着动态的声明周期。局部变量在声明时被创建,直到变量不被访问才会被销毁。函数参数及返回结果也是局部变量,当函数调用时被创建。
for t := 0.0; t < cycles*2*math.Pi; t += res {
x := math.Sin(t)
y := math.Sin(t*freq + phase)
img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), blackIndex)
}
在上面的代码段中,t是在循环开始时被创建,x,y在每次循环中创建。
垃圾收集机制是如何判断何时收回变量呢?这是个比较复杂的问题,这里简单介绍一下其原理。每次创建一个变量时,该变量都作为一个根路径来被他的指针或者其他引用跟踪。如果这样的路径都不存在了,那么这个变量就不可访问了,这时就可以回收了。
变量的生命周期取决于它是否可以被访问,一个局部变量可以存在于他的代码段之外,所以函数返回局部变量的地址也是安全的。
2.4 赋值
变量的值是通过赋值语句来更新的,如下所示:
x = 1
*p = true
person.name = "gexin"
count[x] = count[x] * scale
跟C语言中一样,以下的形式也是正确的
count[x] *= 2
v := 1
v++
v--
2.4.1 Tuple(元组)赋值
元组赋值是说多个变量同时赋值
// 交换变量的值
x, y = y, x
a[i], a[j] = a[j], a[i]
求两个整数的最大公约数
func gdc(x, y int) int {
for y != 0{
x, y = y, x%y
}
return x
}
求第n个菲波那切数列
func fib(n int) int{
x, y := 0, 1
for i:=0; i<n; i++{
x, y = y, x+y
}
return x
}
一些函数需要返回额外的错误码以表明程序执行的状态,比如之前用的到os.Open()
,这时就需要元组赋值了,如下所示:
f, err = os.Open("file.txt")
有三个操作符有时也表现出相同的方式,如下所示:
v, ok = m[key] // map lookup
v, ok = x.(T) // type assertion
v, ok = <-ch // chanel receive
就像变量声明一样,我们也可以用下划线来赋值不想用的值,如下所示:
_, err = io.Copy(dst, src)
_, ok = x.(T)
2.4.2 可赋值性
除了显示的赋值,还有很多地方会有隐式赋值。程序调用时,会隐式的给参数变量赋值;程序返回时,隐式的给结果变量赋值;符合结构的数据使用字符常量,默认给每个成员赋值,如下所示:
medals := []string{"gold", "silver", "bronze"}
// 对每个元素隐式赋值,相当于如下三个赋值
medals[0] = "gold"
medals[1] = "silver"
medals[2] = "bronze"
一个赋值操作,不管是显式的还是隐式的,只要两侧的类型一致,该操作就是合法的。
两个值是否相等,==
或者!=
,与可赋值性相关。在比较操作中,第一个操作数必须可以被第二个的数据类型赋值,反之亦然。
2.5 类型声明
变量或者表达式的类型决定了值得表现形式, 比如值得size,如何表示,支持的运算,与之关联的操作方法等等。
type name underlying_type
一个类型声明定义了一个名为"name"的类型,它与 "underlying_type"有着相同的类型。
type Celsius float64
type student struct {
underlying_type
age int
}name string
age int
对于每个类型T,都有一个转换操作,T(x),该操作将x值转换为T类型。转换在以下几种情况下才是允许的:
+ x的类型和T都有着相同的"underlying_type"
+ 都是未命名的指针类型,而且指向相同的"underlying_type"数据
+ 虽然改变的类型,但是不影响值得表达
转换在数值类型间是可以转换的,字符串和一些slice类型间也是可以转换的。这些转换可能会影响值得表达,比如将一个float64类型装换为int。
### 2.6 包和文件
go中的pacakge就跟C语言中的库是一样的,包的源码分布在一个或者多个.go文件中,每个包给其中的声明都提供了一个独立的命名空间,比如`utf16.Decode()`与`image.Decode()`就是两个不同的函数。
包用简单的方式决定一个变量是否可以被包外访问,首字母大写的才可以在包外访问,首字母小写的只能在包内访问。
我们在这里实现温度转换的例子,该包使用两个文件来实现,一个包用来声明类型、常量等信息,另一个包用来实现方法。
```go
// file tempconv.go
package tempconv
import (
"fmt"
)
type Celsius float64
type Faherenheit float64
const (
AbsoluteZeroC Celsius = -273.15
FreezingC Celsius = 0
BoilingC Celsius = 100
)
func (c Celsius) String() string { return fmt.Sprintf("%g°C".c) }
func (f Faherenheit) String() string { return fmt.Sprintf("%g°F", f) }
// file conv.go
package tempconv
func CToF(c Celsius) Faherenheit { return Faherenheit(c*9/5 + 32) }
func FToC(f Faherenheit) Celsius { return Celsius((f - 32) * 5 / 9) }
2.7 作用域
变量的作用域与生命周期不同,作用域是声明的变量在一段代码中可用的范围,作用域是一个编译时特性,而生命周期是运行时特性。
语法块是由花括号包含的一段区域代码,就像函数或者是循环体内那样。在一个语法块中声明的变量,在语法块外部是不可见的。与语法块类似,在整个代码中,即使不是花括号内,也存在词汇块。
一个声明的词汇块决定了它的作用域,内建类型的作用域是全体代码,比如int、float64、len等等。在函数之外声明的变量的作用域是包级别的,在同一个包的所有文件中都可见。引入的包(import "fmt"
),只在本文件内可见,是文件级别的。
跟c语言一样,作用域是嵌套的,在更近作用域的声明会覆盖到之前的声明,比如下面的代码段。
var (
x int = 10
y int = 20
)
func main(){
x:= 1
fmt.Println(x) // 会输出1
}
golang笔记2_程序结构的更多相关文章
- Python学习笔记(Ⅰ)——Python程序结构与基础语法
作为微软的粉丝,最后终于向Python低头了,拖了两三个月终于下定决心学习Python了.不过由于之前受到C/C#等语言影响的思维定式,前期有些东西理解起来还是很费了些功夫的. 零.先抄书: 1.Py ...
- c# 程序结构
最近工作中需要用到c#,所以从今天开始博客不定期更新c#学习笔记 c#程序结构大体分为, 命名空间 类 Main 方法 命名空间 相当于一个仓库 通过 using 引入命名空间 比如 using ...
- golang快速入门(六)特有程序结构
提示:本系列文章适合对Go有持续冲动的读者 阅前须知:在程序结构这章,更多会关注golang中特有结构,与其他语言如C.python中相似结构(命名.声明.赋值.作用域等)不再赘述. 一.golang ...
- C#学习笔记二:C#程序结构
从最简单的HelloWorld开始入手,这是一个最低限度的C#程序结构. C# Hello World 示例 一个C#程序主要由以下几部分组成: 命名空间声明 一个类 类方法 类属性 一个Main方法 ...
- 【Intel AF 2.1 学习笔记一】AF程序结构
Intel App Framework(原jqMobi)是用来开发hybrid app的开源免费框架,被intel收编之后发布了最新的2.1版本,最近正在学习.af的所谓程序结构,就是AF网页的架构, ...
- Objective-C 学习笔记(一) 语言程序结构
Objective-C语言程序结构 “Hello World”简单示例 #import <Foundation/Foundation.h> //预处理命令,它告诉Objective-C语言 ...
- Go语言基础之1--标识符、关键字、变量和常量、数据类型、Go的基本程序结构、Golang的特性
一.前言 当我们项目较为简单时,我们在src目录下新建一个该项目目录,里面存放源码文件即可,见下图: 当我们一个项目较为复杂时,我们可以在src目录下新建一个该项目目录,在针对该项目不同模块创建不同目 ...
- Laravel5.1学习笔记10 系统架构2 应用程序结构
应用程序结构 简介 根目录 App 目录 为应用程序设置命名空间 简介 默认的 Laravel 应用程序结构是为了给无论构建大型还是小型应用程序都提供一个良好的开始.当然,你可以依照喜好自由地组织应用 ...
- C#程序结构(学习笔记01)
C#程序结构 [原文参考官方教程] https://docs.microsoft.com/zh-cn/dotnet/csharp/tour-of-csharp/program-structure C# ...
随机推荐
- 微服务之配置中心ConfigKeeper
在微服务架构中,配置中心是必不可少的基础服务.ConfigKeeper已开源,本文将深度分析配置中心的核心内容,错过「Spring Cloud中国社区北京沙龙-2018.10.28 」的同学将从本篇文 ...
- Unity-iPhone has Conflicting Provisioning Settings
Select the top level node called Unity-iPhone in the left tree view (the one with the blue item). Se ...
- angular路由传参和获取路由参数的方法
1.首先是需要导入的模块 import { Router } from "@angular/router";//路由传参用到 import{ActivatedRoute,Param ...
- MySQL->导出/导入资料[20180521]
MySQL 导出 INTO OUTFILE将资料导出至文件中 mysqldump工具导出资料和数据结构,并且可以针对数据库.数据表.索引的结构. INTO OUTFILE测试 ...
- web前端 pdf 版电子 好书籍
http://www1.w3cfuns.com/feres.php?do=picture&listtype=book
- Go学习笔记02
前言 上篇内容,介绍了如何在不同的系统上安装 Go 开发环境和部分参数的配置,也简单介绍了 package 的概念.导入方式和我对包的初始化过程的理解,关于初始化顺序的理解,可能有错误,后期会有修改, ...
- SDR软件无线电知识要点(一)噪声系数与噪声因子
SDR软件无线电知识要点(一)噪声系数与噪声因子 信号质量如何评估 Noise Figure (NF) or sensitivity and Error Vector Magnitude (EVM) ...
- python-对于mysql数据库的操作
python操作mysql数据库 问题:DDL,DCL,DML的区别? 语言与框架:jdbc/odbc操作数据库 java(ibatis/hibernate/jpa)操作数据库 客户端工具:navic ...
- 20155209 2016-2017-2《Java程序设计》课程总结
20155209 2016-2017-2<Java程序设计>课程总结 预备作业1 刚刚接触Markdown的写法,刚刚接触博客,简单了解娄老师的教学方式. 预备作业2 怎么将学习java像 ...
- Linux 下 的 Oracle,如何安装 tnsname
运行 netca 即可: