GO语言基础(1)

1.基本语法

1.1变量的声明与初始化

变量的声明  

var name T  

var是关键词

name表示变量名称

T表示类型 ,eg.int string float等


//常见的变量声明
var c []float      //声明一个float类型的切片

var d stract{
    x int          //声明一个匿名结构体,该结构体有一个int类型的字段
}

var e func() bool  //声明一个函数变量

var(
f int
g string           //同时声明多个表变量
)

在go语言中,每一个声明的变量都必须被使用,否则编译不会通过

变量的赋值(=)

  • 直接赋值
var a int =100
  • 利用GO语言提供的类型推导语法糖特性,精简为以下的样式:
var a = 100
b := "Hello"

省略了类型属性,编译器会根据右边的表达式来推测类型

【:=】不能出现在全局变量的声明和初始化中

var a =100       
a := 100         //编译报错
a,b := 100,"OK"  //无异常

【:=】这个短变量声明初始化时,左值的变量中至少有一个变量必须是未定义过的变量,否则编译出错。

  • GO语言还提供了多重赋值和匿名变量的语法糖特性

传统语言赋值时,需要第三方临时变量来执行

var a int = 1
var b int = 2
var tmp int 

tmp = a
a = b
b =tmp

而在GO中,我们通过多重赋值的特性轻松实现类似的变量交换任务

var a int =1
var b int =2

b,a = a,b

 在多重赋值的过程中,变量的左值和右值按照从左往右的顺序赋值

Go语言支持多返回值和多重赋值,但是有时候不需要某些左值,可以使用匿名变量

surname,_:= getName()       //使用匿名变量
_,personalName :=getName()  //使用匿名变量

通过在不需要的变量声明的地方使用【_】代替变量名,我们可以忽略不需要的左值。匿名变量不占用命名空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无非使用

1.2原生数据基本类型

整型

  • 按照整形的长度划分:int8,int16,int32,int64
  • 按照有无符号划分:uint8,uint16,uint32,uint64

整型类型之间可以互相转化,高长度类型向低长度类型转化会发生长度截取,只会保留高长度类型的低位值,造成转化错误,实际使用要注意

var e uint16 = math.MaxUint8 + 1
fmt.Printf("e valud(uint16) is %v\n",e)
var d = uint8(e)
fmt.Printf("d valud(uint8) is %v\n",d)

//输出如下:
e valud(uint16) is 256
d valud(uint8) is 0

因为256在uint16底层的存储方式为:“00000001 00000000”,转化为uint8之后,只是截取后面8位,故为0 

浮点型

  • float32 
  • float64
fmt.Printf("%f\n",math.E)    //按照默认宽度和精度输出
fmt.Printf("%2f\n",math.E)    //按照默认宽度和2位精度输出

布尔型

  • true
  • fasle

不可与整形进行强制转化,无法参与数值运算

字符串型

区分byte 和rune(基于UTF-8)

f := "GO编程"
fmt.Printf("byte len of f is %v\n",len(f))
fmt.Printf("rune len of f is %v\n",utf8.RuneCountInString(f))


//输出结果
byte len of f is 
rune len of f is 8

统计byte的长度,它的类型是”uint8“,代表了一个ASCII字符

统计rune的长度,它的类型是”uint32“,代表了一个UTF-8字符,可类比为java的char型

由于中文字符在UTF8中占用了三个字节,所以使用len方法时获得的中文字符的长度为6个,而utf8,RuneCountInString()方法统计的时字符串的Unicode字符数量

rune能处理一切的字符,而byte仅仅局限于处理ASCII字符

1.3指针

GO语言中限制了指针类型的偏移和运算能力,使得指针类型具备了指针高效访问的特性,但是不会发生指针偏移,避免了非法修改敏感数据的问题。同时GO语言中提供了自动发髻回收机制,也是减少了对指针占用内存回收的复杂性

  • 指针地址
  • 指针类型
  • 指针取值

在程序运行的过程中,每一个变量的值都保存在内存中,变量对应的内存有其特定的地址。假设某一个变量的类型为T,在GO中,我们可以通过取址符号&获取该变量对应内存的地址,生成该变量对应的指针。此时,变量的内存地址即生成的指针的值,指针类型为”*T“,成为T的指针类型,”*“代表了指针 

//Pointer.go
package main
import "fmt"

func main(){
//声明一个string类型
str := "Hello world"

//获取指针
strPrt := &str

fmt.Printf("str type is %T,and value is %v\n",str ,str)
fmt.Printf("strPrt type is %T,and value is %v\n",strPrt,strPrt)


}


//执行结果
str type is string ,and value is Hello world
strPrt type is *string,and value 0x00000645

