go语言基础之数据类型,变量,常量

go语言基础之数据类型,变量,常量

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) 数组类型(c) 结构化类型(struct)(d) Channel 类型(e) 函数类型(f) 切片类型(g) 接口类型(interface)(h) Map 类型



布尔类型

package main

import "fmt"

var doing bool

func main() {
	fmt.Println("布尔值默认是:", doing)
}


//输出结果为
布尔值默认是: false

image-20220316225106336


package main

import "fmt"

var doing bool = true

func main() {
	fmt.Println("当前布尔值是:", doing)
}


//输出结果:
当前布尔值是: true

image-20220316225350643




数字类型

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

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

浮点型

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



其他数字类型

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

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

格式化和获取数据类型

我们可以使用fmt.Printf 来进行格式化。 %s 替换strin类型,%d 替换int类型,%f替换小数类型,%T获取值的数据类型

// %s 替换string 类型 ,%d 替换int 类型 ,%f 替换小数类型, %T ,变量的值类型
package main

import "fmt"

var a = "zhangsan"
var b = 5
// 注意,c的类型是 float64 
var c = 10.0

func main() {
	fmt.Printf("变量a 的值是 %s,变量b 的值是 %d, 变量c的值是%f,类型是: %T", a, b, c, c)
}

 // 输出结果:
 变量a 的值是 zhangsan,变量b 的值是 5, 变量c的值是10.000000,类型是: float64

数据类型转换

整型和浮点类型的数据不能够直接做运算,需要先将数据 转换为同一个类型

package main

import "fmt"

// 有两个变量,值分别为为 10 和 100.0
var a = 10
var b = 100.0

func main() {
	// 使用格式化字符串和%T ,获取数据类型
	// 此时,a是int 类型,b则是float64类型
	fmt.Printf("a的数值类型是:%T,b的数值类型是:%T", a, b)
	// 声明并定义变量result
	// 使用float64(),将int 类型的变量a 转换我为float64类型
	result := float64(a) * b
	fmt.Println("\n结果是:", result)
}

//输出结果:

a的数值类型是:int,b的数值类型是:float64
结果是: 1000


Go 语言变量

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

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

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

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

var identifier type

可以一次声明多个变量:

var identifier1, identifier2 type


package main
import "fmt"
func main() {
    var a string = "Runoob"
    fmt.Println(a)

    var b, c int = 1, 2
    fmt.Println(b, c)
}

以上实例输出结果为:
Runoob
1 2


变量声明

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

var v_name v_type
v_name = value

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


package main
import "fmt"
func main() {
    // 声明一个变量并初始化
    var a = "abc"
    fmt.Println(a)

    // 没有初始化就为零值
    var b int
    fmt.Println(b)

    // bool 零值为 false
    var c bool
    fmt.Println(c)
}

// 输出结果为:
abc
0
false
  • 数值类型(包括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
    var a error // error 是接口
    


package main

import "fmt"

// 定义两个变量,name 为字符串类型, age 为整型
var name string = "zhangsan"
var age int = 10

func main() {
	// 字符串 使用 %s,  数字 使用%d
	fmt.Printf("名字是:%s,年龄是:%d", name, age)
}

输出结果是:
0 0 false ""



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

var v_name = value

package main

import "fmt"

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

//输出结果为:
true


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

注意  :=  这种方式只能在函数体 内部 使用

格式:

v_name := value

例如:

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

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

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

intVal := 1 相等于:

var intVal int 
intVal =1 

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


package main

import "fmt"

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

//输出结果:
abc


多变量声明

//类型相同多个变量, 非全局变量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3

var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声明类型,自动推断

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"
	println(x, y, a, b, c, d, e, f, g, h)
}


// 输出结果:
0 0 0 false 1 2 123 hello 123 hello

值类型和引用类型

值类型变量的值存储在堆中,值类型变量,修改一个变量,不会影响另外一个,因为它们使用的是不同的内存地址

