Go语言数据类型和变量和常量

1、Go 语言数据类型

在 Go 编程语言中,数据类型用于声明函数和变量。

数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,

就可以充分利用内存。

Go 语言按类别有以下几种数据类型:

序号类型和描述
1布尔型:布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。
2数字类型:整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位、的运算采用补码。
3字符串类型:字符串就是一串固定长度的字符连接起来的字符序列,Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。
4派生类型:包括:(a) 指针类型(Pointer)、(b) 数组类型、© 结构化类型(struct)、(d) Channel 类型、(e) 函数类型、(f) 切片类型、(g) 接口类型(interface)、(h) Map 类型

1.1 数字类型

Go 也有基于架构的类型,例如:int、uint 和 uintptr。

序号类型和描述
1uint8 无符号 8 位整型 (0 到 255)
2uint16 无符号 16 位整型 (0 到 65535)
3uint32 无符号 32 位整型 (0 到 4294967295)
4uint64 无符号 64 位整型 (0 到 18446744073709551615)
5int8 有符号 8 位整型 (-128 到 127)
6int16 有符号 16 位整型 (-32768 到 32767)
7int32 有符号 32 位整型 (-2147483648 到 2147483647)
8int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)

1.2 浮点型

序号类型和描述
1float32 IEEE-754 32位浮点型数
2float64 IEEE-754 64位浮点型数
3complex64 32 位实数和虚数
4complex128 64 位实数和虚数

1.3 其他数字类型

以下列出了其他更多的数字类型:

序号类型和描述
1byte 类似 uint8
2rune 类似 int32
3uint 32 或 64 位
4int 与 uint 一样大小
5uintptr 无符号整型,用于存放一个指针

go 1.9版本对于数字类型,无需定义int及float32、float64,系统会自动识别。

package main

import "fmt"

func main() {
	var a = 1.5
	var b = 2
	// 1.5 2
	fmt.Println(a, b)
}

在 Go 中,布尔值的类型为 bool,值是 true 或 false,默认为 false。

package main

import "fmt"

// 全局变量声明
var isActive bool

// 忽略类型的声明
var enabled, disabled = true, false

func main() {
	// 一般声明
	var available bool
	// 简短声明
	valid := false
	// 赋值操作
	available = true
	// false
	fmt.Println(isActive)
	// true
	fmt.Println(enabled)
	// false
	fmt.Println(disabled)
	// false
	fmt.Println(valid)
	// true
	fmt.Println(available)
}

strings 库包含了许多高效的字符串常用操作的函数和方法,巧用这些函数与方法,能极大的提高我们程序的性

能。

package main

import (
	"fmt"
	"strings"
)

func main() {
	// 字符串去除空格和换行符
	str := "这里是 www\n.baidu\n.com"
	fmt.Println("-------- 原字符串 ----------")
	/*
		这里是 www
		.baidu
		.com
	*/
	fmt.Println(str)
	// 去除空格
	str = strings.Replace(str, " ", "", -1)
	// 去除换行符
	str = strings.Replace(str, "\n", "", -1)
	fmt.Println("-------- 去除空格与换行后 ----------")
	// 这里是www.baidu.com
	fmt.Println(str)
}

2、Go 语言变量

变量来源于数学,是计算机语言中能储存计算结果或能表示值抽象概念。

变量可以通过变量名访问。

Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。

声明变量的一般形式是使用 var 关键字:

var identifier type

可以一次声明多个变量:

var identifier1, identifier2 type
package main

import "fmt"

func main() {
	var a string = "Runoob"
	// Runoob
	fmt.Println(a)
	var b, c int = 1, 2
	// 1 2
	fmt.Println(b, c)
}

2.1 变量声明

第一种,指定变量类型,如果没有初始化,则变量默认为零值。

var v_name v_type
v_name = value

零值就是变量没有做初始化时系统默认设置的值。

package main

import "fmt"

func main() {
	// 声明一个变量并初始化
	var a = "RUNOOB"
	// RUNOOB
	fmt.Println(a)
	// 没有初始化就为零值
	var b int
	// 0
	fmt.Println(b)
	// bool 零值为 false
	var c bool
	// false
	fmt.Println(c)
}
  • 数值类型(包括complex64/128)为 0
  • 布尔类型为 false
  • 字符串为 ""(空字符串)
  • 以下几种类型为 nil:
var a *int
var a []int
var a map[string] int
var a chan int
var a func(string) int
// error 是接口
var a error 
package main

import "fmt"

