golang学习笔记--变量和数据类型

本文介绍了Go语言中的变量声明,包括自动类型推断和全局、局部变量的定义。接着详细讲解了数据类型,包括整数、浮点数、指针、自定义数据类型、值类型与引用类型、数组、二维数组、切片以及map的使用,如创建、赋值、遍历和操作。此外,还讨论了常量的定义规则。
摘要由CSDN通过智能技术生成

目录

【1】变量

【2】数据类型

【2.1】数据类型 -- 指针

【2.2】数据类型 -- 自定义数据类型

【2.3】数据类型 -- 值类型和引用类型

【2.4】数据类型 -- 数组

 【2.4.1】数据类型 -- 二维数组

【2.5】数据类型 -- 切片

【2.6】数据类型 -- map

 【2.6.1】数据类型 -- map的curd

 【2.6.2】数据类型 -- map切片

【3】常量


【1】变量

go语言是强类型语言,但是声明变量时也不是必须明确数据类型,如果不声明数据类型,go会自动判断这个变量的数据类型是什么。

package main

import "fmt"

//设置全局变量
var s1 = 10
var s2 = 11

//一次性定义全局变量
var (
    s3 = "你好"
    s4 = 10.99
)


func main(){
    //在函数里面声明的变量为局部变量

	//声明一个变量并设置数据类型
	var age int

	//给变量赋值
	age = 18
	
	//声明一个变量不设置数据类型(自动类型推断)
	var num  = "99.1"
	
	//省略var 使用 := 代替 =
	str := "hello"
	
	//声明多个变量
	num1,str2,num3 := 12,"nihao",1.15
}

交换两个变量

golang可以直接复制交换,无需设置第三个中间变量

【2】数据类型

整数类型

 int 为正负数

unit 为正数

其他类型

Golang程序中整型变量在使用时,遵守保小不保大的原则

即:在保证程序正确运行下,尽量使用占用空间小的数据类型

(如需要设置一个学生的年龄变量,年龄不会存在负数并且大概率不会超过255,所以选择 uint8 或直接用byte类型较为合适)

浮点类型

float32可能会造成精度丢失,一般推荐用float64

【2.1】数据类型 -- 指针

package main

import "fmt"

func main() {
	var num int = 10
	fmt.Printf("num = %v \n",num)

    //1、ptr 是一个指针变量
	//2、ptr 的类型是 *int
	//3、ptr 的值是 &num ,也就是变量num的地址
	var ptr *int = &num
	fmt.Printf("ptr = %v \n",*ptr)

	*ptr = 55
	fmt.Printf("ptr = %v \n num = %v \n",*ptr,num)

	num = 9
	fmt.Printf("ptr = %v \n num = %v \n",*ptr,num)
}

执行结果为

变量num的值为10 ,它在内存中的地址可以在前面加&符号获取,写作&num。

变量ptr在定义时使用了这样的写法 var ptr *int = &num,就是将num的地址当做值保存了起来。

在使用ptr时,前面加上 * 符号就可以通过保存的地址找到对应的变量num的值。

这一系列的操作类似于php中的传引用。

 

【2.2】数据类型 -- 自定义数据类型

如下代码可以自定义数据类型,就是复制int32类型取名为myInt类型,类似取别名。

type myInt int32

但是要注意,虽然它复制了int32 ,但并不能和原本的类型互相赋值

【2.3】数据类型 -- 值类型和引用类型

值类型:基本数据类型int系列, float系列, bool, string 数组结构体struct

值类型通常储存在栈区

引用类型:指针、slice切片、map、管道chan、interface等都是引用类型

引用类型通常储存在堆区

其实,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的数据大小,数据越大,效率越低。

【2.4】数据类型 -- 数组

数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的,不能动态变化。

从下图可以看出数组在内存中的地址为该数组第一个元素的地址(地址为16进制),并且后续元素的地址为上一个地址+8(+8是因为在我的机器是64位时 int 默认为 int64 类型占8字节,如果是 int32 类型则占4字节就是+4)

声明一个数组,int类型默认值是0 ,string类型默认值是 "" ,  bool类型默认值是false

package main

import "fmt"

func main() {
	var arr1 [3]int
	fmt.Printf("arr1的类型是%T\n", arr1) //arr1的类型是[3]int
	fmt.Println(arr1) //[0 0 0]
}