引用类型,修改一个值,另一个也发生改变。因为它们的指针指向的是同一个内存地址


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

4.4.2_fig4.1


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

4.4.2_fig4.2

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

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

内存地址会根据机器的不同而有所不同,甚至相同的程序在不同的机器上执行后也会有不同的内存地址。因为每台机器可能有不同的存储器布局,并且位置分配也可能不同。

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

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

4.4.2_fig4.3

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

同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。

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

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


package main

import "fmt"

//变量在内存中的存储方式
//定义的全局变量,属于文件main.go,直接放入在栈中,压栈时先进入,则弹栈时后出,遵循先进后出的原则
var num int = 10
var sum int = 20

func main() {
	//局部变量,属于函数体 main, 变量产生在 堆中.遵循先进先出。其生命随着函数的结束而结束
	//main 通过指针指向 堆中的位置
	var test1 = "hello"
	var test2 = "world"

	fmt.Println("num=", num)
	// 调用内存地址使用&符号
	fmt.Println("num的内存地址是:", &num)
	fmt.Println("sum的内存地址是:", &sum)

	// 调用函数中变量
	fmt.Println(test1)
	fmt.Println(test2)
}

//输出结果:
num= 10
num的内存地址是: 0xa3a258
sum的内存地址是: 0xa3a260
hello
world
// 在声明了变量后, 如果改变它的值,不会修改它的内存地址
package main

import (
	"fmt"
)

var a int

func main() {
	fmt.Println(&a)
	a = 10
	fmt.Println(&a)
	a = 1010100000
	fmt.Println(&a)
}


// 输出结果:
0x816168
0x816168
0x816168


// 值变量类型示例
package main

import "fmt"

func main() {
  //值类型变量的值存储在堆中,值类型变量,修改一个变量,不会影响另外一个,因为它们使用的是不同的内存地址
	var a = 10
	var b = a
	fmt.Println("a=", a, "\nb=", b)
	fmt.Println("a=", &a, "\nb=", &b)
}

//输出结果:
a= 10 
b= 10
a= 0xc0000aa058 
b= 0xc0000aa070

// 演示引用类型

package main

import "fmt"

//类
type Car struct {
	number int
}

//用用类型的变量
func main() {
	//创建对象c1
	c1 := new(Car)
	c1.number = 10
	// 创建对象c2
  // 引用类型,修改一个值,另一个也发生改变。因为它们的指针指向的是同一个内存地址
	c2 := new(Car)
	c2 = c1
	c2.number = 20

	fmt.Println("c1.number=", c1.number, &c1.number)
	fmt.Println("c2.number=", c2.number, &c2.number)
}

//输出结果:
c1.number= 20 0xc000014098
c2.number= 20 0xc000014098


// a=3,b=4.  交换a和b的值
// 方法一:

package main

import "fmt"

var a int = 3
var b int = 4

func main() {
	fmt.Println(a, b)
	var c = a
	a = b
	b = c
	fmt.Println(a, b)
}

// a=3,b=4.  交换a和b的值
// 方法二

package main

import "fmt"

var a int = 3
var b int = 4

func main() {
	fmt.Println(a, b)
	a, b = b, a

	fmt.Println(a, b)
}




变量的作用域

  • 在函数内部声明的变量叫做局部变量,生命周期仅限与函数内部
  • 在函数外部生命的变量叫做全局变量,生命周期作用于整个包,如果是大写的,则作用于整个程序
  • 语句块中的变量,生命周期仅限于语句块,语句块外部不能被识别
package main

import "fmt"

//定义全局变量
var a string = "abc"

func main() {
	n()
	m()
	if 1 == 1 {
    // 定义语句块内的变量
		var b string = "hello"
		fmt.Println("语句块输出:", b)
	}
	//fmt.Println(b),则报错。说明语句块中定义的变量,出了语句块就不能被识别
	fmt.Println("主函数输出:", a)
}

func n() {
	fmt.Println("n函数输出", a)
}

