go基本语法

go基本语法

数字类型

Go 语言支持整型和浮点型数字,并且原生支持复数,其中位的运算采用补码(详情参见 二的补码 页面)。

Go 也有基于架构的类型,例如:intuintuintptr

这些类型的长度都是根据运行程序所在的操作系统类型所决定的:

  • intuint 在 32 位操作系统上,它们均使用 32 位(4 个字节),在 64 位操作系统上,它们均使用 64 位(8 个字节)。
  • uintptr 的长度被设定为足够存放一个指针即可。

Go 语言中没有 float 类型。(Go语言中只有 float32float64)没有 double 类型。

与操作系统架构无关的类型都有固定的大小,并在类型的名称中就可以看出来:

整数:

  • int8(-128 -> 127)
  • int16(-32768 -> 32767)
  • int32(-2,147,483,648 -> 2,147,483,647)
  • int64(-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807)

无符号整数:

  • uint8(0 -> 255)
  • uint16(0 -> 65,535)
  • uint32(0 -> 4,294,967,295)
  • uint64(0 -> 18,446,744,073,709,551,615)

浮点型(IEEE-754 标准):

  • float32(± 1e-45 -> ± 3.4 * 1e38)
  • float64(± 5 * 1e-324 -> 107 * 1e308)

int 型是计算最快的一种类型。

整型的零值为 0,浮点型的零值为 0.0

float32 精确到小数点后 7 位,float64 精确到小数点后 15 位

你应该尽可能地使用 float64,因为 math 包中所有有关数学运算的函数都会要求接收这个类型。

你可以通过增加前缀 0 来表示 8 进制数(如:077),增加前缀 0x 来表示 16 进制数(如:0xFF),以及使用 e 来表示 10 的连乘(如: 1e3 = 1000,或者 6.022e23 = 6.022 x 1e23)。

你可以使用 a := uint64(0) 来同时完成类型转换和赋值操作,这样 a 的类型就是 uint64

Go 中不允许不同类型之间的混合使用,但是对于常量的类型限制非常少,因此允许常量之间的混合使用

复数

两种类型:complex64;complex128

格式化说明符:%v

func main() {
	var c1 complex64 = 5 + 10i
	var c2 complex128 = 5 - 10i
	var re float32 = 2
	var im float32 = 3
	var c = complex(re, im)
	fmt.Println(c1)
    fmt.Println(real(c1), imag(c2))
	fmt.Printf("%v %v", c2, c)
}

输出

(5+10i)
5 -10
(5-10i) (2+3i)

格式化说明符

在格式化字符串里,%d 用于格式化整数(%x%X 用于格式化 16 进制表示的数字),%g 用于格式化浮点型(%f 输出浮点数,%e 输出科学计数表示法),%0nd 用于规定输出长度为 n 的整数,其中开头的数字 0 是必须的。

%n.mg 用于表示数字 n 并精确到小数点后 m 位,除了使用 g 之外,还可以使用 e 或者 f,例如:使用格式化字符串 %5.2e 来输出 3.4 的结果为 3.40e+00

实例

package main

import "fmt"

func main() {
	var n int16 = 34
	var m int32
	// compiler error: cannot use n (type int16) as type int32 in assignment
	//m = n
	m = int32(n)

	fmt.Printf("32 bit int is: %d\n", m)
	fmt.Printf("16 bit int is: %d\n", n)
}

输出

32 bit int is: 34
16 bit int is: 34

类型

package main

import (
	"fmt"
	"math"
)

func main() {
	var a = "initial" //可以省略类型说明符
	var b, c int = 1, 2
	var d = true
	var e float64
	f := float32(e)
	g := a + "foo"
	fmt.Println(a, b, c, d, e, f)
	fmt.Println(g)

	const s string = "constant"
	const h = 500000
	const l = 3e20 / h
	fmt.Println(s, h, l, math.Sin(h), math.Sin(l))
}

输出

initial 1 2 true 0 0
initialfoo
constant 500000 6e+14 0.17783120151825887 0.9538537219686818

变量(或常量)包含数据,这些数据可以有不同的数据类型,简称类型。使用 var 声明的变量的值会自动初始化为该类型的零值。类型定义了某个变量的值的集合与可对其进行操作的集合。

类型可以是基本类型,如:intfloatboolstring;结构化的(复合的),如:structarray、切片 (slice)、map、通道 (channel);只描述类型的行为的,如:interface

结构化的类型没有真正的值,它使用 nil 作为默认值

值得注意的是,Go 语言中不存在类型继承。