给数组赋值

	var arr1 [3]int
	arr1[0] = 10
	arr1[1] = 15
	fmt.Printf("arr1的类型是%T\n", arr1) //arr1的类型是[3]int
	fmt.Println(arr1) //[10 15 0]

四种初始化数组的方式

	var arr1 [3]float64 = [3]float64 {1.1, 2.3, 10.0}
	fmt.Printf("arr1 的类型是%T  值是%v\n", arr1, arr1)
	//arr1 的类型是[3]float64  值是[1.1 2.3 10]

	var arr2 = [3]string {"tom", "jack", "lucy"}
	fmt.Printf("arr2 的类型是%T  值是%v\n", arr2, arr2)
	//arr2 的类型是[3]string  值是[tom jack lucy]

	var arr3 = [...]int {1, 3, 5}//... 系统自动判断数组长度
	fmt.Printf("arr3 的类型是%T  值是%v\n", arr3, arr3)
	//arr3 的类型是[3]int  值是[1 3 5]

	arr4 := [...]int {1:666, 0:555, 2:777}//指定元素值对应的下标
	fmt.Printf("arr4 的类型是%T  值是%v\n", arr4, arr4)
	//arr4 的类型是[3]int  值是[555 666 777]

数组遍历,使用for - range 。这是go语言一种特有的结构,可以用来遍历数组的元素。具体写法如下。

1、第一个返回值 index 为数组的下标

2、第二个返回值 value 为该下标位置的值

3、如果不想使用下标的话可以用 _ 代替index

4、index 和 value 都是仅在这个for循环内部可以使用的局部变量

5、index 和 value不是固定写法,可以自行命名

package main

import "fmt"

func main() {
	arr := [...]int64 {1, 2, 3, 8, 10}
	for index, value := range arr {
		fmt.Printf("下标为%v的元素值为%v\n", index, value)
	}
}

输出结果为:

 【2.4.1】数据类型 -- 二维数组

使用以下代码定义了一个二维数组,该数组有两个子数组,每个子数组有三个元素。

然后将第二个子数组的第二个元素赋值了10。

package main

import (
	"fmt"
)

func main() {
	var arr2 [2][3]int		// 声明一个二维数组arr2,包含2行3列
	arr2[1][1] = 10			// 给arr2的第2行第2列赋值为10(数组下标从0开始)
	fmt.Println(arr2)		// 打印arr2数组
}

输出结果为

 内存分析:

 arr2 有两个地址  可以看出是他的两个子数组的第一个元素的地址

【2.5】数据类型 -- 切片

1、定义切片并引用一个数组。

 [1:3] 的意思是 从下标 1 开始到 3 结束,但是引用的元素包括下标 1 不包括下标 3

如果要取到最后一个值则  :后面不写下标 ,示例([1:]  、  [:3]  、  [:])

package main

import "fmt"

func main() {
	arr := [...]int{1, 2, 3, 4, 5}
	
	slice := arr[1:3]

    //slice的类型是[]int,值是[2 3]
	fmt.Printf("slice的类型是%T,值是%v",slice,slice)
}

切片是引用类型,从下面可以看出,切片和数组的同一个元素的地址是相同的

package main

import "fmt"

func main() {
	arr := [...]int{1, 2, 3, 4, 5}

	slice := arr[1:3]

	fmt.Printf("arr[1]的地址是%p\n", &arr[1])
	fmt.Printf("slice[0]的地址是%p\n", &slice[0])
	slicelen := len(slice)
	sliceCap := cap(slice)
	fmt.Printf("slice的长度是%v,slice的容量是%v\n", slicelen, sliceCap)
}

输出结果: 

 修改其中一个的值,另一个也会随之变化

 内存示意图:slice实际上只存了arr[1]的地址还有本身的长度和容量。

2、用内置函数make来定义一个切片

基本语法: var  名称  []type = make([]type, len, [cap])

参数说明:type:数据类型  len:切片长度  cap:容量(可选参数)

package main

import "fmt"

func main() {
	var slice []float64 = make([]float64, 5, 10)
	slice[1] = 99
	slice[3] = 100
	fmt.Println(slice)
	fmt.Println("slice的长度是", len(slice))
	fmt.Println("slice的容量是", cap(slice))
}

输出结果 

内存示意图:本质上还是引用了一个数组,但是这个数组是不可见的,只有通过该切片可以使用

