基础语法:
// ---------------------------------------变量声明var [名称] [类型]
var name string
1)声明的变量一定要使用
2)声明同时初始化,可以不写后面类型:var sum = 17
多变量声明同其他语言var a,b int
// 自动推导类型
[名称] := [值]
name := "Bill"
1)注意只能在函数局部使用
2)只能使用一次,用于初始化,第一次出现使用
//多重初始化
a, b, c := 10, "abc", 1.0
//交换两个变量的值
A,b = b,a
//_匿名变量,丢弃数据不处理,一般是配合函数返回值使用
tmp,_ = i,j //tmp=10,go函数支持返回多值
//不同类型变量的声明
var(
a int = 1
b string = “abc”
)
// ---------------------------------------常量
const [名称] [类型] = 值
const a int = 3
const b = “abc”
1、 不能使用自动推导
2、 类型是可选选项
3、 同时声明多个:const a, b, c = 1, false, "str" //多重赋值
常量还可以用作枚举(声明多个常量):
const (
Unknown = 0
Female = 1
Male = 2
)
// ---------------------------------------iota枚举
const (
x = iota // x == 0
y = iota // y == 1
z = iota // z == 2
w // 这里隐式地说w = iota,因此w == 3。其实上面y和z可同样不用"= iota"
)
const v = iota // 每遇到一个const关键字,iota就会重置,此时v == 0
const (
h, i, j = iota, iota, iota //h=0,i=0,j=0 iota在同一行值相同
)
const (
a = iota //a=0
b = "B"
c = iota //c=2
d, e, f = iota, iota, iota //d=3,e=3,f=3
g = iota //g = 4
)
const (
x1 = iota * 10 // x1 == 0
y1 = iota * 10 // y1 == 10
z1 = iota * 10 // z1 == 20
)
1、 iota每隔一行,自动累计加1
2、 iota给常量赋值使用
3、 iota遇到const,重置为0
// ---------------------------------------注释
// 单行注释,CsCode快捷键Ctrl+/
/*
Author by 多行注释
我是多行注释
*/
// --------------------------------------- 类型转换
分兼容的直接转换,和不兼容的利用类库转换
不兼容的一般会类似这样提示
cannot convert expression (type type_name) to type desctype_name
1、 兼容的可以直接转
type_name(expression)
mean = float32(sum) / float32(count)
var ch byte
ch = ‘a’
var t int
t = int(ch)//类型转换
2、 不兼容的可以利用函数strconv里面的方法进行转,一般会返回两个值,转换结果和报错信息
boolvalue, err := strconv.ParseBool("1")//int类型 转成 bool
i, err := strconv.Atoi(“123”) //string 类型 转成 int
// --------------------------------------- 变量输入
Var a int
fmt.Scan(&a)//输入值赋给a
// --------------------------------------- 类型别名type(把类型弄个自己的名字来用)
在结构体和方法里面会用到
Type 别名 类型
type bigint int64 //int64类型改名为bigint
var x bigint = 100//等价于var x int64 = 100
type (
myint int //int改名为myint
mystr string //string改名为mystr
)
// --------------------------------------- 运算符
大同小异,这里列一些和C#不同的
1、++和—只有后自增和后自减
2、位运算符多了<<和>>
3、赋值运算符<<=、>>=
4、返回变量存储地址:&
5、指针变量:*
/* & 和 * 运算符实例 */
var a int = 4
var ptr *int
ptr = &a /* 'ptr' 包含了 'a' 变量的地址,*ptr就是a的值 */
fmt.Printf("a 的值为 %d\n", a);
fmt.Printf("*ptr 为 %d\n", *ptr);
a 的值为 4
*ptr 为 4
一元运算符拥有最高的优先级,二元运算符的运算方向均是从左至右。
下表列出了所有运算符以及它们的优先级,由上至下代表优先级由高到低:
优先级 | 运算符 |
7 | ^ ! |
6 | * / % << >> & &^ |
5 | + - | ^ |
4 | == != < <= >= > |
3 | <- |
2 | && |
1 | || |
// --------------------------------------- if语句
If
if a < 20 {
fmt.Printf("a 小于 20\n" )
}
//支持一个初始化表达式, 初始化字句和条件表达式直接需要用分号分隔
if b := 3; b == 3 {
fmt.Println("b==3")
}
If else
if a < 20 {//{不能跨行
fmt.Printf("a 小于 20\n" );
} else if {//{不能跨行
fmt.Printf("a 不小于 20\n" );
}else {//{不能跨行
fmt.Printf("a 不小于 20\n" );
}
if嵌套
if 布尔表达式 1 {
/* 在布尔表达式 1 为 true 时执行 */
if 布尔表达式 2 {
/* 在布尔表达式 2 为 true 时执行 */
}
}
Switch:和C#比,没括号(go里面,加括号,加分号,保存后,VsCode会自动去掉)
switch var1 {
case val1:
...
case val2:
...
default:
...
}
Type Switch
switch x.(type){
case type:
statement(s);
case type:
statement(s);
/* 你可以定义任意个数的case */
default: /* 可选 */
statement(s);
}
1. 支持多条件匹配
switch{
case 1,2,3,4:
default:
}
2. 不同的 case 之间不使用 break 分隔,默认只会执行一个 case。
3. 如果想要执行多个 case,需要使用 fallthrough 关键字(一般结尾加,没遇到break会继续执行下一个case分支),也可用 break 终止。
4、支持初始化语句
switch num := 1;num{
case 1:
default:
}
4、 switch可以没有条件,case放条件
score := 85
Switch{
Case score > 90:
fmt.mrintlc(“优秀”)
}
// --------------------------------------- 循环 for
有三种形式
1、for init; condition; post { }
2、for condition { } //和while类似
3、for { } //相当于while(true),里面要加break跳出的
- init: 一般为赋值表达式,给控制变量赋初值;
- condition: 关系表达式或逻辑表达式,循环控制条件;
- post: 一般为赋值表达式,给控制变量增量或减量。
- /* for 循环 */
- for a := 0; a < 10; a++ {
- fmt.Printf("a 的值为: %d\n", a)
- }
for a < b {
a++
fmt.Printf("a 的值为: %d\n", a)
}
// --------------------------------------- 迭代器range相当于C#foreach
关键字 range 会返回两个值(可以选择定义一个变量只接受一个值),第一个返回值是元素的数组下标,第二个返回值是元素的值:
s := "abc"
for i := range s { //支持 string/array/slice/map。
fmt.Printf("%c\n", s[i])//只接受下标i
}
for _, c := range s { // 忽略 index
fmt.Printf("%c\n", c)
}
for i, c := range s {
fmt.Printf("%d, %c\n", i, c)
}
// --------------------------------------- goto
用goto跳转到必须在当前函数内定义的标签:
func main() {
for i := 0; i < 5; i++ {
for {
fmt.Println(i)
goto LABEL //跳转到标签LABEL,从标签处,执行代码
}
}
fmt.Println("this is test")
LABEL:
fmt.Println("it is over")
}
// --------------------------------------- 函数
func function_name( [parameter list] ) [return_types] {
函数体
}
- func:函数由 func 开始声明
- function_name:函数名称,函数名和参数列表一起构成了函数签名。
- parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
- return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
- 函数体:函数定义的代码集合。
- 函数名首字母小写即为private,大写即为public
- /* 函数返回两个数的和 */
- func max(num1, num2 int) int {
- /* 声明局部变量 */
- var result int
- Result = num1 + num2
- return result
- }
多个返回值
- /* 函数返回两个数的和 */
- func swap(num1 string, num2 int) (string,int) {
- return num1,num2
- }
不定参数类型
不定参数是指函数传入的参数个数为不定数量。为了做到这点,首先需要将函数定义为接受不定参数类型:
//形如...type格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数
func Test(args ...int) {
for _, n := range args { //遍历参数列表
fmt.Println(n)
}
}
func main() {
//函数调用,可传0到多个参数
Test()
Test(1)
Test(1, 2, 3, 4)
}
2) 不定参数的传递
func MyFunc01(args ...int) {
fmt.Println("MyFunc01")//len(args)
for _, n := range args { //遍历参数列表
fmt.Println(n)
}
}
func MyFunc02(args ...int) {
fmt.Println("MyFunc02")
for _, n := range args { //遍历参数列表
fmt.Println(n)
}
}
func Test(args ...int) {
MyFunc01(args...) //按原样传递, Test()的参数原封不动传递给MyFunc01
MyFunc02(args[1:]...) //Test()参数列表中,第1个参数及以后的参数传递给MyFunc02
}
func main() {
Test(1, 2, 3) //函数调用
}
返回写法比较多样
单个返回值
func Test01() int { //方式1
return 250
}
//官方建议:最好命名返回值,因为不命名返回值,虽然使得代码更加简洁了,但是会造成生成的文档可读性差
func Test02() (value int) { //方式2, 给返回值命名
value = 250
return value
}
func Test03() (value int) { //方式3, 给返回值命名
value = 250
return
}
多个返回值
func Test01() (int, string) { //方式1
return 250, "sb"
}
func Test02() (a int, str string) { //方式2, 给返回值命名
a = 250
str = "sb"
return
}
// --------------------------------------- 函数类型(有点像C#的委托)
在Go语言中,函数也是一种数据类型,我们可以通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型。
type FuncType func(int, int) int //声明一个函数类型, func后面没有函数名
//函数中有一个参数类型为函数类型:f FuncType
func Calc(a, b int, f FuncType) (result int) {//Calc是执行的
result = f(a, b) //通过调用f()实现任务
return
}
//实际调用的函数实现
func Add(a, b int) int {
return a + b
}
//实际调用的函数实现
func Minus(a, b int) int {
return a - b
}
func main() {
//函数调用,第三个参数为函数名字,此函数的参数,返回值必须和FuncType类型一致
result := Calc(1, 1, Add)
fmt.Println(result) //2
var f FuncType = Minus
fmt.Println("result = ", f(10, 2)) //result = 8
}
回调函数
就是利用函数类型实现
实际调用的函数是函数类型的实现
调用那个实现函数类型的是执行的主体
// --------------------------------------- 匿名函数与闭包
所谓闭包就是一个函数“捕获”了和它在同一作用域的其它常量和变量。这就意味着当闭包被调用的时候,不管在程序什么地方调用,闭包能够使用这些常量或者变量。它不关心这些捕获了的变量和常量是否已经超出了作用域,所以只有闭包还在使用它,这些变量就还会存在。
闭包需要匿名函数来实现(都是匿名函数)
在Go语言里,所有的匿名函数(Go语言规范中称之为函数字面量)都是闭包。匿名函数是指不需要定义函数名的一种函数实现方式。
func main() {
i := 0
str := "mike"
//方式1,先定义,再赋函数
f1 := func() { //匿名函数,无参无返回值
//引用到函数外的变量
fmt.Printf("方式1:i = %d, str = %s\n", i, str)
}
f1() //函数调用
//方式1的另一种方式
type FuncType func() //声明函数类型, 无参无返回值
var f2 FuncType = f1//这里f1就是没参数名的函数
f2() //函数调用
//方式2,直接赋函数
var f3 FuncType = func() {
fmt.Printf("方式2:i = %d, str = %s\n", i, str)
}
f3() //函数调用= func() {
//方式3,定义完了加()就是调用
func() { //匿名函数,无参无返回值
fmt.Printf("方式3:i = %d, str = %s\n", i, str)
}() //别忘了后面的(), ()的作用是,此处直接调用此匿名函数
//方式4, 匿名函数,有参有返回值
v := func(a, b int) (result int) {
result = a + b
return
}(1, 1) //别忘了后面的(1, 1), (1, 1)的作用是,此处直接调用此匿名函数, 并传参
fmt.Println("v = ", v)
}
// --------------------------------------- 数组
var variable_name [SIZE]variable_type
var balance [10]float32
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}//初始化
/* 未定义长度的数组只能传给不限制数组长度的函数 */
/* 定义了长度的数组只能传给限制了相同数组长度的函数 */
二维数组
var arrayName [ x ][ y ]variable_type
//初始化
a = [3][4]int{
{0, 1, 2, 3} , /* 第一行索引为 0 */
{4, 5, 6, 7} , /* 第二行索引为 1 */
{8, 9, 10, 11}, /* 第三行索引为 2 */
}
// --------------------------------------- 结构体
type struct_variable_type struct {
member definition;
member definition;
...
member definition;
}
type Books struct {
title string
author string
subject string
book_id int
}
// 创建一个新的结构体
fmt.Println(Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407})
// 也可以使用 key => value 格式
fmt.Println(Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", book_id: 6495407})
// 忽略的字段为 0 或 空
fmt.Println(Books{title: "Go 语言", author: "www.runoob.com"})
var Book1 Books /* 声明 Book1 为 Books 类型 */
/* book 1 描述 */
Book1.title = "Go 语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407
// --------------------------------------- defer 延迟调用
关键字 defer ⽤于延迟一个函数或者方法(或者当前所创建的匿名函数)的执行。注意,defer语句只能出现在函数或方法的内部。
func main() {
defer fmt.Println("this is a defer") //main结束前调用
fmt.Println("this is a test")
/*
运行结果:
this is a test
this is a defer
*/
}
defer语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、加锁、释放锁。通过defer机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放。释放资源的defer应该直接跟在请求资源的语句后。
多个defer执行顺序
以LIFO(后进先出)的顺序执行。哪怕函数或某个延迟调用发生错误,这些调用依旧会被执⾏。
defer和匿名函数结合使用(注意里面的变量是否是闭包引用)
func main() {
a, b := 10, 20
defer func(x int) { // a以值传递方式传给x
fmt.Println("defer:", x, b) // b 闭包引用,a是外部传参进来,已经传进来10,只是没执行
}(a)
a += 10
b += 100
fmt.Printf("a = %d, b = %d\n", a, b)
/*
运行结果:
a = 20, b = 120
defer: 10 120
*/
}
// --------------------------------------- 获取命令行参数
import (
"fmt"
"os" //os.Args所需的包
)
func main() {
args := os.Args //获取用户输入的所有参数,字符串
//如果用户没有输入,或参数个数不够,则调用该函数提示用户
if args == nil || len(args) < 2 {
fmt.Println("err: xxx ip port")
return
}
ip := args[1] //获取输入的第一个参数
port := args[2] //获取输入的第二个参数
fmt.Printf("ip = %s, port = %s\n", ip, port)
}
// ---------------------------------------不同作用域的同名变量
在不同作用域可以声明同名的变量,其访问原则为:在同一个作用域内,就近原则访问最近的变量,如果此作用域没有此变量声明,则访问全局变量,如果全局变量也没有,则报错。
基础数据类型
2.4.1 分类
Go语言内置以下这些基础类型:
类型 | 名称 | 长度 | 零值 | 说明 |
bool | 布尔类型 | 1 | false | 其值不为真即为家,不可以用数字代表true或false |
byte | 字节型 | 1 | 0 | uint8别名,放英文字符‘a’ |
rune | 字符类型 | 4 | 0 | 专用于存储unicode编码,等价于uint32,可以放中文字符‘中’//20013 |
int, uint | 整型 | 4或8 | 0 | 32位或64位 |
int8, uint8 | 整型 | 1 | 0 | -128 ~ 127, 0 ~ 255 |
int16, uint16 | 整型 | 2 | 0 | -32768 ~ 32767, 0 ~ 65535 |
int32, uint32 | 整型 | 4 | 0 | -21亿 ~ 21 亿, 0 ~ 42 亿 |
int64, uint64 | 整型 | 8 | 0 |
|
float32 | 浮点型 | 4 | 0.0 | 小数位精确到7位 |
float64 | 浮点型 | 8 | 0.0 | 小数位精确到15位 |
complex64 | 复数类型 | 8 |
|
|
complex128 | 复数类型 | 16 |
|
|
uintptr | 整型 | 4或8 |
| ⾜以存储指针的uint32或uint64整数 |
string | 字符串 |
| "" | utf-8字符串,str[0]、str[1] |
格式化输出输入
格式 | 含义 |
%% | 一个%字面量 |
%b | 一个二进制整数值(基数为2),或者是一个(高级的)用科学计数法表示的指数为2的浮点数 |
%c | 字符型。可以把输入的数字按照ASCII码相应转换为对应的字符 |
%d | 一个十进制数值(基数为10) |
%e | 以科学记数法e表示的浮点数或者复数值 |
%E | 以科学记数法E表示的浮点数或者复数值 |
%f | 以标准记数法表示的浮点数或者复数值 |
%g | 以%e或者%f表示的浮点数或者复数,任何一个都以最为紧凑的方式输出 |
%G | 以%E或者%f表示的浮点数或者复数,任何一个都以最为紧凑的方式输出 |
%o | 一个以八进制表示的数字(基数为8) |
%p | 以十六进制(基数为16)表示的一个值的地址,前缀为0x,字母使用小写的a-f表示 |
%q | 使用Go语法以及必须时使用转义,以双引号括起来的字符串或者字节切片[]byte,或者是以单引号括起来的数字 |
%s | 字符串。输出字符串中的字符直至字符串中的空字符(字符串以'\0‘结尾,这个'\0'即空字符) |
%t | 以true或者false输出的布尔值 |
%T | 使用Go语法输出的值的类型 |
%U | 一个用Unicode表示法表示的整型码点,默认值为4个数字字符 |
%v | 使用默认格式输出的内置或者自定义类型的值,或者是使用其类型的String()方式输出的自定义值,如果该方法存在的话 |
%x | 以十六进制表示的整型值(基数为十六),数字a-f使用小写表示 |
%X | 以十六进制表示的整型值(基数为十六),数字A-F使用小写表示 |