Go 基础

一、Go 语言结构

在我们开始学习 Go 编程语言的基础构建模块前,让我们先来了解 Go 语言最简单程序的结构。


Go Hello World 实例

Go 语言的基础组成有以下几个部分:

  • 包声明
  • 引入包
  • 函数
  • 变量
  • 语句 & 表达式
  • 注释

接下来让我们来看下简单的代码,该代码输出了"Hello World!":

package main

import "fmt"

func main() {
   /* 这是我的第一个简单的程序 */
   fmt.Println("Hello, World!")
}

让我们来看下以上程序的各个部分:

  1. 第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。

  2. 下一行 import "fmt" 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。

  3. 下一行 func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。

  4. 下一行 /*...*/ 是注释,在程序执行时将被忽略。单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。

  5. 下一行 fmt.Println(...) 可以将字符串输出到控制台,并在最后自动增加换行字符 。 
    使用 fmt.Print("hello, world ") 可以得到相同的结果。 
    Print 和 Println 这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。

  6. 当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )。


执行 Go 程序

让我们来看下如何编写 Go 代码并执行它。步骤如下:

  1. 打开编辑器如Sublime2,将以上代码添加到编辑器中。

  2. 将以上代码保存为 hello.go

  3. 打开命令行,并进入程序文件保存的目录中。

  4. 输入命令 go run hello.go 并按回车执行代码。

  5. 如果操作正确你将在屏幕上看到 "Hello World!" 字样的输出。

$ go run hello.go
Hello, World!

二、Go语言基础

Go 标记

Go 程序可以由多个标记组成,可以是关键字,标识符,常量,字符串,符号。如以下 GO 语句由 6 个标记组成:

fmt.Println("Hello, World!")

6 个标记是(每行一个):

1. fmt
2. .
3. Println
4. (
5. "Hello, World!"
6. )

行分隔符

在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go 编译器自动完成。

如果你打算将多个语句写在同一行,它们则必须使用 ; 人为区分,但在实际开发中我们并不鼓励这种做法。

以下为两个语句:

fmt.Println("Hello, World!")
fmt.Println("666666666666666666")

注释

注释不会被编译,每一个包应该有相关注释。

单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾。如:

// 单行注释
/*
 哈哈哈哈
 我是多行注释
 */

标识符

标识符用来命名变量、类型等程序实体。一个标识符实际上就是一个或是多个字母(A~Z和a~z)数字(0~9)、下划线_组成的序列,但是第一个字符必须是字母或下划线而不能是数字。

以下是有效的标识符:

mahesh  
kumar  
abc  
move_name 
a_123
myname50  
_temp  
j  
a23b9 
retVal

以下是无效的标识符:

1ab(以数字开头)
case(Go 语言的关键字)
a+b(运算符是不允许的)

关键字

下面列举了 Go 代码中会使用到的 25 个关键字或保留字:

  • 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数据
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

除了以上介绍的这些关键字,Go 语言还有 36 个预定义标识符:

append bool byte cap close complex complex64 complex128 uint16
copy false float32 float64 imag int int8 int16 uint32
int32 int64 iota len make new nil panic uint64
print println real recover string true uint uint8 uintptr

程序一般由关键字、常量、变量、运算符、类型和函数组成。

程序中可能会使用到这些分隔符:括号 (),中括号 [] 和大括号 {}。

程序中可能会使用到这些标点符号:.、,、;、: 和 …。

Go 语言的空格

Go 语言中变量的声明必须使用空格隔开,如:

var age int;

语句中适当使用空格能让程序看易阅读。

无空格:

fruit=apples+oranges;

在变量与运算符间加入空格,程序看起来更加美观,如:

fruit = apples + oranges; 

go tools中可以使用gofmt 格式化代码

 
 

三、Go 语言数据类型

在 Go 编程语言中,数据类型用于声明函数和变量。

数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。

Go 语言按类别有以下几种数据类型:

序号类型和描述
1 布尔型
布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。
2 数字类型
整型 int 和浮点型 float,Go 语言支持整型和浮点型数字,并且原生支持复数,其中位的运算采用补码。
3 字符串类型:
字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本。
4 派生类型:
包括:
  • (a) 指针类型(Pointer)
  • (b) 数组类型
  • (c) 结构化类型(struct)
  • (d) Channel 类型
  • (e) 函数类型
  • (f) 切片类型
  • (g) 接口类型(interface)
  • (h) Map 类型

数字类型

Go 也有基于架构的类型,例如:int、uint 和 uintptr。

