Golang 基础知识

发布时间: 更新时间: 总字数:4146 阅读时间:9m 作者: 分享 复制网址

本文通过Golang helloworld示例开始,介绍Golang的标识符、关键字、操作符、常量、变量、指针等基础概念。

hello world

  • main.go
// package info
package main

import "fmt"

/*
  main func
*/
func main() {
  // print
	fmt.Println("hello world!")
}
  • 指定文件编译运行,必须包含 main 函数
$ go build main.go
$ ./main
hello world!

# 显示编译过程
go build -x ./main.go

# 指定编译包名称
go build -o helloworld main.go

# 编译并运行
go run ./main.go
go run -x ./main.go  # 指定临时目录编译
  • 编译目录,必须包含 main 函数,使用 go mod,并安装依赖,正常情况下编译包的名称为 go.mod 中 module 的名称
$ go mod init main
go: creating new go.mod: module main
go: to add module requirements and sums:
        go mod tidy
$ go mod tidy
$ go build
$ ls
go.mod  main    main.go
$ ./main
hello world!

基本元素

标识符

标识符:用于给包名、函数、变量、常量、类型、接口等命名,命名规则如下:

  • 由字母(unicode字符)、数字、下划线(_)组成,区分大小写
  • 以字母、下划线开头
  • 不能是Go语言的关键字、以及预定义的标识符
  • 方法名推荐使用驼峰式,尽量使用英文字符
  • 可以使用中文定义变量名,不推荐
  • 包名推荐全部小写,以 .go 结尾

关键字

关键字(共25个)

  • 声明:
    • import:导包
      • 一般顺序为:
        • Golang 内置包
        • 第三方包
        • 项目包
      • 点操作
        • import . "fmt"
        • Println("hello world")
      • 别名
        • import( f "fmt")
        • f.Println("hello world")
      • _ 操作
        • import (_ "github.com/go-sql-driver/mysql")
    • package
  • 实体声明和定义:
    • chan
    • 声明常量:const
    • 声明函数:func
    • 声明接口:interface
    • map
    • 声明结构体:struct
    • 声明类型:type
    • 声明变量:var
  • 流程控制:break case continue default defer if else for go goto fallthrough range return select switch

预定义的标识符

预定义的标识符:

  • 内置常量:true false nil iota(用于枚举类型)
  • 内置类型:
    • bool:
      • 布尔类型,表示真假;长度一个字节(8位)。零值为 false,字面量只能是 true、false
      • 逻辑运算:
        • 与 &&(aBool && bBool),两个均为 true 时,值为 true
        • 或 ||(aBool || bBool),一个值为 true 时,值为 true
        • 非 !(!aBool),aBool 的相反值
      • 关系运算:
        • 等于 == (true == true) 值为 true
        • 不等于 != (true != true) 值为 false
    • 有符号:int int8(0~2^8-1) int16 int32 int64
    • 无符号:uint uint8(-2^7~2^7-1) uint16 uint32 uint64
    • 1个Unicode码点:rune(将字符串转化成unicode 码点),使用单引号赋值。示例:var r rune = '中'
      • rune is an alias for int32 and is equivalent to int32 in all ways. It is used, by convention, to distinguish character values from integer values.
    • 1个字节(8位):byte,使用单引号赋值。示例:var a byte = 'A'
    • 存储指针的整型:uintptr
    • 说明
      • int uint uintptr 是32位操作系统为int32(4个字节),64位系统为int64(8个字节)
      • 有符号与无符号的区别:
        • 有符号最高位0表示整数、1表示负数
        • 无符号均为正数
    • 浮点数,零值为 0,浮点数存储是有精度损耗的(2.5==2.499999…)
      • float32
      • float64
      • 说明:
        • 字面量:
          • 十进制表示法
          • 科学技术法:MEN = M * 10 ^ N,示例:2.5e-1 == 0.25
        • 算术运算符:+,-,*,/,++,–
        • 关系运算:>,>=,<,<=, 由于存储精度存在损耗,相等运算可以认为差值<0.00005等方法>
        • 赋值运算:=,+=,-=,/=,*=
        • 类型转化可能出现溢出
    • 复数,如:默认为 complex128 i := 1+2i
      • complex64 complex128
    • 字符串:string,只能为 ascii 值
      • 说明:
        • var name string = “xianbin” # 可解释的字符串,包含特殊字符正常展示输出,可以转移输出,如 “\t”
        • var desc string = xianbin,中国人 # 原生字符串,可以包括特殊字符,如 \n,\f,\t,\b…,特殊字符原生输出
      • 算术运行符:
        • 连接:+
      • 关系运算:==,!=,>,>=,<,<=
      • 赋值运行:s := "abc"; s += "def"
      • 索引:0 ~ n-1(n 为字符串的长度,使用 len(s) 计算长度),仅对ascii有效,否则乱码。name = "xianbin"; fmt.Printf("%T, %c", name[0], name[0]) // "int8 x"
      • 切片:s[start:end],字符串长度 start ~ end-1,不包括end,仅对ascii有效,否则乱码。name = "xianbin"; fmt.Printf("%T, %v", name[0:2], name[0:2]) // "string xi"
    • error
    • 进制:十进制、八进制(0644 = 6x8^2 + 4x8^1 + 4)、十六进制(0x开头,0-9A-F)
  • 内置函数:
    • append cap copy close complex delete make len real imag panic revocer
    • new:创建,返回值是指针类型,一般用于值类型中,不在引用类型中使用
  • 空白标识符:_

