go语言学习

曾劲松 · · 1457 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

go语言菜鸟教程:http://www.runoob.com/go/go-tutorial.html

实效go编程:http://docscn.studygolang.com/doc/effective_go.html

在线go运行环境:https://tour.golang.org/welcome/1

go的安装:参照这里在Windows上安装非常easy:http://www.runoob.com/go/go-environment.html

go中文标准库(Ctrl+f):http://docscn.studygolang.com/pkg/#stdlib

go英文包索引与搜索引擎:https://godoc.org/

/**********************************************************************************************************************/

gofmt 程序将Go程序按照标准风格缩进、 对齐,保留注释并在需要时重新格式化。若你想知道如何处理一些新的代码布局,请尝试运行 gofmt

分号并不在源码中出现。词法分析器会自动插入分号。可以概括为: “如果新行前的标记为语句的末尾,则插入分号”。左大括号不能放在下一行,如果这样做,就会在大括号前面插入一个分号,这可能引起不需要的效果。 

Go不再使用 do 或 while 循环,只有一个更通用的 forswitch 要更灵活一点;if 和 switch 像 for一样可接受可选的初始化语句; 此外,还有一个包含类型选择和多路通信复用器的新控制结构:select。 其语法也有些许不同:没有圆括号,而其主体必须始终使用大括号括住。

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

Go 语言数据类型

布尔型
布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。

数字类型
整型 int 和浮点型 float,Go 语言支持整型和浮点型数字,并且原生支持复数,其中位的运算采用补码。

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

