go语言入门2

2023-11-28 20:11:38 浏览数 (1)

这节讲一下go语言的变量定义。go是静态语言类型,不像python、php语言,它不能在运行期间改变变量的类型(除非进行类型转换,参见 go语言入门扩展--4种类型转换)。

一、变量

1.1 变量定义方式:

变量的定义常用的有3种方式:

  1. var 变量名 类型 , 如 var n int
  2. var 变量名 = 值, 如 var n = 1
  3. 变量名 := 值, 如 n := 1

方式1是完整的定义方式,书写比较繁琐,每次定义1个变量都要完整的写出var关键字、变量名、变量类型3个部分;

方式2和方式3可以理解为偷懒的写法。其中方式2省略了变量类型,交由编译器推断;方式3在方式2的基础上进一步偷懒(var关键字由:号代替)。

实际开发中方式3会用的比较多,但在需要明确限制变量类型的场景下,必须使用方式1

代码语言:go复制
package main

import "fmt"


func main() {
	a := 123  // 自动推断类型,int
	b := 123.0 // 变动推断类型, float64
	var c int64 = 123 // 明确指定类型,int64
	fmt.Printf("a: %Tn", a)
	fmt.Printf("b: %Tn", b)
	fmt.Printf("c: %Tn", c)
    /*
    输出
    a: int
    b: float64
    c: int64
    */
}

1.2 多变量声明

上面提到的3种变量定义方式,同样支持一次多个变量的声明,对应写法为:

  1. var x1, x2 type // x1,x2为同类型
  2. var x1, x2 = val1, val2 // x1,x2可以为不同类型,具体类型由编译器根据右边的值进行推断
  3. x1, x2 := val1, val2 // 同上,有编译器根据右边推断左边的类型
代码语言:go复制
package main

import "fmt"

func main() {
	var a, b int = 1, 2 // 等同 var a, b = 1, 2
	fmt.Println(a, b)
	c, d := "hello", 12 // 可以不同类型
	fmt.Println(c, d)
}
/*
输出:
1 2
hello 12
*/

使用多变量声明时,编译器会先计算所有的相关值,然后再从左往右依次赋值

代码语言:go复制
package main

import "fmt"

func main() {
	i, dataList := 0, [3]int{1, 2, 3} // i=0, dataList=[1, 2, 3]
	fmt.Println(i, dataList) // 输出: 0, [1, 2, 3]

    i, dataList[i] = 2, 100 // 等同i, dataList[0] = 2, 100
    /*
    需要注意,这个语句的效果不等同下面这2个语句:
        i = 2
        dataList[i] =100
    编译器会先计算该语句相关项的值,经过计算后,语句等效于:
        i, dataList[0] = 2, 100
    可以理解为:
        赋值语句x = y所表达的含义是:在x所代表的内存地址中存入值y,即=号左边是内存地址,=号右边是可确定的具体值。
    回到这个语句上:
        左边: 
            i: 变量,代表确定的内存地址
            dataList[i]: 需要i来确定地址,因而带入i的值来定位具体的内存地址, i=0,因而变为dataList[0]
        右边:
            2个值都是确定的常量值,无需计算
    */
	fmt.Println(i, dataList)  // 2  [100, 2, 3]

	i, dataList[i] = dataList[i], 222 // 等同 i, dataList[2] = 3, 222
    /*
参照上面的理解,编译器会先计算=号左边地址相关的项、=号右边确定数值的项。
i = 2
=号左边计算可确定的内存地址: i, dataList[2] 
=号右边计算可确定的值: dataList[2] , 222
因而这个语句等效于:
i, dataList[2] = dataList[2], 222
    */
	fmt.Println(i, dataList) // 3 [100 2 222]
}

1.3 变量的重新赋值和同名变量

变量一旦声明后,便不能再重新声明,否则会报错

代码语言:go复制
package main

import "fmt"

func main() {
	var a int
	a := 12 //no new variables on left side of := 
	fmt.Println(a)
}

但如果声明语句中包含有未声明过的变量,那么是不会报错的,那些已经声明过的变量会被重新赋值(不会重新声明,仅仅重新赋值)

代码语言:go复制
package main

import "fmt"

func main() {
	s := "abc"  // 声明并初始化了变量s
	fmt.Println(&s, s) // 0xc000010240 abc
    // s := "hello" 单独这个语句是不允许的,重复声明
	s, y := "hello", 1 // y是新的变量,s是已经声明的变量,在这里会被重新赋值
	fmt.Println(&s, s, y) // 0xc000010240 hello 1  变量s的地址没有改变,仅仅被重新赋值
}