字面量

字面量:用于变量、常量的初始化

  • 基础数据类型,如:0,1.1,true
  • 构造自定义复合数据类型,如:type Name string
  • 复合数据类型值,用来构造array,slice,map,struct的值,如{“a”,“b”,“c”}

操作符

  • 算术运算符:+,-,*,/(5/2=2),%(5%2=1),++(仅支持放在变量后面:age++),–
  • 关系运算符:>,>=,<,<=,==,!=
  • 逻辑运算符:
    • 且:&&
    • 或:||
    • 非:!
  • 位运算(二进制的运算)符:
    • 与 & (5&2 = 2)
    • 或 | (5|2 = 7)
    • 异或 ^ (5^2 = 7)
    • 左移 « (2«1 = 4)
    • 右移 » (2»1 = 1)
    • 位清空(同为1的位清为0) &^ (5&^2 = 5)
  • 赋值运算符:=,+=,-=,*=,/=,%=,&=,|=,^=,«=,»=,&^=
  • 其他运算符:
    • 取地址:&
    • 取引用:*
    • 方法调用或属性值:.
    • 取反:-,a=-1 -> -a=1
    • 可变参数:…
    • chan 的读和写:<-

分隔符

  • 小括号:()
  • 中括号:[]
  • 大括号:{},用来声明作用域
  • 分号:;,每行后可选加;,一行多条语句使用;分隔
  • 逗号:,

变量

变量:对一块存储空间的名称定义,可通过该名称对存储空间的内容进行访问、修改等操作,格式如下:

  • var <变量名> <变量类型> = <变量值>
  • var <变量名> <变量类型>,var a string
  • var <变量名> = <变量值>
  • var <变量名1>, <变量名2>,.. <变量类型>
  • var <变量名1>, <变量名2> <变量类型> = <变量值1> <变量值2>
  • var <变量名1>, <变量名2> = <变量值1> <变量值2>
  • 短声明:<变量名> := <变量值>,变量类型自动推到。该声明方式只能在函数内部使用,如 b := true
  • 说明:
    • 函数内定义的变量必须使用,否则编译错误
    • 函数外定义的变量可以不使用
    • 变量只能声明一次
    • := 左侧至少有一个新变量
    var (
        name string = "xianbin"
        age  int    = 18
    )

    // or

    var (
        name = "xianbin"
        age  = 18
    )

    // switch a, b
    var a, b = 1, 9
    a, b = b, a

常量

常量:不允许修改的量,变量名一般全大写,必须设置值

const NAME string = "xianbin"
const PI = 3.14
const NAME, AGE = "xianbin", 18
const (
  NAME string = "xianbin"
  age int = 18
)
const (
  NAME = "xianbin"
  age = 18
)

// 缺省赋值,同时省略类型和值,表示和前一个常量相同
const (
  C1 int = 1
  C2
  C3
)
fmt.Println(C1, C2, C3)  // 1 1 1

// 枚举赋值,同时省略类型和值,表示前一个常量增加1
const (
  I1 int = iota
  I2
  I3
)
const (
  I4 int = iota
  I5
  I6
)
fmt.Println(I1, I2, I3)  // 0 1 2
fmt.Println(I4, I5, I6)  // 0 1 2

作用域

作用域:用来定义标识符的使用范围,只能被声明一次且变量必须使用,Golang 使用 {} 定义作用域,大括号内的语句称为语句块

  • 子语句块可以使用父语句块的标识符
  • 父语句块不能使用子语句块的标识符
  • 子语句块中可以重复定义父语句块的标识符,并属于两个不同的作用域
  • 常见的语句块:
    • 包语句块;文件语句块;if、switch、case、for、select语句块

注释

  • 单号以 // 开头
  • 单号以 /* 开头,以 */ 结尾
  • 多行以 /* 开头,以 */ 结尾

类型转换

类型转换,包括 int/int*/uint/uint*/byte/rune 之间的相互转换,从大往小转化可能出现溢出截断,如 var A int = 0XFFFF 使用 uint8(A)会出现截断现象

var intA int = 10
var uintB uint = 2
fmt.Println(intA + int(uintB))
fmt.Println(uint(intA) + uintB)

指针

用于存储变量在内存中的存储位置,指针默认被初始化为 nil

package main

import "fmt"

func main() {
	var v1 = 2
	var v2 = 3
	v2 = v1
	fmt.Println(v1, v2) // 2 2
	v1 = 4
	fmt.Println(v1, v2) // 4 2

	// 指针:使用 & 取地址
	p1 := &v1
	var p2 *int = &v2
	fmt.Printf("%T %T\n", p1, p2) // *int *int

	// 取值:使用 * 取指针的值
	fmt.Println(*p1, *p2) // 4 2
	*p1 = 10
	*p2 = 15
	fmt.Println(v1, v2) // 10 15
}