浮点型:float32、float64。complex64(32 位实数和虚数)complex128(64 位实数和虚数

字符串类型:
字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本。

Go 语言变量

Go 语言变量名由字母、数字、下划线组成,其中首个字母不能为数字。

声明变量的一般形式是使用 var 关键字。

第一种,指定变量类型,声明后若不赋值,使用默认值,空指针为nil

var name type
name = value

第二种,根据值自行判定变量类型

var name = value

var e, f = 123, "hello"

第三种,省略var, 注意 :=左侧的变量不应该是已经声明过的,否则会导致编译错误。这种不带声明格式的只能在函数体中出现。由系统自动推断,初始化声明。

name := value//只能在函数体中出现
// 例如
var a int = 10
var b = 10
c : = 10

 第四种,多变量声明(重要)

//类型相同多个变量, 非全局变量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3

var vname1, vname2, vname3 = v1, v2, v3 //和python很像,不需要显示声明类型,自动推断

vname1, vname2, vname3 := v1, v2, v3 //出现在:=左侧的变量不应该是已经被声明过的,否则会导致编译错误


// 这种因式分解关键字的写法一般用于声明全局变量
var (
    vname1 v_type1
    vname2 v_type2
)

值类型和引用类型

所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值。当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝。

如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误(a declared and not used) 但是全局变量是允许声明但不使用。

多变量(已申明)可以在同一行进行赋值,如:

a, b, c = 5, 7, "abc"
//未声明,用:=
a, b, c := 5, 7, "abc"

空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。_ 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。

Go 语言常量

const identifier [type] = value

常量还可以用作枚举:

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

神奇的iota

/*
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
在每一个const关键字出现时,被重置为0,然后再下一个const出现之前,每出现一次iota,其所代表的数字会自动增加1。
iota 可以被用作枚举值:
*/
const (
    a = iota
    b = iota
    c = iota
)

//简写形式
const (
    a = iota
    b
    c
)

const (
            a = iota   //0
            b          //1
            c          //2
            d = "ha"   //独立值,iota += 1
            e          //"ha"   iota += 1
            f = 100    //iota +=1
            g          //100  iota +=1
            h = iota   //7,恢复计数
            i          //8
    )




一个更神奇的用法:

package main

import "fmt"
const (
	i=1<<iota  // i=1<<0 
    j=3<<iota  // j=3<<1
    k          // k=3<<2 12
    l          // j=3<<3 24
)

func main() {
	fmt.Println("i=",i)
	fmt.Println("j=",j)
	fmt.Println("k=",k)
	fmt.Println("l=",l)
}

//以上运行结果为:
i= 1
j= 6
k= 12
l= 24

逻辑运算符&&  ||  !就是判断真假的

位运算符&, |, 和 ^

位运算符对整数在内存中的二进制位进行操作。对一个32位负数取非符号位的其他位a & 0x7fffffff

Go 语言 switch 语句

变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。匹配项后面也不需要再加break,运行完后结束。

//Go 编程语言中 switch 语句的语法如下:
switch var1 {
    case val1:
        ...
    case val2:
        ...
    default:
        ...
}


package main

import "fmt"

func main() {
   /* 定义局部变量 */
   var grade string = "B"
   var marks int = 90

   switch marks {
      case 90: grade = "A"
      case 80: grade = "B"
      case 50,60,70 : grade = "C"
      default: grade = "D"  
   }

   switch {
      case grade == "A" :
         fmt.Printf("优秀!\n" )     
      case grade == "B", grade == "C" :
         fmt.Printf("良好\n" )      
      case grade == "D" :
         fmt.Printf("及格\n" )      
      case grade == "F":
         fmt.Printf("不及格\n" )
      default:
         fmt.Printf("差\n" );
   }
   fmt.Printf("你的等级是 %s\n", grade );      
}

Go 语言 select 语句

select是Go中的一个控制结构,类似于用于通信的switch语句。每个case必须是一个通信操作,要么是发送要么是接收。select随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。一个默认的子句应该总是可运行的。

以下描述了 select 语句的语法:

*每个case都必须是一个通信
*所有channel表达式都会被求值
*所有被发送的表达式都会被求值
*如果任意某个通信可以进行,它就执行;其他被忽略。
*如果有多个case都可以运行,Select会随机公平地选出一个执行。其他不会执行。 
*否则:
*如果有default子句,则执行该语句。
*如果没有default字句,select将阻塞,直到某个通信可以运行;Go不会重新对channel或值进行求值。
package main

import "fmt"

func main() {
   var c1, c2, c3 chan int
   var i1, i2 int
   select {
      case i1 = <-c1:
         fmt.Printf("received ", i1, " from c1\n")
      case c2 <- i2:
         fmt.Printf("sent ", i2, " to c2\n")
      case i3, ok := (<-c3):  // same as: i3, ok := <-c3
         if ok {
            fmt.Printf("received ", i3, " from c3\n")
         } else {
            fmt.Printf("c3 is closed\n")
         }
      default:
         fmt.Printf("no communication\n")
   }    
}

Go 语言 for 循环

搭配break,continue使用;Go语言的For循环有3中形式,只有其中的一种使用分号:

//和 C 语言的 for 一样:
for init; condition; post { }


//和 C 的 while 一样,取代while:
for condition { }


//和 C 的 for(;;) 一样:
for { }

for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。格式如下:

for key, value := range oldMap {
    newMap[key] = value
}

Go 语言函数

Go 语言函数定义格式如下:

func f(参数) (返回值) {
   函数体
}

//返回多个值
func swap(x, y string) (string, string) {
   return y, x
}

/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
   var result int

   if (num1 > num2) {
      result = num1
   } else {
      result = num2
   }
   return result 
}

函数做为值使用:

package main

import (
   "fmt"
   "math"
)

func main(){
   getSquareRoot := func(x float64) float64 {
      return math.Sqrt(x)
   }
   fmt.Println(getSquareRoot(9))

}

Go 语言匿名函数

func(参数列表) (返回值列表) {

       函数体...

 }
    //直接使用,无参数直接加括号
    func() int {
        var i int =5
        fmt.Printf("func 1\n")
        return i
    }()


   //直接使用,有参数,在括号里加参数
    func(arge int)  {

          fmt.Printf("func %d\n",arge)
    }(2)


  //也可以先赋给一个变量再调用
   a := func() int {
          fmt.Printf("func 3\n")
          return 5
     }

   a()

Go 语言数组

  • 数组是值。将一个数组赋予另一个数组会复制其所有元素。
  • 切片保存了对底层数组的引用,若你将某个切片赋予另一个切片,它们会引用同一个数组。
  • 特别地,若将某个数组传入某个函数,它将接收到该数组的一份副本而非指针。
  • 数组的大小是其类型的一部分。类型 [10]int 和 [20]int 是不同的。

Go 语言数组声明需要指定元素类型及元素个数,语法格式如下:

var name [SIZE] type

//var a [10] int

//数组初始化:
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

//如果忽略[]中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小:
var balance = []float32{1000.0, 2.0, 3.4, 7.0, 50.0}

Go 语言多维数组

var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type

//var threedim [5][10][4]int

