go语言语法基础

63 阅读10分钟

www.runoob.com/go/go-basic…

Go 语言数据类型

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

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

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

序号类型和描述
1布尔型 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。
2数字类型 整型 int 和浮点型 float32、float64,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。

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

浮点型

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

其他数字类型

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

Go 语言变量

vname1, vname2, vname3 := v1, v2, v3 // 出现在 := 左侧的变量不应该是已经被声明过的,否则会导致编译错误
//类型相同多个变量, 非全局变量
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
)

值类型和引用类型

Go 语言常量

package main  
  
import "unsafe"  
const (  
    a = "abc"  
    b = len(a)  
    c = unsafe.Sizeof(a)  
)  
  
func main(){  
    println(a, b, c)  
}

Go 语言运算符

同其他语言

Go 语言条件语句

Go 语言条件语句

/* 函数返回两个数的最大值 */  
func max(num1, num2 intint {  
   /* 声明局部变量 */  
   var result int  
  
   if (num1 > num2) {  
      result = num1  
   } else {  
      result = num2  
   }  
   return result  
}

Go 语言变量作用域


func main() {  
  
   /* 声明局部变量 */  
   var ab int  
  
   /* 初始化参数 */  
   a = 10  
   b = 20  
   g = a + b  
  
   fmt.Printf("结果: a = %d, b = %d and g = %d\n", ab, g)  
}

Go 语言数组

var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
package main  
  
import "fmt"  
  
func main() {  
   var i,j,k int  
   // 声明数组的同时快速初始化数组  
   balance := [5]float32{1000.02.03.47.050.0}  
  
   /* 输出数组元素 */         ...  
   for i = 0i < 5i++ {  
      fmt.Printf("balance[%d] = %f\n", i, balance[i] )  
   }  
     
   balance2 := [...]float32{1000.02.03.47.050.0}  
   /* 输出每个数组元素的值 */  
   for j = 0; j < 5; j++ {  
      fmt.Printf("balance2[%d] = %f\n", j, balance2[j] )  
   }  
  
   //  将索引为 13 的元素初始化  
   balance3 := [5]float32{1:2.0,3:7.0}    
   for k = 0; k < 5; k++ {  
      fmt.Printf("balance3[%d] = %f\n", k, balance3[k] )  
   }  
}

Go 语言指针

package main  
  
import "fmt"  
  
func main() {  
   var a int20   /* 声明实际变量 */  
   var ip *int        /* 声明指针变量 */  
  
   ip = &a  /* 指针变量的存储地址 */  
  
   fmt.Printf("a 变量的地址是: %x\n", &a  )  
  
   /* 指针变量的存储地址 */  
   fmt.Printf("ip 变量储存的指针地址: %x\n", ip )  
  
   /* 使用指针访问值 */  
   fmt.Printf("*ip 变量的值: %d\n", *ip )  
}

Go 空指针

当一个指针被定义后没有分配到任何变量时,它的值为 nil。

nil 指针也称为空指针。

nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。

一个指针变量通常缩写为 ptr。

查看以下实例:

实例

package main

import "fmt"

func main() {
var  ptr *int

   fmt.Printf("ptr 的值为 : %x\n", ptr  )
}

Go 语言结构体

type Books struct {  
   title string  
   author string  
   subject string  
   book_id int  
}

func main() {  
   var Book1 Books        /* 声明 Book1 为 Books 类型 */  
   var Book2 Books        /* 声明 Book2 为 Books 类型 */  
  
   /* book 1 描述 */  
   Book1.title = "Go 语言"  
   Book1.author = "www.runoob.com"  
   Book1.subject = "Go 语言教程"  
   Book1.book_id = 6495407
   

Go 语言切片(Slice)

Go 语言切片是对数组的抽象。 Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

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)  
}
package main  
  
import "fmt"  
  
func main() {  
   var numbers []int  
   printSlice(numbers)  
  
   /* 允许追加空切片 */  
   numbers = append(numbers, 0)  
   printSlice(numbers)  
  
   /* 向切片添加一个元素 */  
   numbers = append(numbers, 1)  
   printSlice(numbers)  
  
   /* 同时添加多个元素 */  
   numbers = append(numbers, 2,3,4)  
   printSlice(numbers)  
  
   /* 创建切片 numbers1 是之前切片的两倍容量*/  
   numbers1 := make([]int, len(numbers), (cap(numbers))*2)  
  
   /* 拷贝 numbers 的内容到 numbers1 */  
   copy(numbers1,numbers)  
   printSlice(numbers1)    
   
}  
  
func printSlice(x []int){  
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)  
}

Go 语言范围(Range)

遍历简单的数组,2**%d 的结果为 2 对应的次方数:

实例

package main  
  
import "fmt"  
  
var pow = []int{1248163264128}  
  
func main() {  
   for i, v := range pow {  
      fmt.Printf("2**%d = %d\n", i, v)  
   }  
}  

以上实例运行输出结果为:

2**0 = 1
2**1 = 2
2**2 = 4
2**3 = 8
2**4 = 16
2**5 = 32
2**6 = 64
2**7 = 128