func main() {
	var i int
	var f float64
	var b bool
	var s string
	// 0 0 false ""
	fmt.Printf("%v %v %v %q\n", i, f, b, s)
}

第二种,根据值自行判定变量类型。

var v_name = value
package main

import "fmt"

func main() {
	var d = true
	// true
	fmt.Println(d)
}

第三种,如果变量已经使用 var 声明过了,再使用 := 声明变量,就产生编译错误,格式:

v_name := value

例如:

var intVal int 
// 这时候会产生编译错误,因为intVal已经声明,不需要重新声明
intVal :=1 

直接使用下面的语句即可:

// 此时不会产生编译错误,因为有声明新的变量,因为 := 是一个声明语句
intVal := 1 

intVal := 1 相等于:

var intVal int 
intVal =1 

可以将 var f string = "test" 简写为 f := "test"

package main

import "fmt"

func main() {
	// var f string = "test"
	// var f string,f = "test"
	f := "test"
	// test
	fmt.Println(f)
}

2.2 多变量声明

//类型相同多个变量,非全局变量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3
// 和 python 很像,不需要显示声明类型,自动推断
var vname1, vname2, vname3 = v1, v2, v3 
// 出现在:=左侧的变量不应该是已经被声明过的,否则会导致编译错误
vname1, vname2, vname3 := v1, v2, v3 
// 这种因式分解关键字的写法一般用于声明全局变量
var (
    vname1 v_type1
    vname2 v_type2
)
package main

var x, y int

// 这种因式分解关键字的写法一般用于声明全局变量
var (
	a int
	b bool
)
var c, d int = 1, 2
var e, f = 123, "hello"

// 这种不带声明格式的只能在函数体中出现
// g, h := 123, "hello"
func main() {
	g, h := 123, "hello"
	// 0 0 0 false 1 2 123 hello 123 hello
	println(x, y, a, b, c, d, e, f, g, h)
}

2.3 值类型和引用类型

所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值。

当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝。

你可以通过 &i 来获取变量 i 的内存地址,例如:0xf840000040(每次的地址都可能不一样)。

值类型变量的值存储在堆中。

内存地址会根据机器的不同而有所不同,甚至相同的程序在不同的机器上执行后也会有不同的内存地址。因为每台

机器可能有不同的存储器布局,并且位置分配也可能不同。

更复杂的数据通常会需要使用多个字,这些数据一般使用引用类型保存。

一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置。

这个内存地址称之为指针,这个指针实际上也被存在另外的某一个值中。

同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一

种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。

当使用赋值语句 r2 = r1 时,只有引用(地址)被复制。

如果 r1 的值被改变了,那么这个值的所有引用都会指向被修改后的内容,在这个例子中,r2 也会受到影响。

2.4 简短形式,使用 := 赋值操作符

我们知道可以在变量的初始化时省略变量的类型而由系统自动推断,声明语句写上 var 关键字其实是显得有些多余

了,因此我们可以将它们简写为 a := 50 或 b := false。

a 和 b 的类型(int 和 bool)将由编译器自动推断。

这是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值。

使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。

如果在相同的代码块中,我们不可以再次对于相同名称的变量使用初始化声明,例如:a := 20 就是不被允许的,

编译器会提示错误 no new variables on left side of :=,但是 a = 20 是可以的,因为这是给相同的变量赋予一个

新的值。

如果你在定义变量 a 之前使用它,则会得到编译错误 undefined: a。

如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误,例如下面这个例子当中的变量

a:

package main

import "fmt"

func main() {
	var a string = "abc"
	fmt.Println("hello, world")
}

尝试编译这段代码将得到错误 a declared but not used

此外,单纯地给 a 赋值也是不够的,这个值必须被使用,所以使用

fmt.Println("hello, world", a)

会移除错误。

但是全局变量是允许声明但不使用的。 同一类型的多个变量可以声明在同一行,如:

var a, b, c int

多变量可以在同一行进行赋值,如:

var a, b int
var c string
a, b, c = 5, 7, "abc"

上面这行假设了变量 a,b 和 c 都已经被声明,否则的话应该这样使用:

a, b, c := 5, 7, "abc"

右边的这些值以相同的顺序赋值给左边的变量,所以 a 的值是 5, b 的值是 7,c 的值是abc。

这被称为并行或同时赋值。

如果你想要交换两个变量的值,则可以简单地使用 a, b = b, a,两个变量的类型必须是相同。

空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。

_ 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你

并不需要使用从一个函数得到的所有返回值。