str 类型为string,它的指针strPrtd 类型为*string,指针的值就是str内存中的地址

还可以继续对strPrt进行取址操作

strPrtPrt := &strPrt
fmt.Printf("strPrtPrt type is %T,and value is %v\n",strPrtPrt,strPrtPrt)

//输出结果为
strPrtPrt type is **string,and value is 0xc0004556

此时获取了strPrt对应的内存地址,并且保存在strPrtPrt指针中

【实例1】

package main
import "fmt"

func main(){
str := "Hello world"
strPrt := &str

fmt.Printf("str type is %T,value is %v,address is %p\n",str.str,&str)
fmt.Printf("strPrt type is %T,value is %v",strPrt,strPrt)

newStr := *strPrt  //获取指针对应变量的值
fmt.Printf("newStr type is %T,value is %v,and address is %p\n",newStr,newStr,&newStr)

*strPrt = "jAVA IS GOOD TOO" //通过指针对变量赋值
fmt.Printf("newStr type is %T,value is %v,and address is %p\n",newStr,newStr,&newStr)
fmt.Printf("str type is %T,value is %v,and address is %p\n",str,str,&str)

}


//输出结果为
str type is string,value is Hello world,address is 0xc0000621c0
strPrt type is *string,value is  0xc0000621c0
newStr type is string,value is  Hello world,and address is  0xc0000621f0
newStr type is string,value is  Hello world,and address is  0xc0000621f0
str type is string,value is jAVA IS GOOD TOO,and address is 0xc0000621c0

通过strPrt指针获取str的值赋予给newStr变量,可以观察到str和newstr是两个不同的变量,他们对应的内存地址是不一样的 ,赋值过程中发生了值拷贝。值拷贝会创建新的内存空间,然后将原有变量的值复制到新的内存空间中,形成两个独立的变量。通过指针修改str变量的值并不会影响到newstr,因为这两个变量所对应的 内存地址是不一样的 

除了使用&对变量进行取址操作创建指针,还可以使用new函数直接分配内存,并且返回指向内存的指针,此时内存中的值会被初始化为类型的默认值。

str := new(string)
*str = "Hello world"

在上述代码中,通过new函数创建了一个*string指针,并且通过指针对其进行赋值

在GO语言的flag包中,命令行参数一般以指针的返回

【实例2】使用flag从命令行中读取参数

package main
import(
"flag"
"fmt"
)

func main(){
//定义一个类型为string,名称为surname的命令行参数
//参数依次是命令行参数的名称,默认值,提示
surname := flag.String("surname","王",”您的姓“)
//定义一个类型为string,名称为personalName的命令行参数
//除了返回指针类型结果,还可以直接传入变量地址获取参数值
var personalName string
flag.StringVar(&personalName,"personalName","小二",”您的名”)
//定义一个类型为int,名称为id的命令行参数
id := flag.Int("id",0,"您的id")
//解析命令行参数
flag.Parse()
fmt.Printf("I am %v %v,and my id is %v\n",*surname,personalName,*id)

}

在上述代码可以看出,除了直接获取指针类型的返回结果,还可以将参数变量的指针传递给 flag.*Val方法,获取 命令行参数的 值。输入一下的 执行参数

go run Flag.go -surname="苍" -personalName="小屋" -id=100

go语言中flag支持多种样式的命令行参数,包括

-id = 100
--id = 100
-id 100
--id 100

 输出结果

 I  am 苍 小屋, and my  id  is 100

1.4 常量与类型别名

  • 常量的值在是声明后不允许变化的。通过const关键词可以声明常量

const str string = "Hello world"

类型推导,能省略常量声明时的类型和同时声明多个 常量

const name = " Hello world"
const(
surname = "王"
personalName = “小二”
)
  • 类型别名的样式

type name = T

与之对应的,类型定义的样式 

type name T

区分: 

类型别名的类型还是T类型,而类型定义是重新定义,属于新的类型

1.5 分支与循环控制

  • if
if expression1{                //go语言规定【{】必须与if和表达式位于同一行,否则报错

        branch1}

else if expression2{        //else也是必须与上一个分支的【}】位于同一行

        branch2}

else{

        branch3}
  •  switch
//根据人名来分配工作
name := "小红"
switch name {
case "小明":
    fmt.Println("扫地")
case"小红":
    fmt.Println("擦黑板")
case"小刚":
    fmt.Println("倒垃圾")
default:
fmt.Println("没人干活")
}
  • for

GO语言中,仅提供for关键词,没有其他语言中提供的while或者do-while形式

for init;condition;end{
循环代码
}

猜你喜欢

转载自blog.csdn.net/weixin_49489840/article/details/124161002