range 遍历其他数据结构:

实例

package main  
import "fmt"  
func main() {  
    //这是我们使用 range 去求一个 slice 的和。使用数组跟这个很类似  
    nums := []int{234}  
    sum := 0  
    for _, num := range nums {  
        sum += num  
    }  
    fmt.Println("sum:", sum)  
    //在数组上使用 range 将传入索引和值两个变量。上面那个例子我们不需要使用该元素的序号,所以我们使用空白符"_"省略了。有时侯我们确实需要知道它的索引。  
    for i, num := range nums {  
        if num == 3 {  
            fmt.Println("index:", i)  
        }  
    }  
    //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)  
    }  
}  

以上实例运行输出结果为:

sum: 9
index: 1
a -> apple
b -> banana
0 103
1 111

Go 语言Map(集合)

// 创建一个空的 Map  
m := make(map[string]int)  
  
// 创建一个初始容量为 10 的 Map  
m := make(map[string]int10)  

也可以使用字面量创建 Map:

// 使用字面量创建 Map
m := map[string]int{
    "apple": 1,
    "banana": 2,
    "orange": 3,
}

遍历 Map:

// 遍历 Map
for k, v := range m {
    fmt.Printf("key=%s, value=%d\n", k, v)
}

删除元素:

// 删除键值对
delete(m, "banana")

下面实例演示了创建和使用map:

实例

package main

import "fmt"

func main() {
var siteMap map[string]string /*创建集合 */
siteMap = make(map[string]string)

    /* map 插入 key - value 对,各个国家对应的首都 */
siteMap [ "Google" ] = "谷歌"
siteMap [ "Runoob" ] = "菜鸟教程"
siteMap [ "Baidu" ] = "百度"
siteMap [ "Wiki" ] = "维基百科"

    /*使用键输出地图值 */
for site := range siteMap {
fmt.Println(site, "首都是", siteMap [site])
}

    /*查看元素在集合中是否存在 */
name, ok := siteMap [ "Facebook" ] /*如果确定是真实的,则存在,否则不存在 */
/*fmt.Println(capital) */
/*fmt.Println(ok) */
if (ok) {
fmt.Println("Facebook 的 站点是", name)
} else {
fmt.Println("Facebook 站点不存在")
}
}

以上实例运行结果为:

Wiki 首都是 维基百科
Google 首都是 谷歌
Runoob 首都是 菜鸟教程
Baidu 首都是 百度
Facebook 站点不存在

delete() 函数

delete() 函数用于删除集合的元素, 参数为 map 和其对应的 key。实例如下:

实例

package main  
  
import "fmt"  
  
func main() {  
        /* 创建map */  
        countryCapitalMap := map[string]string{"France""Paris""Italy""Rome""Japan""Tokyo""India""New delhi"}  
  
        fmt.Println("原始地图")  
  
        /* 打印地图 */  
        for country := range countryCapitalMap {  
                fmt.Println(country, "首都是", countryCapitalMap [ country ])  
        }  
  
        /*删除元素*/ delete(countryCapitalMap, "France")  
        fmt.Println("法国条目被删除")  
  
        fmt.Println("删除元素后地图")  
  
        /*打印地图*/  
        for country := range countryCapitalMap {  
                fmt.Println(country, "首都是", countryCapitalMap [ country ])  
        }  
}  

以上实例运行结果为:

原始地图
India 首都是 New delhi
France 首都是 Paris
Italy 首都是 Rome
Japan 首都是 Tokyo
法国条目被删除
删除元素后地图
Italy 首都是 Rome
Japan 首都是 Tokyo
India 首都是 New delhi

Go 语言类型转换

字符串类型转换

将一个字符串转换成另一个类型,可以使用以下语法:

func main() {  
   var sum int = 17  
   var count int = 5  
   var mean float32  
     
   mean = float32(sum)/float32(count)  
   fmt.Printf("mean 的值为: %f\n",mean)  
}
var str string = "10"
var num int
num, _ = strconv.Atoi(str)

func main() {
var sum int = 17
var count int = 5
var mean float32

mean = float32(sum)/float32(count)
fmt.Printf("mean 的值为: %f\n",mean)
}

接口类型转换

接口类型转换有两种情况 :类型断言类型转换

类型断言用于将接口类型转换为指定类型,其语法为:

value.(type) 
或者 
value.(T)

其中 value 是接口类型的变量,type 或 T 是要转换成的类型。

如果类型断言成功,它将返回转换后的值和一个布尔值,表示转换是否成功。

实例

package main

import "fmt"

func main() {
var i interface{} = "Hello, World"
str, ok := i.(string)
if ok {
fmt.Printf("'%s' is a string\n", str)
} else {
fmt.Println("conversion failed")
}
}

以上实例中,我们定义了一个接口类型变量 i,并将它赋值为字符串 "Hello, World"。然后,我们使用类型断言将 i 转换为字符串类型,并将转换后的值赋值给变量 str。最后,我们使用 ok 变量检查类型转换是否成功,如果成功,我们打印转换后的字符串;否则,我们打印转换失败的消息。

