写在前面的东东:
https://www.golangtc.com/ 然后设置环境变量。
开始是在atom上搞得,发现不能自行编译,学习语法的我觉得好麻烦啊啊啊啊啊!虽然配置好了,但是不想用,真得真的真的推荐goland这个,加上pycharm,idea,clion,vs突然发现我用得ide已经被捷克人给统治了!!!!只有vs还坚挺!最后真心推荐goland!
ps:goland有很多提示,比如,result:
ps:不要问我怎么破解,我不回告诉你去淘宝买账号,捷克老毛子的软件十块钱通吃~
1.nil
nil在go中相当于其他语言的NULL,null,none等。在实际的使用中,表示某一个变量为空。nil只能赋值给point,channel,func,interface,map或者slice类型的变量。如果将nill赋值给其他变量将会引发panic。
结论:由于go中interface会同时存储类型和值,如果将一个nill对象赋值给一个interface,这个interface为非nill。
package main
import "fmt"
type MyError struct{}
func (this *MyError) Error() string {
return ""
}
func test() error {
var p *MyError = nil
/*
//Check err and Set p
if bad() {
p = ErrBad
}
*/
return p
}
func main() {
err := test()
if err == nil {
fmt.Println("err is nil")
} else {
fmt.Println("err is NOT nil")
}
}
结果是:
err is NOT nil
虽然现在这个函数看不懂,先把代码粘贴上。
///引用 https://studygolang.com/articles/5969
2.for
func main() {
var i,j = 0,100
for i == 0 {
fmt.Println("first style")
i++
}
for i = 0;i<=j;i++{
fmt.Printf("second style %d\n",i)
}
}
3.函数可以返回多个值
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("Mahesh", "Kumar")
fmt.Println(a, b)
}
4.函数闭包function closure
func funcclosure() func() int {
i:=0
fmt.Println("funcclosure",i)//这里是重点
return func() int{
i++
return i
}
}
func main() {
//function closure
nextnum := funcclosure()
//调用 nextnum(),变量 i 自增,并返回
fmt.Println(nextnum())
fmt.Println(nextnum())
fmt.Println(nextnum())
fmt.Println(nextnum())
nextnum1 := funcclosure()
fmt.Println(nextnum1())
fmt.Println(nextnum1())
fmt.Println(nextnum1())
}
输出
funcclosure 0
1
2
3
4
funcclosure 0
1
2
3
为什么会这样呢?其实nextnum := funcclosure() 这一句,nextnum()就等价于 func(),也就是funcclosure()中返回的那个函数。其实nextnum()每次调用的就是这个func(),所以i每次都会增加1.
5.方法
结构:
func (variable_name variable_data_type) function_name() [return_type]{
/* 函数体*/
}
即
func (方法接受者) 方法名字 返回类型{
//函数体
}
方法接受者可以是命名类型或者结构体类型的一个值或者是一个指针。
package main
import "fmt"
type Circle struct {
radius float64
}
func main(){
var c1 Circle
c1.radius = 10.00
fmt.Println("Area of Circle(c1) = ", c1.getArea())
}
//该method 属于 Circle 类型对象中的方法
func (c Circle) getArea() float64{
//
return 3.14 * c.radius * c.radius
}
结果:
Area of Circle(c1) = 314
6.变量作用域
Go 语言程序中全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑。实例如下:
package main
import "fmt"
/* 声明全局变量 */
var g int = 20
func main() {
/* 声明局部变量 */
var g int = 10
fmt.Printf ("结果: g = %d\n", g)
}
结果: g = 10
7.数组
var variable_name [SIZE] variable_type
初始化数组:
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小:
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
数组得访问:
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
var salary float32= balance[4]
fmt.Println(salary)
多维数组:
var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type
向函数传递数组:
void myFunction(param [10]int)
{
......
}
void myFunction(param []int)
{
.......
}
求平均值:
package main
import "fmt"
func main(){
var arr = []int{23,19,100,231,51}
var avg float32
avg = getAvag(arr,5)
fmt.Println("平均值为:",avg)
}
func getAvag(arr []int,size int) float32{
var i,sum int
var avg float32
for i=0;i<size;i++{
sum += arr[i]
}
avg = float32(sum / size)
return avg
}
结果:
平均值为: 84
8.指针
var var_name *var-type
var ip *int /* 指向整型*/
var fp *float32 /* 指向浮点型 */
一般用法:
package main
import "fmt"
func main(){
var ptr *int
fmt.Println("ptr:",ptr)
fmt.Printf("ptr value :%x\n",ptr)
var a = 10
ptr = &a
fmt.Println("ptr:",ptr)
fmt.Printf("ptr value :%d\n",*ptr)
fmt.Printf("ptr value :%x\n",ptr)
var b = "abc"
var ptr1 *string
ptr1 = &b
fmt.Println("address is:",ptr1)
fmt.Println("ptr1 value:",*ptr1)
}
结果:
ptr: <nil>
ptr value :0
ptr: 0xc0420080e0
ptr value :10
ptr value :c0420080e0
address is: 0xc0420381c0
ptr1 value: abc
数组与指针:
package main
import "fmt"
func main(){
//array
var arr = []int{15,20,25}
fmt.Println("arr:",arr)
var i int
for i=0;i<3;i++{
fmt.Println("arr:",&arr[i])
}
}
结果是:
arr: [15 20 25]
arr: 0xc0420500c0
arr: 0xc0420500c8
arr: 0xc0420500d0
和c/c++得指针有相似得地方,也有不同得地方。数组名不是数组首地址,就代表了数组。
指向指针的指针:
package main
import "fmt"
func main(){
//
var tmp1 int
var ptr2 *int
var ptr3 **int
tmp1 = 7000
ptr2 = &tmp1
ptr3 = &ptr2
fmt.Println("tmp1:",tmp1)
fmt.Println("ptr2:",ptr2)
fmt.Println("*ptr2:",*ptr2)
fmt.Println("&*ptr2:",&(*ptr2))
fmt.Println("ptr3:",ptr3)
fmt.Println("*ptr3:",*ptr3)
fmt.Println("**ptr3:",**ptr3)
}
结果是:
tmp1: 7000
ptr2: 0xc0420520d0
*ptr2: 7000
&*ptr2: 0xc042008108
ptr3: 0xc042070020
*ptr3: 0xc0420520d0
**ptr3: 7000
指针作为参数:
package main
import "fmt"
func main(){
var a1,a2 int = 520,250
fmt.Println("before a1,a2",a1,a2)
swap(&a1,&a2)
fmt.Println("atfer a1,a2",a1,a2)
}
func swap(x *int,y *int){
var temp int
temp = *x
*x = *y
*y = temp
}
结果:
before a1,a2 520 250
atfer a1,a2 250 520
9.结构体:
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
type struct_variable_type struct {
member definition;
member definition;
...
member definition;
}
声明格式:
variable_name := structure_variable_type {value1, value2...valuen}
如果要访问结构体成员,需要使用点号 (.) 操作符,格式为:"结构体.成员名"。
golang的指针一定要理解清楚。
package main
import "fmt"
type BookInfo struct{
title string
author string
subject string
book_id int
}
func main(){
var book1 BookInfo
var book2 BookInfo
var book3 BookInfo
//这个指针只是来存储地址的,类型是BookInfo
var bookPtr *BookInfo
book1.title = "Go 语言教程"
book1.author = "Alpha"
book1.subject = "Go 语言"
book1.book_id = 10109527
book2.title = "Python 教程"
book2.author = "Alpha"
book2.subject = "Python 教程"
book2.book_id = 10109528
book3.title = "C++ 教程"
book3.author = "Alpha"
book3.subject = "C++ 教程"
book3.book_id = 10109529
fmt.Println("book1:",book1)
fmt.Println("book2:",book2)
fmt.Println("book3:",book3)
PrintBookInfo(book1)
PrintBookInfo(book2)
//一定要这样调用
bookPtr = &book3
PrintBookInfoPoint(bookPtr)
}
func PrintBookInfo(book BookInfo){
fmt.Println("book title:",book.title)
fmt.Println("book author:",book.author)
fmt.Println("book subject:",book.subject)
fmt.Println("book book_id",book.book_id)
}
func PrintBookInfoPoint(book *BookInfo){
fmt.Println("book title:",book.title)
fmt.Println("book author:",book.author)
fmt.Println("book subject:",book.subject)
fmt.Println("book book_id",book.book_id)
}
结果:
book1: {Go 语言教程 Alpha Go 语言 10109527}
book2: {Python 教程 Alpha Python 教程 10109528}
book3: {C++ 教程 Alpha C++ 教程 10109529}
book title: Go 语言教程
book author: Alpha
book subject: Go 语言
book book_id 10109527
book title: Python 教程
book author: Alpha
book subject: Python 教程
book book_id 10109528
book title: C++ 教程
book author: Alpha
book subject: C++ 教程
book book_id 10109529
10.切片(slice)
Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大.(抄的 =。=)
格式:
var identifier []type
切片不需要说明长度。
或使用make()函数来创建切片:
var slice1 []type = make([]type, len)
也可以简写为
slice1 := make([]type, len)
也可以指定容量,其中capacity为可选参数。
make([]T, length, capacity)
这里 len 是数组的长度并且也是切片的初始长度。
//初始化
s :=[] int {1,2,3 }
//接初始化切片,[]表示是切片类型,{1,2,3}初始化值依次是1,2,3.其cap=len=3
s := arr[:]
//初始化切片s,是数组arr的引用
s := arr[startIndex:endIndex]
//将arr中从下标startIndex到endIndex-1 下的元素创建为一个新的切片
s := arr[startIndex:]
//缺省endIndex时将表示一直到arr的最后一个元素
s := arr[:endIndex]
//缺省startIndex时将表示从arr的第一个元素开始
s1 := s[startIndex:endIndex]
//通过切片s初始化切片s1
s :=make([]int,len,cap)
//通过内置函数make()初始化切片s,[]int 标识为其元素类型为int的切片
len()与cap():
切片是可索引的,并且可以通过len()方法获取长度。
切片提供了计算容量的方法cap(),可以测量切片最长可以达到多少。
package main
import "fmt"
func main(){
//len() cap()
var sl1 = make([]int,3,5)
var sl2 []int
printSlice(sl1)
printSlice(sl2)
if(sl2 == nil){
fmt.Println("sl2 切片是空的")
}
/*创建切片*/
sl3 := []int{0,1,2,3,4,5,6,7,8,9}
printSlice(sl3)
/*打印原始切片*/
fmt.Println("sl3 == ",sl3)
/*打印子切片从索引1到索引4*/
fmt.Println("sl3[1:4] == ",sl3[1:4])
/*默认下限为 0*/
fmt.Println("sl3[:4] == ",sl3[:4])
/*默认上限为 len(s)*/
fmt.Println("sl3[4:] == ",sl3[4:])
sl4 := make([]int,0,5)
printSlice(sl4)
/*打印子切片从索引 0(包含) 到索引 2(不包含)*/
//索引!!!!
sl5 := sl3[:2]
printSlice(sl5)
/* 打印子切片从索引 2(包含) 到索引 5(不包含)*/
//索引!!!!
sl6 := sl3[2:5]
printSlice(sl6)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
结果是:
len=3 cap=5 slice=[0 0 0]
len=0 cap=0 slice=[]
sl2 切片是空的
len=10 cap=10 slice=[0 1 2 3 4 5 6 7 8 9]
sl3 == [0 1 2 3 4 5 6 7 8 9]
sl3[1:4] == [1 2 3]
sl3[:4] == [0 1 2 3]
sl3[4:] == [4 5 6 7 8 9]
len=0 cap=5 slice=[]
len=2 cap=10 slice=[0 1]
len=3 cap=8 slice=[2 3 4]
append()与copy()
package main
import (
"fmt"
)
func main(){
var slice1 []int
printSlice(slice1)
/*允许住家空切片*/
slice1 = append(slice1,0)
printSlice(slice1)
/*向切片添加一个元素*/
slice1 = append(slice1,1)
printSlice(slice1)
/*同时添加多个元素*/
slice1 = append(slice1,1,2,3,4,5)
printSlice(slice1)
/*创建切片 slice2是之前切片的两倍*/
slice2 := make([]int,len(slice1),(cap(slice1))*2)
/*拷贝slice1到slice2*/
copy(slice2,slice1)
printSlice(slice2)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
结果是:
len=3 cap=5 slice=[0 0 0]
len=0 cap=0 slice=[]
sl2 切片是空的
len=10 cap=10 slice=[0 1 2 3 4 5 6 7 8 9]
sl3 == [0 1 2 3 4 5 6 7 8 9]
sl3[1:4] == [1 2 3]
sl3[:4] == [0 1 2 3]
sl3[4:] == [4 5 6 7 8 9]
len=0 cap=5 slice=[]
len=2 cap=10 slice=[0 1]
len=3 cap=8 slice=[2 3 4]
11.rang
range关键字用于for循环中迭代数组(array)、切片(slice)、通道(channel)或者集合(map)的元素。在数组中和切片中他返回元素的索引值,在集合中返回key-value对的key值。
package main
import "fmt"
func main() {
//这是我们使用range去求一个slice的和,使用数组跟这个很类似
nums := []int{2, 3, 4, 5}
sum := 0
for _, num := range nums {
sum += num
}
fmt.Println("sum:", sum)
//在数组上使用range将传入index和值两个变量。上面的例子不需要使用元素序号,所以可以用空白符代替。
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字符串
for i,c := range "golang"{
fmt.Println(i,c)
}
}
结果是:
sum: 14
index: 1
b, -> banana
a, -> apple
0 103
1 111
2 108
3 97
4 110
5 103
12.Map 集合
map是一种无序的键值对集合,(和python的dict挺像的)。Map是使用hash表来实现的。
可以是用make,也可以使用map来定义。
/* 声明变量,默认 map 是 nil */
var map_variable map[key_data_type]value_data_type
/* 使用 make 函数 */
map_variable := make(map[key_data_type]value_data_type)
如果不初始化 map,那么就会创建一个 nil map。nil map 不能用来存放键值对。
package main
import (
"fmt"
)
func main(){
var countryCapitalMap map[string] string
//创建集合
countryCapitalMap = make(map[string]string)
//map 插入`key-value`键值对
countryCapitalMap["France"] = "Paris"
countryCapitalMap["Italy"] = "Rome"
countryCapitalMap["Japan"] = "Tokyo"
countryCapitalMap["India"] = "New Delhi"
countryCapitalMap["China"] = "BeiJing"
for country := range countryCapitalMap{
fmt.Println("Capital of ",country," is ",countryCapitalMap[country])
}
//查看元素在集合中是否存在
captial,ok := countryCapitalMap["United States"]
if(ok){
fmt.Println("Captial of United States is ",captial)
}else{
fmt.Println("Capital of United States is not present.")
}
delete(countryCapitalMap,"France")
fmt.Println("Entry for France is deleted.")
for country := range countryCapitalMap{
fmt.Println("Capital of ",country," is ",countryCapitalMap[country])
}
}
结果是:
Capital of Italy is Rome
Capital of Japan is Tokyo
Capital of India is New Delhi
Capital of China is BeiJing
Capital of France is Paris
Capital of United States is not present.
Entry for France is deleted.
Capital of Italy is Rome
Capital of Japan is Tokyo
Capital of India is New Delhi
Capital of China is BeiJing
13.递归函数
func recursion() {
recursion() /* 函数调用自身 */
}
func main() {
recursion()
}
Go 语言支持递归。但我们在使用递归时,开发者需要设置退出条件,否则递归将陷入无限循环中。
递归函数对于解决数学上的问题是非常有用的,就像计算阶乘,生成斐波那契数列等.
阶乘与斐波那契数列:
package main
import "fmt"
func Factorial(n uint64)(result uint64){
if( n > 0){
result = n * Factorial( n-1 )
return result
}
return 1
}
func fibonacci(n int) int {
if n < 2 {
return n
}
return fibonacci(n-2) + fibonacci( n -1)
}
func main(){
//factorial
var i int = 15
fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(uint64(i)))
//fibonacci
var j int
for j = 0; j < 10; j++{
fmt.Printf("%d\t",fibonacci(j))
}
}
结果:
15 的阶乘是 1307674368000
0 1 1 2 3 5 8 13 21 34
14.类型转换
格式:
type_name(expression)
example:
package main
import "fmt"
func main() {
var sum int = 17
var count int = 5
var mean float32
mean = float32(sum)/float32(count)
fmt.Printf("mean 的值为: %f\n",mean)
}
结果:
mean 的值为: 3.400000
15.interface 接口
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] {
/* 方法实现*/
}
实例:
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()
}
运行结果:
I am Nokia,I can call you!
I am iPhone,I can call you
上面定义了一个接口Phone,接口里面有方法call()。然后在main函数里面定义了一个Phone类型变量,并分别为他们赋值为NokiaPhone和IPhone。然后调用call方法。这个和c++有些不一样,虽然也是new,但是不一样的。
16.错误处理
自带的error类型接口:
type error interface {
Error() string
}
例子:
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("math: square root of negative number")
}
// 实现
}
......
result, err:= Sqrt(-1)
if err != nil {
fmt.Println(err)
}
实例2:
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(100,10); errorMsg ==""{
fmt.Println("100/10 = ",result)
}
// 当被除数为零的时候会返回错误信息
if _,errorMsg := Divide(100,0);errorMsg != ""{
fmt.Println("errorMsg is :",errorMsg)
}
}
结果:
100/10 = 10
errorMsg is : Cannot proceed,the divider is zero.
dividee: 100
divider: 0
习惯c++和python的语法要转化一下思维~~~~
注,语法是根据
http://www.runoob.com/go/go-ide.html
来的。