函数

可以是一个确定的类型

就是以函数作为返回类型。这种类型的声明要写在函数名和可选的参数列表之后,例如:

func FunctionName (a typea, b typeb) typeFunc

你可以在函数体中的某处返回使用类型为 typeFunc 的变量 var

return var
可拥有多种返回值

返回类型之间需要使用逗号分割,并使用小括号 () 将它们括起来,如:

func FunctionName (a typea, b typeb) (t1 type1, t2 type2)

返回的形式:

return var1, var2

这种多返回值一般用于判断某个函数是否执行成功 (true/false) 或与其它返回值一同返回错误消息(详见之后的并行赋值)。

type关键字

使用 type 关键字可以定义你自己的类型,也可以定义一个已经存在的类型的别名,如:

type IZ int

这里并不是真正意义上的别名,因为使用这种方法定义之后的类型可以拥有更多的特性,且在类型转换时必须显式转换。

然后我们可以使用下面的方式声明变量:

var a IZ = 5

这里我们可以看到 int 是变量 a 的底层类型,这也使得它们之间存在相互转换的可能

如果你有多个类型需要定义,可以使用因式分解关键字的方式,例如:

type (
   IZ int
   FZ float64
   STR string
)

每个值都必须在经过编译后属于某个类型(编译器必须能够推断出所有值的类型),因为 Go 语言是一种静态类型语言。

类型转换

go不存在隐式转换

只能在定义正确的情况下成功,例如从取值范围小到大;精度从小到大

具有相同底层类型的变量之间可以相互转换

a := 5.0
b := int(a)

常量

一个没有指定类型的常量被使用时,会根据其使用环境而推断出它所需要具备的类型。

换句话说,未定义类型的常量会在必要时刻根据上下文来获得相关类型。

const PI=3,1415
var n int
f(n + 5) // 无类型的数字型常量 “5” 它的类型在这里变成了 int

因为在编译期间自定义函数均属于未知,因此无法用于常量的赋值,但内置函数可以使用,如:len()

var c = "string"
const c = getnum() //未定义,所以是错误的
const c2 = len(s)

当常量赋值给一个精度过小的数字型变量时,可能会因为无法正确表达常量所代表的数值而导致溢出,这会在编译期间就引发错误。另外,常量也允许使用并行赋值的形式

const beef, two, c = "eat", 2, "veg"
const Monday, Tuesday, Wednesday, Thursday, Friday, Saturday = 1, 2, 3, 4, 5, 6
//常量可以进行枚举
const (
	Monday, Tuesday, Wednesday = 1, 2, 3
	Thursday, Friday, Saturday = 4, 5, 6
)

常量枚举

类似于c++当中的enum

关于iota:每遇到一个新的常量块或单个常量声明(每次遇到一个const关键字)iota都重置为0

const (
	a=iota //0
    b=iota //1
    c=iota //2
    d=5
    e //5
)

// 赋值两个常量,iota 只会增长一次,而不会因为使用了两次就增长两次
const (
	Apple, Banana = iota + 1, iota + 2 // Apple=1 Banana=2
	Cherimoya, Durian                  // Cherimoya=2 Durian=3
	Elderberry, Fig                    // Elderberry=3, Fig=4
)
// 使用 iota 结合 位运算 表示资源状态的使用案例
const (
	Open = 1 << iota  // 0001
	Close             // 0010
	Pending           // 0100
)

const (
	_           = iota             // 使用 _ 忽略不需要的 iota
	KB = 1 << (10 * iota)          // 1 << (10*1)
	MB                             // 1 << (10*2)
	GB                             // 1 << (10*3)
	TB                             // 1 << (10*4)
	PB                             // 1 << (10*5)
	EB                             // 1 << (10*6)
	ZB                             // 1 << (10*7)
	YB                             // 1 << (10*8)
)

变量-var

变量的类型放在变量的名称后面,防止类型分不清

所有的内存在 Go 中都是经过初始化的,字符串指针为nil,其余的则为0

var a, b *int

var a int
var b bool
var str string
//全局变量
var (
	a int
	b bool
	str string
)

不申明类型,go可以通过值来自行推断

var a = 15
var b = false
var str = "Go says hello to the world!"

在函数体内声明局部变量

这是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。

a := 1

声明包级别的全局变量

var (
	HOME = os.Getenv("HOME")
	USER = os.Getenv("USER")
	GOROOT = os.Getenv("GOROOT") //获取环境变量中的值
)