var a = [3][4]int{  
 {0, 1, 2, 3} ,   /*  第一行索引为 0 */
 {4, 5, 6, 7} ,   /*  第二行索引为 1 */
 {8, 9, 10, 11}   /*  第三行索引为 2 */
}

Go 语言向函数传递数组

//指定大小
void myFunction(param [10]int)
{
.
.
.
}

//不指定大小
void myFunction(param []int)
{
.
.
.
}

//例子
func getAverage(arr []int, size int) float32 {
   var i,sum int
   var avg float32  

   for i = 0; i < size;i++ {
      sum += arr[i]
   }

   avg = float32(sum / size)

   return avg;
}

Go 语言指针

//指针声明格式如下:
var name *type

//打印指针地址%x:
fmt.Printf("变量的地址: %x\n", &a )

//Go 空指针,它的值为 nil
if(ptr == nil)

Go 语言指针数组

声明整型指针数组(数组每个元素都是指针):
var ptr [3]*int;

Go 语言指向指针的指针

var ptr **int;

Go 语言指针作为函数参数

func swap(x *int, y *int) {
   var temp int
   temp = *x    /* 保存 x 地址的值 */
   *x = *y      /* 将 y 赋值给 x */
   *y = temp    /* 将 temp 赋值给 y */

Go 语言结构体

定义结构体和声明变量

//结构体定义需要使用 type 和 struct 语句。
//type 语句设定了结构体的名称。结构体的格式如下:
type my_type struct {
   name1 type1
   name2 type2
   ...
}

一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:
name := my_type {value1, value2...valuen}


//例子:
type Books struct {
   title string
   author string
   subject string
   book_id int
}

var Book1 Books

Book1.title = "Go 语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407

//结构体作为函数参数
func printBook( book Books ) {}

//结构体指针
var ptr *Books

Go 语言切片(Slice)

Go 语言切片是对数组的抽象。Go 数组的长度不可改变。切片("动态数组"),长度是不固定的

切片保存了对底层数组的引用,若你将某个切片赋予另一个切片,它们会引用同一个数组。

声明一个未指定大小的数组来定义切片:
var name []type

或使用make()函数来创建切片:
var slice1 []type = make([]type, len)
或者make([]T, length, capacity)//capacity是可选参数
也可以简写为:
slice1 := make([]type, len)

/************************************/

s :=[] int {1,2,3 }            //直接初始化

s := arr[:]                    //用数组初始化切片

s := arr[startIndex:endIndex]  //新切片

fmt.Printf("slice=%v\n",x)     //输出切片2种方法 结果:slice=[0 0 0]
fmt.Println("slice=", x)

func printSlice(x []int){      //这个函数用于输出切片信息
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

if(numbers == nil){            //判断空切片
      fmt.Printf("切片是空的")
}

切片截取

package main

import "fmt"

func main() {
   /* 创建切片 */
   numbers := []int{0,1,2,3,4,5,6,7,8}   
   printSlice(numbers)

   /* 打印原始切片 */
   fmt.Println("numbers ==", numbers)

   /* 打印子切片从索引1(包含) 到索引4(不包含)*/
   fmt.Println("numbers[1:4] ==", numbers[1:4])

   /* 默认下限为 0*/
   fmt.Println("numbers[:3] ==", numbers[:3])

   /* 默认上限为 len(s)*/
   fmt.Println("numbers[4:] ==", numbers[4:])

   numbers1 := make([]int,0,5)
   printSlice(numbers1)

   /* 打印子切片从索引  0(包含) 到索引 2(不包含) */
   number2 := numbers[:2]
   printSlice(number2)

   /* 打印子切片从索引 2(包含) 到索引 5(不包含) */
   number3 := numbers[2:5]
   printSlice(number3)

}

func printSlice(x []int){
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

切片append()和 copy()

   var numbers []int

   /* 允许追加空切片 */
   numbers = append(numbers, 0)

   /* 向切片添加一个元素 */
   numbers = append(numbers, 1)

   /* 同时添加多个元素 */
   numbers = append(numbers, 2,3,4)

   /* 创建切片 numbers1 是之前切片的两倍容量*/
   numbers1 := make([]int, len(numbers), (cap(numbers))*2)

   /* 拷贝 numbers 的内容到 numbers1 */
   copy(numbers1,numbers)

Go 语言范围(Range)

//range一个切片,返回下标和对应项
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
  sum += num
}

    //range也可以用在map的键值对上。
    kvs := map[string]string{"a": "apple", "b": "banana"}
    for k, v := range kvs {
        fmt.Printf("%s -> %s\n", k, v)
    }

    //range也可以用来枚举Unicode字符串。
    //第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
    for i, c := range "go" {
        fmt.Println(i, c)
    }
    //结果:
    0 103
    1 111
    

Go 语言Map(集合)

Map 是一种无序的键值对的集合。Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。

/* 声明变量,默认 map 是 nil ,nil map 不能用来存放键值对*/
var myname map[ktype]vtype

/* 使用 make 函数,之后才能添加元素 */
myname = make(map[ktype]vtype)
//例子
   var countryCapitalMap map[string]string
   /* 创建集合 */
   countryCapitalMap = make(map[string]string)
   
   /* map 插入 key-value 对,各个国家对应的首都 */
   countryCapitalMap["France"] = "Paris"
   countryCapitalMap["Italy"] = "Rome"
   countryCapitalMap["Japan"] = "Tokyo"
   countryCapitalMap["India"] = "New Delhi"
   
   /* 使用 key 输出 map 值 */
   for country := range countryCapitalMap {
      fmt.Println("Capital of",country,"is",countryCapitalMap[country])
   }
   
   /* 查看元素在集合中是否存在 */
   captial, ok := countryCapitalMap["United States"]
   /* 如果 ok 是 true, 则存在,否则不存在 */
   if(ok){
      fmt.Println("Capital of United States is", captial)  
   }else {
      fmt.Println("Capital of United States is not present") 
   }

map中删除元素

//delete(), 参数为 map 和其对应的 key

/* 删除元素 */
delete(countryCapitalMap,"France");

Go 语言类型转换

type(myname)

   var a int = 17
   var b int = 5
   var c float32
   
   c = float32(a)/float32(b)
   fmt.Printf("c 的值为: %f\n",c)

Go 语言new

Go提供了两种分配原语,即内建函数 new 和 make

new不会初始化内存,只会将内存置零。 也就是说,new(T) 会为类型为 T 的新项分配已置零的内存空间, 并返回它的地址,也就是一个类型为 *T 的值。

Go 语言make

内建函数 make(T, args) 的目的不同于 new(T)

它只用于创建切片、映射和信道,并返回类型为 T(而非 *T)的一个已初始化 (而非置零)的值。 出现这种用差异的原因在于,这三种类型本质上为引用数据类型,它们在使用前必须初始化。

make 只适用于映射、切片和信道且不返回指针。若要获得明确的指针, 请使用 new 分配内存(如下)。

var p *[]int = new([]int)       // 分配切片结构;*p == nil;基本没用
var v  []int = make([]int, 100) // 切片 v 现在引用了一个具有 100 个 int 元素的新数组

// 没必要的复杂:
var p *[]int = new([]int)
*p = make([]int, 100, 100)

// 习惯用法:
v := make([]int, 100)

Go 错误处理

Go 语言通过内置的错误接口提供了非常简单的错误处理机制。

我们可以在编码中通过实现 error 接口类型来生成错误信息。

函数通常在最后的返回值中返回错误信息。

error类型是一个接口类型,这是它的定义:

type error interface {
    Error() string
}
package main

import (
	"fmt"
)

// 定义一个 DivideError 结构
type DivideError struct {
	dividee int
	divider int
}

// 实现`error`接口
func (de *DivideError) Error() string {
	strFormat := `
	Cannot proceed, the divider is zero.
	dividee: %d
	divider: 0
`
	return fmt.Sprintf(strFormat, de.dividee)
}

// 定义 `int` 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
	if varDivider == 0 {
		dData := DivideError{
			dividee: varDividee,
			divider: varDivider,
		}
		errorMsg = dData.Error()
		return
	} else {
		return varDividee / varDivider, ""
	}

}

func main() {

	// 正常情况
	if result, errorMsg := Divide(100, 10); errorMsg == "" {
		fmt.Println("100/10 = ", result)
	}
	// 当被除数为零的时候会返回错误信息
	if _, errorMsg := Divide(100, 0); errorMsg != "" {
		fmt.Println("errorMsg is: ", errorMsg)
	}

}

 


有疑问加站长微信联系(非本文作者)

本文来自:开源中国博客

感谢作者:曾劲松

查看原文:go语言学习

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1457 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传