前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go语言入门2

go语言入门2

原创
作者头像
willsiom
发布2023-11-28 20:11:38
1410
发布2023-11-28 20:11:38
举报
文章被收录于专栏:沈钦华的专栏

这节讲一下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: %T\n", a)
	fmt.Printf("b: %T\n", b)
	fmt.Printf("c: %T\n", 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
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、变量
    • 1.1 变量定义方式:
      • 1.2 多变量声明
        • 1.3 变量的重新赋值和同名变量
        • 二、常量
          • iota枚举值
            • 字符串
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档