在 Go 语言中,指针属于引用类型,其它的引用类型还包括 slices,maps和 channel。被引用的变量会存储在堆中,以便进行垃圾回收,且比栈拥有更大的内存空间

一些省略写法

//并行赋值
//并行赋值也被用于当一个函数返回多个返回值时,比如这里的 `val` 和错误 `err` 是通过调用 `Func1` 函数同时得到:`val, err = Func1(var1)`。
a, b, c := 5, 7, "abc"
//交换值
a,b=b,a

抛弃值

_ 实际上是一个只写变量,你不能得到它的值。这样做是因为你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值

_,b=5,7

易错

package main

import (
   "fmt"
   "./trans"
)

var twoPi = 2 * trans.Pi

func main() {
   fmt.Printf("2*Pi = %g\n", twoPi) // 2*Pi = 6.283185307179586
}

输出

原因: ':='不用于全局变量的声明

G00

Bool值

可以使用 %t 来表示你要输出的值为布尔型。

var aVar = 10
aVar != 5 -> true
aVar != 10 -> false

位运算

条件:只能用于整数;且拥有等长位模式

%b 是用于表示位的格式化标识符

二元运算符

  • 按位与 &:有0则0

  • 按位或 |:有1则1

  • 按位异或 ^:同为0,异为1

  • 位清除 &^:将指定位置上的值设置为 0

    package main
    import "fmt"
    func main() {
    	var x uint8 = 15
    	var y uint8 = 4
    	fmt.Printf("%08b\n", x &^ y);  // 00001011
    }
    

一元运算符

  • 按位补足 ^

    该运算符与异或运算符一同使用,即 m^x,对于无符号 x 使用 “全部位设置为 1” 的规则,对于有符号 x 时使用 m=-1。例如:

    ^10 = -01 ^ 10 = -11
    
  • 位左移 <<

    • 用法:bitP << n
    • bitP 的位向左移动 n 位,右侧空白部分使用 0 填充;如果 n 等于 2,则结果是 2 的相应倍数,即 2 的 n 次方。例如:
  • 位右移 >>

    • 用法:bitP >> n
    • bitP 的位向右移动 n 位,左侧空白部分使用 0 填充;如果 n 等于 2,则结果是当前值除以 2 的 n 次方。

当希望把结果赋值给第一个操作数时,可以简写为 a <<= 2 或者 b ^= a & 0xffffffff

算术运算符

常见可用于整数和浮点数的二元运算符有 +-*/

(相对于一般规则而言,Go 在进行字符串拼接时允许使用对运算符 + 的重载,但 Go 本身不允许开发者进行自定义的运算符重载)

/ 对于整数运算而言,结果依旧为整数,例如:9 / 4 -> 2

取余运算符只能作用于整数:9 % 4 -> 1