func m() {
  // 定义函数体内的局部变量
	a := "nihao"
	fmt.Println("m函数输出:", a)
}


//输出结果:
n函数输出 abc
m函数输出: nihao
语句块输出: hello
主函数输出: abc

变量的作用优先级

package main

import "fmt"

//定义了a 为全局变量,作用在整个文件中,最先被加载
var a int = 10

func main() {
	//在函数体内定义,则 a 为 局部变量,只能作用于数体内部,只用使用到次 变量才会被加载.
	var a int = 20
	//变量调用,就近原则。
	fmt.Println("a的值是:", a)
}


//输出结果:
a的值是: 20

image-20220316233852432



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

我们知道可以在变量的初始化时省略变量的类型而由系统自动推断,声明语句写上 var 关键字其实是显得有些多余了,因此我们可以将它们简写为 a := 50 或 b := false。

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

这是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。

package main

/*
:=
定义即声明,无需写var
系统自动定义数据类型,无需定义数据类型
格式只能作用在函数体内
*/

import "fmt"

func main() {
	a := "hello"
	fmt.Println(a)
	//查看数值类型
	fmt.Printf("数据类型是%T", a)
}

//输出结果
hello
数据类型是string

注意事项

如果在相同的代码块中,我们不可以再次对于相同名称的变量使用初始化声明,例如: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() {
	//定义多个变量,接收test()函数的返回值
	_, str1, str2 := test()
	fmt.Println("str1=", str1, "\nstr2=", str2)
}

//test的小括号里定义 传入的参数,小括号后面定义返回的参数
func test() (int, int, string) { 
	//定义多个变量
	a, b, c := 10, 20, "hello"
	//返回的许哟啊和 定义的返回参数的类型,数量 一致
	return a, b, c
}


//返回值是:
str1= 20 
str2= hello


Go 语言常量

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

常量使用const 修饰,代表永远只读,不能修改

const 只能修改boolean , number(int相关类型,浮点类型,complex) 和string

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

常量的定义格式:const identifier [type] = value

const b string = "hello world"
const b = "hello world"
const Pi = 3.1415926
const a = 9 / 3
const c = getValue()  //值被保护,不会被修改

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

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

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

const c_name1, c_name2 = value1, value2
const (
	a  = 9 / 3
	b  = "hello world"
	Pi = 3.1415926
)
package main

import "fmt"

//计算r=5 的圆面积和周长
func main() {
	const Pi float32 = 3.14

	var r = 5

	//周长
	c := 2 * Pi * float32(r)

	//面积
	s := Pi * float32(r) * float32(r)
	fmt.Println("周长是:", c, "\n面积是:", s)
}



iota

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

iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。

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

package main

import "fmt"

func main() {
	const (
		a = iota //0
		b        //1
		c        //2
	)
	fmt.Println(a, b, c)
}

// 输出结果
0 1 2
package main

import "fmt"

func main() {
	const (
		// 做位运算
		a = 1 << iota //1
		b             //2
		c             //4
		d             //8
	)
	fmt.Println(a, b, c, d)
}

// 输出结果
1 2 4 8
package main

import "fmt"

func main() {
	const (
		i = 1 << iota   // 1 << 0
		j = 2 << iota   // 2 << 0
		k               // 2 << 1
		m               
		n
	)
	fmt.Println(i, j, k, m, n)

}

//输出结果:
1 4 8 16 32



附录:

获取系统的环境变量

我们可以用os.Getenv 来获取系统的环境变量

package main

import (
	"fmt"
	"os"
)

func main() {
	//获取系统参数.  go 的工作目录
	var home = os.Getenv("GOROOT")
	fmt.Println("go的工作目录是:", home)
	// 获取go的项目目录
	var path = os.Getenv("GOPATH")
	fmt.Println("go的项目目录是:", path)
}

//输出结果
go的工作目录是: C:\Program Files\Go
go的项目目录是: F:\goproject
posted @ 2022-03-17 10:11  知己一语  阅读(171)  评论(0编辑  收藏  举报