类型转换用于将一个接口类型的值转换为另一个接口类型,其语法为:

T(value)

T 是目标接口类型,value 是要转换的值。

在类型转换中,我们必须保证要转换的值和目标接口类型之间是兼容的,否则编译器会报错。

Go 语言接口

Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。

接口可以让我们将不同的类型绑定到一组公共的方法上,从而实现多态和灵活的设计。

Go 语言中的接口是隐式实现的,也就是说,如果一个类型实现了一个接口定义的所有方法,那么它就自动地实现了该接口。因此,我们可以通过将接口作为参数来实现对不同类型的调用,从而实现多态。

实例

实例

/* 定义接口 */
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
...
method_namen [return_type]
}

/* 定义结构体 /
type struct_name struct {
/
variables */
}

/* 实现接口方法 /
func (struct_name_variable struct_name) method_name1() [return_type] {
/
方法实现 /
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
/
方法实现*/
}

实例

以下两个实例演示了接口的使用:

实例 1

package main  
  
import (  
    "fmt"  
)  
  
type Phone interface {  
    call()  
}  
  
type NokiaPhone struct {  
}  
  
func (nokiaPhone NokiaPhone) call() {  
    fmt.Println("I am Nokia, I can call you!")  
}  
  
type IPhone struct {  
}  
  
func (iPhone IPhone) call() {  
    fmt.Println("I am iPhone, I can call you!")  
}  
  
func main() {  
    var phone Phone  
  
    phone = new(NokiaPhone)  
    phone.call()  
  
    phone = new(IPhone)  
    phone.call()  
  
}  

在上面的例子中,我们定义了一个接口 Phone,接口里面有一个方法 call()。然后我们在 main 函数里面定义了一个 Phone 类型变量,并分别为之赋值为 NokiaPhone 和 IPhone。然后调用 call() 方法,输出结果如下:

I am Nokia, I can call you!
I am iPhone, I can call you!

第二个接口实例:

实例

package main  
  
import "fmt"  
  
type Shape interface {  
    area() float64  
}  
  
type Rectangle struct {  
    width  float64  
    height float64  
}  
  
func (r Rectangle) area() float64 {  
    return r.width * r.height  
}  
  
type Circle struct {  
    radius float64  
}  
  
func (c Circle) area() float64 {  
    return 3.14 * c.radius * c.radius  
}  
  
func main() {  
    var s Shape  
  
    s = Rectangle{width: 10, height: 5}  
    fmt.Printf("矩形面积: %f\n", s.area())  
  
    s = Circle{radius: 3}  
    fmt.Printf("圆形面积: %f\n", s.area())  
}  

以上实例中,我们定义了一个 Shape 接口,它定义了一个方法 area(),该方法返回一个 float64 类型的面积值。然后,我们定义了两个结构体 Rectangle 和 Circle,它们分别实现了 Shape 接口的 area() 方法。在 main() 函数中,我们首先定义了一个 Shape 类型的变量 s,然后分别将 Rectangle 和 Circle 类型的实例赋值给它,并通过 area() 方法计算它们的面积并打印出来,输出结果如下:

矩形面积: 50.000000
圆形面积: 28.260000

需要注意的是,接口类型变量可以存储任何实现了该接口的类型的值。在示例中,我们将 Rectangle 和 Circle 类型的实例都赋值给了 Shape 类型的变量 s,并通过 area() 方法调用它们的面积计算方法。

Go 错误处理

在下面的例子中,我们在调用 Sqrt 的时候传递的一个负数,然后就得到了 non-nil 的 error 对象,将此对象与 nil 比较,结果为 true,所以 fmt.Println(fmt 包在处理 error 时会调用 Error 方法)被调用,以输出错误,请看下面调用的示例代码:

result, err:= Sqrt(-1)

if err != nil {
   fmt.Println(err)
}

Go 错误处理

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

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

type error interface {
    Error() string
}

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

函数通常在最后的返回值中返回错误信息。使用 errors.New 可返回一个错误信息:

func Sqrt(f float64) (float64, error) {
    if f < 0 {
        return 0, errors.New("math: square root of negative number")
    }
    // 实现
}

在下面的例子中,我们在调用 Sqrt 的时候传递的一个负数,然后就得到了 non-nil 的 error 对象,将此对象与 nil 比较,结果为 true,所以 fmt.Println(fmt 包在处理 error 时会调用 Error 方法)被调用,以输出错误,请看下面调用的示例代码:

result, err:= Sqrt(-1)

if err != nil {
   fmt.Println(err)
}

实例

实例

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(10010); errorMsg == "" {  
            fmt.Println("100/10 = ", result)  
    }  
    // 当除数为零的时候会返回错误信息  
    if _, errorMsg := Divide(1000); errorMsg != "" {  
            fmt.Println("errorMsg is: ", errorMsg)  
    }  
  
}  


执行以上程序,输出结果为:

100/10 = 10 errorMsg is:
Cannot proceed, the divider is zero.     dividee: 100     divider: 0