序号类型和描述
1 uint8
无符号 8 位整型 (0 到 255)
2 uint16
无符号 16 位整型 (0 到 65535)
3 uint32
无符号 32 位整型 (0 到 4294967295)
4 uint64
无符号 64 位整型 (0 到 18446744073709551615)
5 int8
有符号 8 位整型 (-128 到 127)
6 int16
有符号 16 位整型 (-32768 到 32767)
7 int32
有符号 32 位整型 (-2147483648 到 2147483647)
8 int64
有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)

浮点型:

序号类型和描述
1 float32
IEEE-754 32位浮点型数
2 float64
IEEE-754 64位浮点型数
3 complex64
32 位实数和虚数
4 complex128
64 位实数和虚数

其他数字类型

以下列出了其他更多的数字类型:

序号类型和描述
1 byte
类似 uint8
2 rune
类似 int32
3 uint
32 或 64 位
4 int
与 uint 一样大小
5 uintptr
无符号整型,用于存放一个指针

四、Go 语言常量

常量是一个简单值的标识符,在程序运行时,不会被修改的量。

常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。

常量的定义格式:

package main

import "fmt"

//常量的数据类型 只可以是布尔型、数字型(整数型、浮点型、复数)、字符串型
func main() {
    const identifier string = "chao"

    //声明多个相同类型
    const name, last_name, first_name= "yu", "yuyu", "yuyuyu"
    //显示类型定义
    const age int  =10
    //隠式类型定义
    const year  = 5
    var age_year int
    age_year=age*year
    fmt.Printf("年纪是:%d",age_year)
    fmt.Println()
    fmt.Println(name,last_name,first_name)
    }

多个常量的应用:

package main

import "unsafe"

// 常量还可以用作枚举
const (
    Unknow = 0
    Female = 1
    Male   = 2

)
//常量可以计算表达式的值len(),cap(),unsafe.Sizeof()等,  常量表达式中,函数必须是内置函数,否则编译不过
const (
    a="abc"
    b=len(a)
    c=unsafe.Sizeof(a)
)

func main()  {
    println(a,b,c)
}

iota

iota,特殊常量,可以认为是一个可以被编译器修改的常量。

在每一个const关键字出现时,被重置为0,然后再下一个const出现之前,每出现一次iota,其所代表的数字会自动增加1。

iota 可以被用作枚举值:

const (
    a = iota
    b = iota
    c = iota
)
//因此这里,第一个iota a是0,新一行使用iota就自动加1,所以是  0,1,2
/*
也可以写作
const(
    a = iota
    b
    c

)
 */
func main() {
    println(a, b, c)
}

iota 用法

package main

// iota 特殊常量,一个可以被编译器修改的常量
// 每一个const关键字出现时,被重置为0,然后下一个const出现之前,每一个iota,代表的数字会增加1

import "fmt"
const(
    i=1<<iota
    j=3<<iota
    k
    l
    // k=3<<2  l=3<<3  位运算,理解成  3乘以 2的3次方,3*8 ,所以3<<3等于24
)
func main(){
    fmt.Println("i=",i)
    fmt.Println("j=",j)
    fmt.Println("k=",k)
    fmt.Println("l=",l)
}

五、Go语言之讲解GOROOT、GOPATH、GOBIN

Go是一门全新的静态类型开发语言,具有自动垃圾回收丰富的内置类型,函数多返回值错误处理匿名函数,并发编程反射等特性.

go命令依赖一个重要的环境变量:$GOPATH 
GOPATH允许多个目录,当有多个目录时,请注意分隔符,多个目录的时候Windows是分号;

当有多个GOPATH时默认将go get获取的包存放在第一个目录下 