3、定义一个切片直接指向具体数组,使用原理类似make

package main

import "fmt"

func main() {
	var slice []string = []string{"tom","jack","lucy"}
	fmt.Println(slice)
	fmt.Println("slice的长度是", len(slice))
	fmt.Println("slice的容量是", cap(slice))
}

 输出结果

【2.6】数据类型 -- map

数据类型格式:map(键类型)值类型

package main

import "fmt"

func main() {
	//第一种定义方法 使用make函数
	// var valMap map[string]any
	valMap := make(map[any]any, 5)
	valMap["XX1"] = "x5x5"
	valMap[2] = "hello"
	valMap[15] = "world"
	valMap[8] = "nihao"
	valMap[0] = 1

	for i, v := range valMap {
		fmt.Printf("%v=>%v\n", i, v)
	}
	fmt.Println("--------------------------------")

	//第二种定义方法,定义map时直接赋值
	heroes := map[string]string{
		"no1": "武松",
		"no2": "李逵",
	}
	fmt.Println(heroes)
	fmt.Println("--------------------------------")

	//map嵌套
	students := make(map[string]map[string]string)
	students["stu01"] = make(map[string]string)
	students["stu01"]["name"] = "小明"
	students["stu01"]["sex"] = "男"

	students["stu02"] = make(map[string]string)
	students["stu02"]["name"] = "小花"
	students["stu02"]["sex"] = "女"

	fmt.Println(students)
	fmt.Println("--------------------------------")

}

输出结果

根据上面的循环打印输出结果可以看出map内部是无序的

 【2.6.1】数据类型 -- map的curd

1、查询

package main

import "fmt"

func main() {
	heroes := map[string]string{
		"no1": "武松",
		"no2": "李逵",
		"no3": "宋江",
	}
	//查询某个key是否存在
	no1, ok := heroes["no1"]
	if ok {
		fmt.Println("ok = ", ok)
		fmt.Println("no1 = ", no1)
	}
	no4, ok := heroes["no4"]
	if ok {
		fmt.Println(no4)
	} else {
		fmt.Println("没有no4,ok = ", ok)
	}

	fmt.Println("-----------------------------")

	fmt.Println(heroes)
}

输出结果

 2、删除

	//删除一个元素  delete(要删除的map原值,要删除的key)
	delete(heroes, "no1")

	//删除全部元素 for循环删除 或者直接重新make
	heroes = make(map[string]string)

 

3、创建和更新

package main

import "fmt"

func main() {
	heroes := map[string]string{
		"no1": "武松",
		"no2": "李逵",
		"no3": "宋江",
	}
	//查询no1是否存在,如果存在就更新为林冲
	_, ok := heroes["no1"]
	if ok {
		heroes["no1"] = "林冲"
	}

	//查询no4是否存在,如果不存在就添加
	_, ok2 := heroes["no4"]
	if !ok2 {
		heroes["no4"] = "扈三娘"
	}

	fmt.Println("-----------------------------")
	fmt.Println(heroes)
}

 【2.6.2】数据类型 -- map切片

package main

import "fmt"

func main() {
	//创建一个map切片 必须使用make函数
	mapSlice := make([]map[string]any, 2)
	//给切片添加一个map
	mapSlice[0] = make(map[string]any)
	mapSlice[0]["name"] = "tom"
	mapSlice[0]["age"] = 13

	//使用append给切片添加两个map
	mapSlice2 := map[string]any{
		"name": "jack",
		"age":  16,
	}
	mapSlice = append(mapSlice, mapSlice2, map[string]any{
		"name": "lucy",
		"age":  14,
	})
	fmt.Println(mapSlice)
	fmt.Println("--------------------------------")
	fmt.Println(mapSlice[3]["name"])

}

 

【3】常量

使用const可以定义常量:

1、常量必须给初始值

2、常量必须是编译时就可以确定的值,并且不能被修改

3、可以批量定义常量

4、常量不必全部大写

5、常量仍是通过首字母大小写来控制是否可以被其他包访问

6、iota可以给常量赋值为当前const内的第n行。后面常量的如果没给值会+1

package main

import "fmt"

const TestInt = 10
const TestString string = "hello"

func main() {
	const (
		a = 9
		b = iota
		c
		d
		e = 50
	)
	fmt.Println(TestInt, TestString)
	fmt.Println(a, b, c, d, e)
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值