并行赋值也被用于当一个函数返回多个返回值时,比如这里的 val 和错误 err 是通过调用 Func1 函数同时得到:

val, err = Func1(var1)。

空白标识符在函数返回值时的使用:

package main

import "fmt"

func main() {
	//只获取函数返回值的后两个
	_, numb, strs := numbers()
	// 2 str
	fmt.Println(numb, strs)
}

// 一个可以返回多个值的函数
func numbers() (int, int, string) {
	a, b, c := 1, 2, "str"
	return a, b, c
}

3、Go 语言常量

常量是一个简单值的标识符,在程序运行时,不会被修改的量。

常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。

常量的定义格式:

const identifier [type] = value

你可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型。

  • 显式类型定义: const b string = "abc"
  • 隐式类型定义: const b = "abc"

多个相同类型的声明可以简写为:

const c_name1, c_name2 = value1, value2

以下实例演示了常量的应用:

package main

import "fmt"

func main() {
	const LENGTH int = 10
	const WIDTH int = 5
	var area int
	//多重赋值
	const a, b, c = 1, false, "str"
	area = LENGTH * WIDTH
	// 面积为 : 50
	fmt.Printf("面积为 : %d\n", area)
	// 1 false str
	println(a, b, c)
}

常量还可以用作枚举:

const (
    Unknown = 0
    Female = 1
    Male = 2
)

数字 0、1 和 2 分别代表未知性别、女性和男性。

常量可以用 len(), cap(), unsafe.Sizeof() 函数计算表达式的值。

常量表达式中,函数必须是内置函数,否则编译不过:

package main

import "unsafe"

const (
	a = "abc"
	b = len(a)
	c = unsafe.Sizeof(a)
)

func main() {
	// abc 3 16
	println(a, b, c)
}

字符串字节长度:字符串类型在 go 里是个结构,包含指向底层数组的指针和长度,这两部分每部分都是 8 个字

节,所以字符串类型大小为 16 个字节。

3.1 iota

iota,特殊常量,可以认为是一个可以被编译器修改的常量。

iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数

一次(iota 可理解为 const 语句块中的行索引)。

iota 可以被用作枚举值:

const (
    a = iota
    b = iota
    c = iota
)

第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形

式:

const (
    a = iota
    b
    c
)

3.2 iota 用法

package main

import "fmt"

func main() {
	const (
		a = iota //0
		b        //1
		c        //2
		d = "ha" //独立值,iota += 1
		e        //"ha"   iota += 1
		f = 100  //iota +=1
		g        //100  iota +=1
		h = iota //7,恢复计数
		i        //8
	)
	// 0 1 2 ha ha 100 100 7 8
	fmt.Println(a, b, c, d, e, f, g, h, i)
}

再看个有趣的的 iota 实例:

package main

import "fmt"

const (
	i = 1 << iota
	j = 3 << iota
	k
	l
)

func main() {
	// i= 1
	fmt.Println("i=", i)
	// j= 6
	fmt.Println("j=", j)
	// k= 12
	fmt.Println("k=", k)
	// l= 24
	fmt.Println("l=", l)
}

iota 表示从 0 开始自动加 1,所以 i=1<<0**, **j=3<<1(<< 表示左移的意思),即:i=1, j=6,这没问题,关键在 k 和

l,从输出结果看 k=3<<2**,**l=3<<3。

简单表述:

  • i=1:左移 0 位,不变仍为 1。

  • j=3:左移 1 位,变为二进制 110,即 6。

  • k=3:左移 2 位,变为二进制 1100,即 12。

  • l=3:左移 3 位,变为二进制 11000,即 24。

左移运算符 << 是双目运算符。左移 n 位就是乘以 2 的 n 次方。 其功能把 << 左边的运算数的各二进位全部左

移若干位,由 << 右边的数指定移动的位数,高位丢弃,低位补 0。

右移运算符 >> 是双目运算符。右移 n 位就是除以 2 的 n 次方。 其功能是把 >> 左边的运算数的各二进位全部右

移若干位, >> 右边的数指定移动的位数。

在定义常量组时,如果不提供初始值,则表示将使用上行的表达式。

package main

import "fmt"

const (
	a = 1
	b
	c
	d
)

func main() {
	// 1
	fmt.Println(a)
	// b、c、d没有初始化,使用上一行(即a)的值
	// 1
	fmt.Println(b)
	// 1
	fmt.Println(c)
	// 1
	fmt.Println(d)
}

iota 只是在同一个 const 常量组内递增,每当有新的 const 关键字时,iota 计数会重新开始。

package main

