这一节看下Golang的基础语法...嘛...如果懒得看的话,记得以下两点就好:
1 Golang的变量、函数参数等等涉及到类型的声明,都是变量名写在前,类型写在后的(和正统C系语法刚好相反)
2 循环语句for通吃所有
然后,就开始啦~
Go语言基本语法
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello Azen٩(●˙▿˙●)۶…⋆ฺ")
}
作用:使用fmt这个库运行输出
定义变量
var 关键字
func variableZeroValueDefine() {
var a int
var s string
fmt.Printf("%d %q\n", a, s)
}
知识点:
- 变量名写在前面,变量类型写在后面
原因:在定义变量的时候,往往是先想到名字,再想到变量类型 - 定义完变量后,变量都是有默认初值的。
2.1. int类型的初值为0
2.2. string类型的初始为空串 - 使用%q可打印空串,展示结果为→ ""
同时定义变量并赋初值
注意:变量定义后,一定要用到,否则编译器报错。
func varibaleInitial() {
var a, b int = 1, 2
var s string = "猜猜我是谁"
fmt.Println(a, b, s)
}
此例中,int 和 string是多余的,编译器可以做类型推断 - Type Deduction
类型推断
func varibaleTypeDeduction() {
var a, b, c, s = 1, 2, true, "ㄟ..."
b = 666
fmt.Println(a, b, c, s)
}
:=定义并赋初值
更推荐用这种方式定义并初始化变量
func varibaleShortcut() {
a, b := 233, 666
s := "老铁蛤蛤蛤"
fmt.Println(a, b, s)
}
在函数外定义变量
var a, b int
var ss = "总想搞个大新闻"
在函数外面定义的变量,不是全局变量,而是包内变量。
Go语言木有全局变量的说法。
注意:在函数外定义变量不能用:=的方式,一定要被var关键字修饰
使用()定义多个包内变量
可以把变量定义在括号中,以减少var关键字的使用数量
var (
s string
a, b int
// a, b := 666, 233 // 全局变量不能用:=定义
)
var ss = "总想搞个大新闻"
内建变量类型
- bool,string
- (u)int, (u)int8, (u)int16, (u)int32, (u)int64, uintptr
- 没跟长度,则长度按操作系统位数而定
- uintptr是个指针
- 字符型
- byte
- rune
- 不是char,char只有1个字节
- rune的长度为32位,4字节,为应对全球化的字符
- 浮点数
- float32,float64
- complex64,complex128
- 复数,有实部和虚部
- complex64的实部和虚部为32位
- complex128的实部和虚部为64位
- 把复数直接作为内建变量的类型
强制类型转换
Golang只有强制类型转换,没有隐式类型转换
示例:计算斜边的长度
a = 3 b = 4 求斜边c
func triangle() {
var a, b = 3, 4
var c int
c = int(math.Sqrt(float64(a * a + b * b)))
fmt.Println(c)
}
math.Sqrt函数的入参和出参都是float64,所以需要强制转换。
常量的定义
关键字:const
常量类型可规定也可不规定
- 如果不规定类型,常量会像「文本替换」功能一样(类似宏定义),到使用它的地方再决定类型
Golang中不会把常量全部大写,因为首字母大写在golang中代表作用域为public
func consts() {
const (
filename = "abc.txt"
a, b = 3, 4
)
var c int
c = int(math.Sqrt(a * a + b * b))
fmt.Println(filename, c)
}
*注:上面的a*a就不用强制转为float64,因为被当成类似「宏替换」了
枚举类型
基本定义:就定义成一组const就好
func enums() {
const (
azen = 0
daker = 1
buran = 2
)
fmt.Println(azen, daker, buran)
}
iota自增值
定义枚举
func enums() {
const (
azen = iota
daker
_
buran
)
fmt.Println(azen, daker, buran)
}
iota参与运算
const (
b = 1 << (10 * iota)
kb
mb
gb
tb
pb
)
条件语句
if语句
func bounded(v int) int {
if v > 100 {
return 100
} else if v < 0 {
return 0
} else {
return v
}
}
特点:if条件不需要用括号包裹
示例:
func ReadFile(filename string) ([]byte, error)
返回两个值:
- [ ]byte 是读取到的文件内容数组 - 使用%s打印内容
- error 是出错信息
func fileRead() {
const filename = "abc.txt"
contents, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Println(contents)
} else {
fmt.Println(err)
}
}
使用;简化条件
func fileRead() {
const filename = "abc.txt"
if contents, err := ioutil.ReadFile(filename); err != nil {
fmt.Println(contents)
} else {
fmt.Println(err)
}
}
在条件中可以用:=做声明和赋值
出了if的作用域就不能访问如上定义的content和err了
switch语句
基本用法
func eval(a, b int, op string) int {
var result int
switch op {
case "+":
result = a + b
case "-":
result = a - b
case "*":
result = a * b
case "/":
result = a / b
default:
panic("不支持的操作类型")
}
return result
}
特点:
- 不需要加break,默认都有break.
- 如果不需要break,手动加fallthrough - 这一点和swift一样
panic
抛出一个错误 - 类似于NSError的raise
switch后不接表达式
在case中加入条件即可
func grade(score int) string {
var g string
switch {
case score < 0 || score > 100:
panic(fmt.Sprintf("Wrong score: %d", score))
case score < 60:
g = "F"
case score < 80:
g = "B"
case score < 90:
g = "A"
case score <= 100:
g = "SSR"
}
return g
}
循环语句
for语句
func accumulate() {
sum := 0
for i := 0; i < 100; i++ {
sum++
}
fmt.Println(sum)
}
栗子:转二进制
转二进制的思路:不断取模,获得的数字往前加
func convertToBin(number int) string {
result := ""
for ; number > 0; number /= 2 {
lsb := number % 2
result = strconv.Itoa(lsb) + result
}
return result
}
strconv.Itoa → 将int转为string
for的while用法(只有结束条件)
func pintFile(filename string) {
file, err := os.Open(filename)
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(file)
// func (s *Scanner) Scan() bool - 在读完或报错的时候,会返回false
for scanner.Scan() {
}
}
当for 语句中,省略其实条件、递增条件,只有结束条件的时候,类似于while
for省略所有表达式 - 死循环
func forever() {
for {
fmt.Println("Bilibili")
}
}
Golang中经常要用到死循环,goroutine的肚子里就是死循环。好多死循环的goroutine在互相进行通讯。 - 这和iOS开发中的runloop是一样一样的
函数定义
func eval(a, b int, op string) int
函数名在前,类型在后。和变量定义思路一致
基本示例
func convertToBin(number int) string {
result := ""
for ; number > 0; number /= 2 {
lsb := number % 2
result = strconv.Itoa(lsb) + result
}
return result
}
函数返回多个值示例
带余除法
func div(a, b int) (int, int) {
return a / b, a % b
}
给返回值起名字
好处是可读性更强,函数调用者可以很方便的知道返回值是啥...
func div(a, b int) (q, r int) {
return a / b, a % b
}
如下写法也可以正确返回q和r,但是可读性不强,不建议这样写
func divNoRecommend(a, b int) (q, r int) {
q = a / b
r = a % b
return
}
多返回值只使用一个
result, _ := div(10, 7)
多值返回的习惯用法
第一个值返回正常值,第二个值返回error
函数和函数式编程
函数在Golang中是一等公民,函数可以作为参数、返回值
函数作为参数
func apply(op func(a, b int) int, a, b int) (result int) {
return op(a, b)
}
打印下op看看它是谁
- 拿函数名
- 需要用reflect.ValueOf(op).Pointer()拿到这个函数的指针
- 使用runtime获取函数名runtime.FuncForPC(p).Name()
func apply(op func(a, b int) int, a, b int) (result int) {
p := reflect.ValueOf(op).Pointer()
opName := runtime.FuncForPC(p).Name()
fmt.Println(opName)
return op(a, b)
}
匿名函数作为函数参数
fmt.Println(
apply(func (a, b int) int {
return a + b
}, 10, 20),
)
Golang没有花哨的lambda表达式
函数其他说明
可变参数列表
可变参数列表的使用方法,可以和数组一样用
func sum(numbers ...int) int {
result := 0
for i := range numbers {
result += numbers[i]
}
return result
}
没有默认参数、可选参数概念
指针
func pointerDefine() {
var a = 0
var pa *int = &a
*pa = 0x66CCFF
fmt.Println(a)
}
特点
- 指针不能运算
- C可以拿到指针的头指针做加法运算 - 步长由指针类型决定
函数参数传递
C、C++、OC可以值传,也可以引用传
Java和Python基本上都是引用传递
Golang只有值传递一种方式
凡调函数,参数都会拷贝一份 - 和指针配合,实现相当引用传递的效果
举例:交换两个变量的值
func swap(a, b *int) {
*a, *b = *b, *a
}
有疑问加站长微信联系(非本文作者)