输入&输出

fmt.Println("...")  // 输出一行
fmt.Print("...")  // 输出字符,不带换行

格式化输出

fmt.Printf("%s", "...")  // 格式化输出,不换行,使用 `\n` 换行
  • %s 字符串,若出现 %!s(xx=bool),表示使用 %s 输出的类型不匹配
  • %T 类型
  • %d 整数
  • %8d 占8位格式化整数,没有的空格填充
  • %08d 占8位格式化整数,没有的0填充
  • %-8d 占8位格式化整数,左对齐(没有-表示右对齐)
  • %f 浮点数
  • %6.2f 6位浮点数(包括小数点),保留2位小数
  • %b 二进制
  • %o 八进制
  • %x 十六进制
  • %t bool 类型
  • %c byte 类型
  • %p 指针地址
  • %q rune (码点)类型,原始值
  • %U rune (码点)类型,编码值
  • %v 调用每种类型自己的格式输出
  • %#v 打印结构体
  • %% 输出百分号

输入

从命令行读入数据

package main

import "fmt"

func main() {
	var input string
	fmt.Print("please input something:")
	fmt.Scan(&input)
	fmt.Println("you input is", input)
}

注意:

  • 若输入的类型与期望的类型不匹配,将会赋予对应类型的零值

流程控制

条件 if-else

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	rand.Seed(time.Now().UnixNano())
	r := rand.Intn(10)
	if r >= 8 {
		fmt.Printf("%v >= 8", r)
	} else if r >= 5 {
		fmt.Printf("5 =< %v < 8", r)
	} else {
		fmt.Printf("%v < 5", r)
	}
}

选择 switch-case

// 值判断
switch var1 {
  case val1:
  case val2:
  default:
}

// 条件判断
switch {
  case conditionA:
  case conditionB:
  default:
}
  • 示例1
package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	rand.Seed(time.Now().UnixNano())
	r := rand.Intn(10)

  // 值判断
	switch r {
	case 10:
		fmt.Printf("%v == 8\n", r)
	case 9, 8:
		fmt.Printf("%v == 8 or %v == 9\n", r, r)
	default:
		fmt.Printf("%v < 8\n", r)
	}

  // 条件判断
	switch {
	case r >= 8:
		fmt.Printf("%v == 8", r)
	case r >= 5:
		fmt.Printf("5 =< %v < 8", r)
	default:
		fmt.Printf("%v < 5", r)
	}
}
  • 示例2
package main

import "fmt"

func main() {
	isMatch := func(i int) bool {
		switch i {
		case 1:
		case 2:
			return true
		}
		return false
	}

	fmt.Println(isMatch(1))
	fmt.Println(isMatch(2))
}

// false, true

循环 for

for 初始化子语句; 条件子语句;后置子语句 {
    ...
}
for condition {

}
for {}
for range
  • 示例
package main

import "fmt"

func main() {
	sum := 0
	for i := 1; i <= 10; i++ {
		sum += i
	}
	fmt.Println(sum)

	sum = 0
	i := 1
	for i <= 10 {
		sum += i
		i++
	}
	fmt.Println(sum)

	sum = 0
	i = 0
	for {
		sum += i
		if i == 10 {
			break
		}
		i++
	}
	fmt.Println(sum)
}

for range

适用于字符串、数组、切片、映射、管道等 ascii 编码

package main

import "fmt"

func main() {
	message := "我是中文"
	for index, ch := range message {
		fmt.Printf("%d %T %q\n", index, ch, ch)
	}
}

输出:

0 int32 '我'
3 int32 '是'
6 int32 '中'
9 int32 '文'

说明:

  • rune 类型也是 int32
  • 字符使用单引号,字符串使用双引号

break & continue

  • break 跳出循环
  • continue 跳过本次循环
package main

import "fmt"

func main() {
	for i := 0; i < 10; i++ {
		if i == 4 {
			continue // 跳过 4
		} else if i == 6 {
			break // 跳出循环
		}
		fmt.Println(i)
	}
}
  • break + 标签用法
package main

import "fmt"

func main() {
END:
	for i := 0; i < 5; i++ {
		for j := 0; j < 5; j++ {
			if i*j == 9 {
				break END
			}
			fmt.Printf("%v * %v = %v\n", i, j, i*j)
		}
	}

	fmt.Println("end") // 总会执行
}

continue 同样有次用法,label 必须定义在循环的上面

goto

作用跳转到指定位置,可用于多层嵌套的for中

  • 标签定义:大写英文字符 + “:”

  • goto 标签:实现跳转到指定位置

  • goto 示例1

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	rand.Seed(time.Now().UnixNano())
	r := rand.Intn(10)
	if r > 5 {
		goto GT5
	} else {
		goto END
	}

GT5:
	fmt.Println("gt 5")
END:
}
  • goto 示例2
package main

import "fmt"

func main() {
    i := 1
    sum := 0

START:
    if i > 10 {
        goto END
    }
    sum += i
    i++
    goto START

END:
    fmt.Println(sum)
}
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数