$GOPATH目录约定有三个子目录

  • src存放源代码(比如:.go .c .h .s等)   按照golang默认约定,go run,go install等命令的当前工作路径(即在此路径下执行上述命令)。
  • pkg编译时生成的中间文件(比如:.a)  golang编译包时
  • bin编译后生成的可执行文件(为了方便,可以把此目录加入到 $PATH 变量中,如果有多个gopath,那么使用${GOPATH//://bin:}/bin添加所有的bin目录)

代码目录结构规划

GOPATH下的src目录就是接下来开发程序的主要目录,所有的源码都是放在这个目录下面,那么一般我们的做法就是一个目录一个项目,

例如: $GOPATH/src/mymath 表示mymath这个应用包或者可执行应用,这个根据package是main还是其他来决定,main的话就是可执行应用,其他的话就是应用包,这个会在后续详细介绍package。

首先看下我的go环境:go env

C:UsersAdministrator>go env
set GOARCH=amd64
set GOBIN=
set GOEXE=.exe
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=D:project
set GORACE=
set GOROOT=D:BaiduNetdiskDownloadgo
set GOTOOLDIR=D:BaiduNetdiskDownloadgopkg	oolwindows_amd64
set GCCGO=gccgo
set CC=gcc
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0
set CXX=g++
set CGO_ENABLED=1
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config

GOROOT

其实就是golang 的安装路径
当你安装好golang之后其实这个就已经有了

GOBIN

首先看一下结构:

我们通常是在project目录下执行go build,例如:

D:projectsrcgo_devday1package_examplemain>go run main.go
400 100

现在需要编译main.go,golang 会自动去src下找hello目录,因为我的main.go中代码的开通导入了packag main包,所以可以编译成可执行文件,但是这样默认在当前目录下生成可执行文件,虽然可以指定目录,但是还是感觉不是非常方便

d:project>go build go_dev/day1/package_examplemain

所以还有两个非常好用的命令:go get 和go install

go get

go get会做两件事:
1. 从远程下载需要用到的包
2. 执行go install

go install

go install 会生成可执行文件直接放到bin目录下,当然这是有前提的
你编译的是可执行文件,如果是一个普通的包,会被编译生成到pkg目录下该文件是.a结尾

关于go的整体一个开发目录

go_project     // go_project为GOPATH目录
  -- bin
     -- myApp1  // 编译生成
     -- myApp2  // 编译生成
     -- myApp3  // 编译生成
  -- pkg
  -- src
     -- myApp1     // project1
        -- models
        -- controllers
        -- others
        -- main.go 
     -- myApp2     // project2
        -- models
        -- controllers
        -- others
        -- main.go 
     -- myApp3     // project3
        -- models
        -- controllers
        -- others
        -- main.go 

Linux下配置go环境

1、首先下载linux下的go包:https://studygolang.com/dl/golang/go1.9.2.linux-amd64.tar.gz
2、下载之后 

tar -zxvf go1.9.2.linux-amd64.tar.gz 解压源码包

3、移动到 /usr/local/go 也就是GOROOT

4、设置GOPATH,还有PATH环境变量

export GOROOT=/usr/local/go #设置为go安装的路径
export GOPATH=$HOME/gocode #默认安装包的路径
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin

查看Linux go env

GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/root/gocode"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build057487015=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
go env

参考文章地址:

https://studygolang.com/articles/7202   

http://www.cnblogs.com/zhaof/p/7906722.html

记录GOPATH在GOLAND中的坑

首先环境已配置好:

GO的目录结构是:

add.go

package calc
//函数名必须大写首字母,不然外部包找不到 func Add(a
int,b int)(int){ return a+b }

sub.go

package calc
func Sub(a int,b int)(int){
    return a-b
}

执行文件,main.go

package main

import "fmt"
//导入包路径,这里src省略,默认加在GOPATH
import "go_dev/day1/package_example/calc" func main(){ sum := calc.Add(100,300) sub := calc.Sub(200,100) fmt.Println(sum,sub) }

在cmd正确执行是:

这样是没问题的,GOPATH已经确定正确

但是GOLAND有个坑

在IDE中执行一直报错:

这里的GOPATH已经出错了,所以编译找不到,我检查了go env也是正确的GOPATH....

原来我忘记了GOLAND IDE中也有设置:

在这里修改为正确的GOPATH,再次执行:

package main

// 关键字var 声明变量 类型信息放在变量名后
//声明一个int型变量
var v1 int

//声明一个string类型
var v2 string

//声明多个变量
var v3, v4 bool

//[0 0 0 0 0 0 0 0 0 0] 数组
var v5 [10]int

//数组切片
var v6 []int

//声明结构体
var v7 struct {
    f int
}

// 声明指针
var v8 *int

//声明map key为string类型,value是int类型
var v9 map[string]int

// 匿名函数和闭包
var v10 func(a int) int

//多个需要声明的变量放在一起
var (
    name int
    age  string
)
声明变量
package main

import "fmt"

func main() {
    //声明变量的初始化,var 可以省略写法
    var v1 int = 10
    //编译器可以自动算出v2的类型
    var v2 = 10
    //编译器自动算出v3的类型
    //同时进行变量声明和初始化
    v3 := 10
    fmt.Println(v1, v2, v3)
}

//  出现在:= 左侧的变量 只能声明一次,不可重复
变量初始化
package main

import "fmt"

//声明之后再赋值
func main() {
    var i int
    i = 100
    var j int
    j = 50
    //Go语言的多重赋值,如下代码交换i和j的变量
    //go的多重赋值特性可以很明显的优化代码,相比c/c++
    i, j = j, i
    fmt.Println(i, j)
}
变量赋值
package main

import "fmt"

//使用强类型语言编程时,调用函数时为了获取一个值,却因为函数返回多个值,又得定义一堆变量
//可以使用多重返回和匿名变量来避免这些问题
func GetName() (firstname, lastname, nickname string) {
    return "yu", "yuchao", "chaoge"
}

//只想获得nickname,函数调用语句可以写
func main() {
    //优化代码的清晰度
    _, _, nickname := GetName()
    fmt.Println(nickname)

}
匿名变量
package main

//声明go代码所属的包,包是go例最基本的分发单位,要生成可执行程序,必须名为main,且下面有个main()函数,作为执行起点

//导入本程序所以来的包,下列用到Println()函数,所以导入fmt
import "fmt"

//不得在源代码中写入未使用的包,否则编译器会出错
//软件工程的设计哲学,强制左花括号 { 的放置位置
//函数名的大小写规则
func Compute(value1 int, value2 float64) (resule float64, err error) {
    //函数体

    fmt.Println(value1, value2)
    return
}

//main函数不能带参数,也不能定义返回值,命令行传入的参数存在os.Args变量中
func main() {
    Compute(1, 3)
}
代码解读
package main

//常量是编译期间就已知,而且不可改变,可以是数值类型(整型、浮点型、复数)、布尔类型、字符串
const Pi float64 = 3.14159265358979323846

//无类型浮点常量
const Zero = 0.0

const (
    //无类型常量
    size int64 = 1024
    eof        = -1
)

//常量多重赋值 u=0.0  v=3.0
const u, v float32 = 0, 3
//无类型整型,字符串常量
const a, b, c = 3, 4, "foo"
常量
1. 任何一个代码文件隶属于一个包
2. import 关键字,引用其他包:
import(“fmt”)
import(“os”)
通常习惯写成:
import (
      “fmt”
       “os”
)
3. golang可执行程序,package main,
     并且有且只有一个main入口函数
4. 包中函数调用:
a. 同一个包中函数,直接调用
b. 不同包中函数,通过包名+点+
函数名进行调用
5. 包访问控制规则:

大写意味着这个函数/变量是可导出的
小写意味着这个函数/变量是私有的,
     包外部不能访问
package main
/*
算出结果是:
0+10=10
1+9=10
2+8=10
3+7=10
4+6=10
5+5=10
6+4=10
7+3=10
8+2=10
9+1=10
10+0=10
 */
import "fmt"

func list(n int) {
    for i := 0; i <= n; i++ {
        fmt.Printf("%d+%d=%d
", i, n-i, n)
    }
}
func main() {
    list(10)
}

2. 一个程序包含两个包add和main,其中add包中有两个变量:Name和age。请问main
包中如何访问Name和age?

package main

import (
    //包别名的用法  a
    "fmt"
    a "go_dev/day2/example2/add"
)

func main() {
    fmt.Println("Name=:", a.Name)
    //add.age是小写 私有变量,所以找不到
    fmt.Println("age=:", a.Age)
}
 
main.go
package add

import (
    //_"go_dev/day2/example2/test"
)

func init() {
    Name = "hello world"
    Age = 10
}

var Name string = "xxxxx"
var Age int = 66
View Code

3. 包别名的应用,开发一个程序,使用包别名来访问包中的函数?

答案同2题

4. 每个源文件都可以包含一个init函数,这个init函数自动被go运行框架调用。开发一个程序
演示这个功能?

目录结构如图:

package main

import (
    //包别名的用法  a
    "fmt"
    a "go_dev/day2/example2/add"
)

func main() {
    //5 打印add中的
    fmt.Println("Name=:", a.Name)
    //add.age是小写 私有变量,所以找不到
    //6
    fmt.Println("age=:", a.Age)
}
main.go
package test

import "fmt"

var Name string = "This is test package"
var Age int = 1000

func init() {
    //1
    fmt.Println("this is a test,init")
    //2
    fmt.Println("test.package.Name=", Name)
    //3
    fmt.Println("test.package.Age=", Age)
    Age=10
    //4
    fmt.Println("test.package.age=",Age)
}
test.go
package add

import (
    _"go_dev/day2/example2/test"
)

func init() {

    Name = "hello world"
    Age = 10
}

var Name string = "xxxxx"
var Age int = 66
add.go
6. 定义两个常量Man=1和Female=2,获取当前时间的秒数,如果能被Female整除,则
在终端打印female,否则打印man。

Second := time.Now().Unix()
package main

import (
    "fmt"
    "time"
)

const (
    Man = iota + 1
    Female
)
/*
6. 定义两个常量Man=1和Female=2,获取当前时间的秒数,如果能被Female整除,则
    在终端打印female,否则打印man。

Second := time.Now().Unix()

 */
func main() {
    //for 就是个死循环
    for {
        time.Sleep(100000 * time.Microsecond)
        second := time.Now().Unix()
        if second%Female == 0 {
            fmt.Println("female")
        } else {
            fmt.Println("man")
        }
    }
}
原文地址:https://www.cnblogs.com/bubu99/p/12521634.html