在同一个代码作用域内,变量不能重复声明,但在不同作用域,可以声明同名变量。还是以上面的代码为例

代码语言:go复制
package main

import "fmt"

func main() {
	s := "abc"
	fmt.Println(&s, s)  // 0xc000010240 abc
	s, y := "hello", 1
	fmt.Println(&s, s, y) // 0xc000010240 hello 1
	{ // 进入代码块
		s := "world" // 不同作用域,可以声明同名变量
		fmt.Println(&s, s) // 0xc000010270 world  此处s的地址值和外面s的地址值不同
	} // 离开这个代码块后,作用域中的s便销毁了
	fmt.Println(&s, s) // 0xc000010240 hello 留意这里s的地址值
}

二、常量

编译期可以确定的值,如数字、字符串、布尔值、函数返回值等等,使用const关键字进行定义,不可修改

代码语言:go复制
const x, y int = 1, 2
const msg = "hello world" // 可以不指定类型,有编译器自行推断
// 可以使用()定义一组常量,如
const (
    a = "hello"
    b = "world"
    c = len(a) // 可以是函数返回值
    d  // 不给表达式,那么会采用上1个变量的表达式,即等同: d = len(a)
)

iota枚举值

iota是一个从0开始、按行计数的自增枚举值。iota在遇到const关键字被重置为0,否则会一直按行递增。也可以同时提供多个iota,它们各自增长。中途可以打断iota(但依然会自增计数),但恢复时需要显示恢复。

代码语言:go复制
package main

import "fmt"

const (
	a = iota //a = 0
	b //b = 1 未给表达式,采用上一个变量的表达式,即等同 b=iota 
	c = "hello" // 打断iota自增
	d = "world"
	e = iota // e = 4 显示恢复,c/d的两行会被计数,因而这里为4
)
const (
	f = iota // f = 0 遇到const关键字,从0开始
	g  // g = 1
	h = iota // h = 2
)
// 同时提供多个iota
const (
	m1, n1 = iota, iota //  m1=0  n1=0
	m2, n2 // m2=1 n2=1
	m3, n3 = "hello", iota // 打断第一个iota, m3="hello" n3=2
	m4, n4 = iota, iota // 显示恢复 m4=3 n4=3
)

func main() {
	fmt.Println("a=", a, "b=", b, "c=", c, "d=", d, "e=", e)//a= 0 b= 1 c= hello d= world e= 4
	fmt.Println("f=", f, "g=", g, "h=", h) //f= 0 g= 1 h= 2
	fmt.Println("m1=", m1, "n1=", n1) //m1= 0 n1= 0
	fmt.Println("m2=", m2, "n2=", n2) // m2= 1 n2= 1
	fmt.Println("m3=", m3, "n3=", n3)// m3= hello n3=2
	fmt.Println("m4=", m4, "n4=", n4)// m4= 3 n4= 3
}

字符串

字符串是不可变的字节系列,通常用来存储可读的文本,其内部用指针指向UTF-8的字节数组[]byte。当字符串中的字符是ASCII码表上的字符时则占用1个字节,其他字符根据需要占用2~4个字节,比如1个中文占用3个字节。

  • 字符串默认为空串""
  • 可以用索引号访问底层字节数组的某个字节,如s[1]
  • 不能用序号获取字节数组中某个字节的地址,&s[1]非法
  • 不能修改字节数组中的元素, s[1] = 'b' 非法
  • 字节数组末尾不包含NULL
  • 使用len获取字符串长度返回字符数组的长度,如包含中文,可转为[]rune再获取长度(可将1个中文长度记为1)
代码语言:go复制
package main

import "fmt"

func main() {
	s := "hello张三"
	fmt.Println(s[1], string(s[1]))  // 101  e   字符e在ASCII编码表id 101
	// var p *byte = &s[1] 非法 Cannot take the address of 's[1]'
	// s[1] = 'o'  非法 Cannot assign to s[1]
	fmt.Println(len(s), string(s[1]), string(s[5])) // 11 e å   一个中文占3个字节
	rs := []rune(s)
	fmt.Println(len(rs), string(rs[1]), string(rs[5])) // 7 e 张   转为rune,每个中文长度1
}

0 人点赞