const (
	i = iota
	j = iota
	x = iota
)
const xx = iota
const yy = iota

func main() {
	// 0 1 2 0 0
	println(i, j, x, xx, yy)
}

其它常见的写法:

// 类似枚举的iota
const (
	c0 = iota // c0 = 0
	c1 = iota // c0 = 1
	c2 = iota // c0 = 2
)

// 简写模式
const (
	c0 = iota  // c0 = 0
	c1		   // c0 = 1
	c2		   // c0 = 2
)

// 注意iota逐行增加
const (
	a = 1 << iota	// a = 1 (iota = 0)
	b = 1 << iota	// b = 1 (iota = 1)
	c = 3			// c = 3 (iota = 2, unused)
	d = 1 << iota	// d = 8 (iota = 3)
)

const(
    u = iota * 42        // u == 0 (untyped integer constant)
    v float = iota * 42  // v == 42.0 (float64 constant)
	w = iota * 42        // w == 84 (untyped integer constant) 
)

// 分开的const语句, iota每次从0开始
const x = iota	// x = 0
const y = iota	// y = 0

4、自定义类型

type newtype oldtype

newtype 和 oldtype 是两个完全不同的类型,newtype 不会继承 oldtype 的方法。无论 oldtype 是什么类型,使

用 type 声明的新类型都是一种命名类型,也就是说,自定义类型都是命名类型。

// INT是一个使用预声明类型声明的自定义类型
type INT int 
// Map是一个使用类型字面量声明的自定义类型
type Map map[string]string
// myMap是一个自定义类型Map声明的自定义类型
type myMap Map
// INT、Map、myMap 都是命名类型
package main

import (
	"fmt"
)

type Map map[string]string

func (m Map) Print() {
	for _, key := range m {
		fmt.Println(key)
	}
}

type iMap Map

func (m iMap) Print() {
	for _, key := range m {
		fmt.Println(key)
	}
}

type slice []int

func (s slice) Print() {
	for _, v := range s {
		fmt.Println(v)
	}
}

func main() {
	mp := make(map[string]string, 10)
	mp["hi"] = "tata"
	var ma Map = mp
	// 如下语句不能通过编译
	//var im iMap = ma
	var im iMap = (iMap)(ma)
	//var im iMap = mp

	ma.Print()
	im.Print()

	//Map实现了Print()所以其可以赋值给接口变量
	var i interface {
		Print()
	} = ma

	i.Print()
	s1 := []int{1, 2, 3}
	var s2 slice
	s2 = s1
	s2.Print()

	s := "hello,世界!"
	var a []byte
	a = []byte(s)
	var b string
	b = string(a)
	var c []rune
	c = []rune(s)
	fmt.Printf("%T\n", a)
	fmt.Printf("%T\n", b)
	fmt.Printf("%T\n", c)
}
# 程序输出
tata
tata
tata
1
2
3
[]uint8
string
[]int32

5、Go语言类型转换

由于 Go 是强类型的语言,如果不满足自动转换的条件,则必须进行强制类型转换。任意两个不相干的类型如果进

行强制转换,则必须符合一定的规则。

类型转换用于将一种数据类型的变量转换为另外一种类型的变量。Go 语言类型转换基本格式如下:

type_name(expression)

type_name 为类型,expression 为表达式。

// 以下实例中将整型转化为浮点型,并计算结果,将结果赋值给浮点型变量
package main

import "fmt"

func main() {
	var sum int = 17
	var count int = 5
	var mean float32
	mean = float32(sum) / float32(count)
	// mean 的值为: 3.400000
	fmt.Printf("mean 的值为: %f\n", mean)
}
// go 不支持隐式转换类型
package main

import "fmt"

func main() {
	var a int64 = 3
	var b int32
	b = a
	fmt.Printf("b 为 : %d", b)
}

此时会报错:

cannot use a (variable of type int64) as type int32 in assignment

但是如果改成 b = int32(a) 就不会报错了:

// 如果改成 b = int32(a) 就不会报错了
package main

import "fmt"

func main() {
	var a int64 = 3
	var b int32
	b = int32(a)
	// b 为 : 3
	fmt.Printf("b 为 : %d", b)
}

注意:

(1)、数值类型和 string 类型之间的相互转换可能造成值部分丢失;其他的转换仅是类型的转换,不会造成值的改

变。string 和数字之间的转换可使用标准库 strconv。

(2)、Go语言没有语言机制支持指针和 interger 之间的直接转换,可以使用标准库中的 unsafe 包进行处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值