golang学习随便记1-准备工作、程序结构

准备工作

用vscode编写golang程序显然要麻烦一点,另外,在墙内也是多了一些麻烦,好在资源够多:

go get更换国内镜像源_goldVitaminC的博客-CSDN博客   用镜像才能顺利下载包(包括vscode插件)

The Go Programming Language (google.cn)   这个代替官网

以前是在linux下用,而且是先 gcc 编译低版本go,然后再用低版本go编译高版本编译器,windows下就直接下载二进制安装包 .msi 了,装完:

C:\Program Files\Go    这里是 go 自己的东西,包括标准库包,

用户主目录 %HOMEPATH% (如 c:\users\zime)下有go子目录,里面有 bin、src 和 pkg 子目录,当我们使用其他包(包括插件)时,下载到 pkg 子目录。我们的源代码放入src子目录(对每个项目在里面建立一个子目录

可以在主目录下 mkdir  go,然后 mklink  /J  d:\go  .\go 在 D: 盘下创建符号链接(Junction)go 。然后切换到 D:\go,再 mkdir  src, cd  src,mkdir  hello,cd  hello,go mod init example/hello (不进行模块初始化,go run 没有问题,go build 会出错),在 VS Code 中,将文件夹 D:\go\src 添加到工作区,然后VS Code的资源管理中,在 D:\go\src\hello 下新建 hello.go文件(VS Code将提示安装golang有关扩展,安装即可),把下面的代码贴进去:

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

运行看结果:

D:\go\src\hello>go run .
Hello, World!

和我以前用 go 不一样的地方是,现在推荐项目下有名称为 go.mod 的依赖跟踪文件(每个项目一个go.mod),如果原来没有也简单  go  mod  init  模块路径名,这里 模块路径名  通常是项目在仓库的路径,类似 github.com/mymodule,这样可以确保 go的那些工具能下载这个包(如果我们的包并不需要给他人用,可以随便给个名称,不过一般使用这个包相对于$GOPATH/src的路径名称,如src下放了项目hello,就 go mod init  hello)

命令行参数

引入 "os" 包,os.Args 数组存放了命令行参数,项目 echo1

package main

import (
	"fmt"
	"os"
)

func main() {
	var s, sep string
	for i := 1; i < len(os.Args); i++ {
		s += sep + os.Args[i]
		sep = " "
	}
	fmt.Println(s)
}

os.Args[0] 为命令自己,os.Args[1]为第一个参数 ,如 go build 编译,./echo1  aaa  bbb  ccc 将输出 aaa  bbb  ccc

程序结构

常量

const boilingF = 212.0
const freezingF, boilingF = 32.0, 212.0

变量

var s string
var i, j, k int
var b, f, s = true, 2.3, "four"
var f, err = os.Open(name)

短变量声明,即用  :=  在赋值的同时声明变量,用所赋值确定变量的类型,如

anim := gif.GIF { LoopCount: nframes }
freq := rand.Float64() * 3.0
t := 0.0
i, j := 0, 1
f, err := os.Open(name)

指针

和C指针基本类似,如 p  *Point,这里先略过

new函数

p := new(int)
fmt.Println(*p)  // 0
*p = 2
fmt.Println(*p)  // 2

new()是内置函数,不是关键字(可以有变量名为new,但它的作用下不能调用new()函数),相当于分配内存返回指针,因为 go中变量总是初始化的,所以上例中第一条打印语句输出0

变量生命周期

var global *int

func f() {
  var x int
  x = 1
  global = &x
}

上例中,存在“变量逃逸”现象,即变量 x 尽管在函数 f 内部局部声明,但其地址赋值给了全局变量global,所以,编译器必然会将其分配在堆上。go可以保证变量逃逸下程序仍能正常运行,但过多的变量逃逸可能带来性能问题。

命名类型(类型别名)

这个差不多就是 typedef 的意思,例如定义两个类型分别表示摄氏温度类型和华氏温度类型:

type Celsius float64
type Fahrenheit float64

命名了这两个新类型之后,就可以用来声明常量和变量了

const (
  AbsoluteZeroC Celsius = -273.15
  FreezingC  Celsius = 0
  BoilingC  Celsius = 100
)

func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }

值得注意的是,golang是比C++ “更强”的强类型语言,上面的例子中,尽管 Celsius 和 Fahrenheit 内部都是 64位浮点数,但不能直接把一个 Celsius 类型的变量值赋值给 Fahrenheit 类型的变量(反之亦然),也不能用一个Celsius类型变量减去(或加上)另一个Fahrenheit类型的变量,必须进行显式类型转换,上例中 Celsius(t) 和 Fahrenheit(t) 是类型转换,不是函数。

我们可以为新命名的类型“附加”它的字符串表达,即为类型附加 String() 方法,从而打印该变量时,会按指定的字符串表达输出——这个和PHP里面 echo $v; 时,输出$v的字符串表达一样(如果$v 是类实例,则 $v 的魔术方法 __toString() 自动被调用)

func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }

大致按以下意思理解:func + <拥有者实例c> + String() + <返回类型> { ... return ... }

如果 

c := FToC(212.0)

则直接 fmt.Println(c) 将输出 100°C,当然,我们仍然可以输出 c 的值

fmt.Println(c.String())  // "100°C"
fmt.Printf("%v\n", c)    // "100°C"
fmt.Printf("%s\n", c)    // "100°C"
fmt.Println(c)           // "100°C"
fmt.Printf("%g\n", c)     // "100"
fmt.Printfln("float64(c)) // "100"

包和文件

golang每个文件开头都用 package 声明定义包的名称,当我们导入一个其他包时,那个包的成员(导出常量、导出变量、导出函数)可以用 packagename.CToF那样的方式引用。注意:这里有一个被王垠大神吐槽的点,就是哪些标识符是被导出的,哪些是外部不可见的,是按首字符大小写约定的(大写字母开头导出的,外部可见,小写字母开头是包内私有的

包使用例子,创建 greetings 目录(对应项目greetings) 和 hello目录(对应项目hello),我们将在 hello项目引用greetings项目的包(可以认为识别是几个项目,就是看go.mod文件)。这里是两个项目,在VS Code中,应当把两个项目目录加入工作区,直接打开这两个项目的根目录,VS Code的代码提示不能正确识别标识符的相互依赖,会提示出错!

<ch1>/
 |-- greetings/
 |-- hello/

在greetings目录用 go mod init example.com/greetings 创建依赖跟踪文件go.mod(同时命名了我们这个package对外的模块名为 example.com/greetings),然后创建 greetings.go 源文件如下:

package greetings

import "fmt"

func Hello(name string) string {
	message := fmt.Sprintf("Hi, %v. Welcome!", name)
	return message
}

注意,函数Hello首字母是大写的,因为它需要被包外“用户”使用!

在 hello 目录同样用 go mod init example.com/hello 创建依赖跟踪文件go.mod(同时命名了这个package对外的模块名为 example.com/hello),然后创建源代码文件 hello.go

package main

import (
	"fmt"

	"example.com/greetings"
)

func main() {
	name := "张三"
	fmt.Println(greetings.Hello(name))
}

这样直接 go run . 会出错,因为 go get 试图从仓库中去找 greetings 包,我们必须在 hello 项目的依赖文件告诉编译器,从本地文件系统找该包,所以,执行

go mod edit -replace example.com/greetings=../greetings

这时, go.mod 文件就多一句 replace 语句完成了模块路径的重定向,再用 go mod tidy 同步一下模块依赖(就是找出我们的源码依赖哪些外部包,它们的版本是什么,然后在 go.mod 依赖文件进行记录),hello的go.mod文件如下 (require语句声明了模块依赖项,注意,require后面的是模块名,对应前面 go  mod  init 命令后面的名称,最后是版本等信息)

module example.com/hello

go 1.17

replace example.com/greetings => ../greetings

require example.com/greetings v0.0.0-00010101000000-000000000000

这时 go run . 就可以正常工作了

作用域

基本上和直觉一致,略过

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值