整数除以 0 可能导致程序崩溃,将会导致运行时的恐慌状态(如果除以 0 的行为在编译时就能被捕捉到,则会引发编译错误);[第 13 章](file:///D:/self/资料/go/the-way-to-go/eBook/13.0.md) 将会详细讲解如何正确地处理此类情况。

浮点数除以 0.0 会返回一个无穷尽的结果,使用 +Inf 表示

你可以将语句 b = b + a 简写为 b += a,同样的写法也可用于 -=*=/=%=

对于整数和浮点数,你可以使用一元运算符 ++(递增)和 --(递减)

同时,带有 ++-- 的只能作为语句,而非表达式,因此 n = i++ 这种写法是无效的,其它像 f(i++) 或者 a[i]=b[i++] 这些可以用于 C、C++ 和 Java 中的写法在 Go 中也是不允许的。

在运算时 溢出 不会产生错误,Go 会简单地将超出位数抛弃

需要范围无限大的整数或者有理数(意味着只被限制于计算机内存),你可以使用标准库中的 big 包,该包提供了类似 big.Intbig.Rat 这样的类型

运算符优先级

优先级 	运算符
 7 		^ !
 6 		* / % << >> & &^
 5 		+ - | ^
 4 		== != < <= >= >
 3 		<-
 2 		&&
 1 		||

ASCII

var ch byte = 65 
var ch byte = '\x41'

IF语句

判断条件不加括号,if之后必须加{},其余跟c类似

package main

import "fmt"

func main() {
	if 7%2 == 0 {
		fmt.Println("7 is even")
	} else {
		fmt.Println("7 is odd")
	}
}

for loop

go语言中没有while系列循环,只有for

package main

import "fmt"

func main() {
	for {
		fmt.Println("loop")
		break
	}

	for j := 7; j < 9; j++ {
		fmt.Println(j)
	}
	
	for n := 0; n < 5; n++ {
		if n%2 == 0 {
			fmt.Println(n)
			continue
		}
	}
}

输出

loop
7
8
0
2
4

switch

变量可以不仅仅是数字,也可以是字符串

不需要加break

package main

import (
	"fmt"
	"time"
)

func main() {
	a := 2
	switch a {
	case 1:
		fmt.Println("one")
	case 2:
		fmt.Println("two")
	case 3:
		fmt.Println("three")
	case 4, 5:
		fmt.Println("four or five")
	default:
		fmt.Println("other")
	}
	
	t := time.Now()
	switch {
	case t.Hour() < 12:
		fmt.Println("It's befor noon")
	default:
		fmt.Println("It's afternoon")
	}
}

输出

two
It's afternoon

切片

package main

import "fmt"

func main() {
	s := make([]string, 3)
	s[0] = "a"
	s[1] = "b"
	s[2] = "c"
	fmt.Println("get:", s[2])
	fmt.Println("len:", len(s))

	s = append(s, "d")
	s = append(s, "e", "f")
	fmt.Println(s)

	c := make([]string, len(s))
	copy(c, s)
	fmt.Println(c)

	fmt.Println(s[2:5])
	fmt.Println(s[:5])
	fmt.Println(s[2:])

	good := []string{"g", "o", "o", "d"}
	fmt.Println(good)
}

输出

get: c
len: 3
[a b c d e f]
[a b c d e f]
[c d e]
[a b c d e]
[c d e f]
[g o o d]

map

随机顺序

package main

import "fmt"

func main() {
	m := make(map[string]int) //类似于一个类型配另一个类型(可相同)
	m["one"] = 1
	m["two"] = 2
	fmt.Println(m, len(m), m["one"], m["unknown"])

	r, ok := m["unknown"]
	fmt.Println(r, ok)

	delete(m, "one")

	m2 := map[string]int{"one": 1, "two": 2}
	var m3 = map[string]int{"one": 1, "two": 2}
	fmt.Println(m2, m3)
}

输出

map[one:1 two:2] 2 1 0
0 false
map[one:1 two:2] map[one:1 two:2]

range

package main

import "fmt"

func main() {
	nums := []int{2, 3, 4}
	sum := 0
	for i, num := range nums {
		sum += sum
		if sum == 2 {
			fmt.Println("index:", i, "num:", num)
		}
	}
	fmt.Println(sum)

	m := map[string]string{"a": "A", "b": "B"}
	for k, v := range m {
		fmt.Println(k, v)
	}
	for k := range m {
		fmt.Println("key", k)
	}
}

输出

0
a A
b B
key b
key a

函数

package main

import "fmt"

func add(a int, b int) int { //数据类型后置
	return a + b
}

func add2(a, b int) int {//类型相同可省略
	return a + b
}

func exists(m map[string]string, k string) (v string, ok bool) {
	v, ok = m[k]
	return v, ok
}

func main() {
	res := add(1, 2)
	fmt.Println(res)
	v, ok := exists(map[string]string{"a": "A"}, "a")
	fmt.Println(v, ok)
}

输出

3
A true

指针

对传入的参数作出修改

package main

import "fmt"

func add(a *int) {
	*a += 2
}

func add2(b int) {
	b += 2
}

func main() {
	n := 5
	add2(n)
	fmt.Println(n)
	add(&n)
	fmt.Println(n)
}

输出

5
7

结构体

package main

import (
	"fmt"
)

type user struct {
	name     string
	password string
}

func checkpassword(u user, password string) bool {
	return u.password == password
}

func check2(u *user, password string) bool {
	return u.password == password
}

func main() {
	a := user{name: "wang", password: "1024"}
	b := user{"wang", "1024"}
	c := user{name: "wang"}
	c.password = "1024"
	var d user
	d.name = "wang"
	d.password = "1024"

	fmt.Println(a, b, c, d)
	fmt.Println(checkpassword(a, "haha"))
	fmt.Println(check2(&a, "haha"))
}

输出

{wang 1024} {wang 1024} {wang 1024} {wang 1024}
false
false

类成员函数

package main

import "fmt"

type user struct {
	name     string
	password string
}

func (u *user) check2(password string) bool {
	return u.password == password
}

func (u *user) reset(password string) { //从一个普通函数变成类成员函数
	u.password = password
}

func main() {
	a := user{name: "wang", password: "1024"}
	a.reset("2048")
	fmt.Println(a.check2("2048"))
}

输出

true

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值