1-golang基础(基本数据类型、指针、运算符)

1. sdk下载:

2. windows下安装sdk

  1. 下载windows sdk,并安装
  2. 配置环境变量,go1.17.6.windows-amd64.msi安装后会默认配置GOROOT,只需要重新配置GOPATH,用于指向工作目录(项目存放目录)
  3. 验证是否安装成功
go version

3. golang执行流分析

两种执行流:

  • XX.go文件通过执行go build XX.go生成XX.exe文件,执行XX.exe得到结果
  • go run XX.go直接执行得到结果

两种执行流的区别

  • 通过go build得到的.exe文件,在没有go环境的机器上也可以运行
  • go run XX.go需要有go开发环境才能执行
  • go build得到的.exe文件相比原来的.go文件,内存变大了很多,是因为编译器会将程序运行依赖的库文件包含在可执行文件中

通过go build可以生成指定文件名的可执行文件

go build -o myhello.exe hello.go

4. golang 开发注意事项

  1. golang源文件以.go为扩展名
  2. go应用程序执行入口是main()函数
  3. 严格区分大小写
  4. 每个语句后面不需要加分号
  5. 不能把多条一句写在一行,否则会报错
  6. 定义的变量或者import的包,如果没有使用,需要删掉,否则会报错
  7. 方法体的左大括号必须和方法名同一行,否则会报错

5. golang变量使用

package main

import "fmt"

// 定义全局变量
var b = 3
var c = "cc"
var (
	d = 4
	e = "ee"
)

func main() {

	// 第一种:完整定义并赋值
	var a int = 1
	fmt.Println("a=", a)
	// 第二种:指定变量类型,若不赋值,则使用默认值
	var i int
	fmt.Println("i=", i)
	// 第三种:根据值自行判断变量类型(类型推导)
	var num = 10.11
	fmt.Println("num=", num)
	// 第四种:省略var,注意 := 左侧的变量不能是之前申明过的,否则会报错
	// 下面方式等价于 var name string = "tom"
	name := "tom"
	fmt.Println("name=", name)

	// 一次申明多个变量
	var n1, n2, n3 int
	fmt.Println("n1=", n1, "n2=", n2, "n3=", n3)

	var n4, n5, n6 = 4, "tom", 6
	fmt.Println("n4=", n4, "n5=", n5, "n6=", n6)

	n7, n8, n9 := 7, "tom8", 9
	fmt.Println("n7=", n7, "n8=", n8, "n9=", n9)

	fmt.Println("b=", b, "c=", c, "d=", d, "e=", e)

	// 加号的使用
	// 两边是数值类型,则是做加法运算
	var m1 = 1
	var m2 = 2
	var m3 = m1 + m2
	fmt.Println("m3=", m3)
	// 两边有字符串,则是做拼接
	var m4 = "m4"
	var m5 = "m5"
	var m6 = m4 + m5
	fmt.Println("m6=", m6)

}

6. golang数据类型

1. 整数类型

类型

有无符号

占用存储空间

数据范围

备注

int8


1字节

-128 ~ 127

int16


2字节

-2^15 ~ 2^15-1

int32


4字节

-2^31 ~ 2^31-1

int64


8字节

-2^63 ~ 2^63-1

uint8


1字节

0 ~ 255

uint16


2字节

0 ~ 2^16-1

uint32


4字节

0 ~ 2^32-1

uint64


8字节

0 ~ 2^64-1

int


32位系统4字节,64位系统8字节

2^31 ~ 231-1,-263 ~ 2^63-1

uint


32位系统4字节,64位系统8字节

0 ~ 2^32-1,0 ~ 2^64-1

rune


与int32一样

2^31 ~ 2^31-1

与int32一样,表示一个unicode码,存储中文字符用rune

byte


与uint8一样

0 ~ 255

与uint8一样,要存储字符时选择byte

golang中整型默认声明为int型

// 查看数据类型
var a1 = 100
fmt.Printf("a1的类型是 %T", a1)
// 查看数据类型和占用字节数
var a2 int8 = 100
fmt.Printf("a2的类型是 %T, 占用的字节数是 %d", a2, unsafe.Sizeof(a2))

2. 浮点类型

类型

占用存储空间

数据范围

单精度float32

4字节

-3.403E38 ~ 3.403E38

双精度float64

8字节

-1.798E308 ~ 1.798E308

golang中浮点型默认声明为float64,开发中推荐使用float64,精度更高

3. 字符类型

var b1 byte = 'a'
// 如果直接输出byte,得到的是字符对应的ASCII码
fmt.Println("b1=", b1)
// 如果希望输出字符,需要使用格式化输出
fmt.Printf("b1=%c", b1)

4. 布尔类型

bool只能是true或者false,占1个字节

5. 字符串类型

golang中采用的utf8编码,不用担心中文乱码问题;

字符串一般用双引号(“”),想输出代码块可以用反引号(``)

str1 := "abc\nabc"
fmt.Println(str1)

str2 := `
	var b1 byte = 'a'
	// 如果直接输出byte,得到的是字符对应的ASCII码
	fmt.Println("b1=", b1)
	// 如果希望输出字符,需要使用格式化输出
	fmt.Printf("b1=%c", b1)
	`
fmt.Println(str2)

当一个字符串很长,需要+拼接是,+必须放在上一行,否则会报错

str3 := "hello " + "world " +
	"hello " + "world " +
	"hello " + "world " +
	"hello " + "world " +
	"hello " + "world "
fmt.Println(str3)

6. 基本数据类型的默认值

数据类型

默认值

整数类型

0

浮点类型

0

布尔类型

false

字符串类型

“”

7. 基本数据类型的相互转换

golang在数据类型的转换需要显示转换,不能自动转换

var i int32 = 100
var m int64 = int64(i)
var n int8 = int8(i)
fmt.Printf("i=%d, m=%d, n=%d", i, m, n)

注意:被转换的是值,变量本身的数据类型并没有转换。上面的例子,转换完以后,i依然是int32
// 大范围转小范围,可能会溢出,但不会报错,结果和我们期望的不一样
var n1 int64 = 99999999
var n2 int8 = int8(n1)
fmt.Println(n2)

8. 基本数据类型转string

a := 100
b := 101.1
c := true
d := 'h'

// 第一种方式 fmt.Sprintf 转换
var str1 string = fmt.Sprintf("%d", a)
var str2 string = fmt.Sprintf("%f", b)
var str3 string = fmt.Sprintf("%t", c)
var str4 string = fmt.Sprintf("%c", d)
fmt.Printf("str1=%q str2=%q str3=%q str4=%q", str1, str2, str3, str4)

// 第二种方式 strconv函数转换
// 10 代表10进制
var str5 string = strconv.FormatInt(int64(a), 10)
// 'f' 代表格式; 10 代表保留10个小数位; 64 表示这个小数是float64
var str6 string = strconv.FormatFloat(b, 'f', 10, 64)
var str7 string = strconv.FormatBool(c)
fmt.Printf("str5=%q str6=%q str7=%q", str5, str6, str7)

// strconv.FormatInt 简写
var str8 string = strconv.Itoa(a)
fmt.Printf("str8=%q", str8)

9. string转基本数据类型

str1 := "true"
// strconv.ParseBool(str)有两个返回值 (value bool, err error)
// 我们只需要获取value bool的值,所以用 _ 忽略error
a, _ := strconv.ParseBool(str1)
fmt.Printf("a=%t", a)

str2 := "1000"
// strconv.ParseInt(str)的返回值是int64和error
b, _ := strconv.ParseInt(str2, 10, 0)
fmt.Printf("b=%v", b)

str3 := "123.456"
// strconv.ParseFloat(str)的返回值是float64和error
c, _ := strconv.ParseFloat(str3, 64)
fmt.Printf("c=%v", c)

// 注意:如果转换失败了,会存储一个默认值
str4 := "hello"
var d int64 = 12
d, _ = strconv.ParseInt(str4, 10, 64)
// 输出结果为默认值0
fmt.Printf("d=%v", d)

10. 指针

  • 基本数据类型变量存的就是值,也叫值类型
  • 值类型包括基本数据类型int系列、float系列、bool、string、数组和结构体(struct)
  • 获取值类型变量的地址,用&,例如:&i
  • 指针变量本身占用一个地址,里面存的是也是一个地址(例如:var ptr *int = &i),这个地址指向的是值类型变量的值
  • 获取指针变量所指向的值,用*,例如 *ptr
func main() {

	i := 10
	// 获取i的地址
	fmt.Println("i的地址=", &i)

	// ptr是一个指针变量,类型是 *int (int型的指针)
	// ptr存储的值是 &i,也就是i的地址
	var ptr *int = &i
	fmt.Printf("ptr=%v\n", ptr)
	// ptr本身也有一个地址
	fmt.Printf("ptr的地址=%v\n", &ptr)
	// 获取ptr指向的值
	fmt.Printf("ptr指向的值=%v\n", *ptr)
}

11. 值类型和引用类型

值类型:int系列、float系列、bool、string、数组、结构体(struct);

引用类型:指针、切片(slice)、map、管道(channel)、interface;

值类型变量直接存储值,内存在栈中分配;

引用类型变量存储的是地址,这个地址对应的空间存储的是值,内存在堆中分配,没有任何变量引用这个地址时,等待GC回收

7. 标识符命名规范

  1. 由26个英文字母大小写,0~9, _ 组成
  2. 数字不能开头
  3. 严格区分大小写 (例如:var num int 和 var Num int是不一样的)
  4. 不能包含空格
  5. _ 是空标识符,可以代表任何其他标识符,此时会忽略其他标识符对应的值,因此仅被作为占位符使用,自定义标识符中不可单独使用 _
  6. 保留关键字不能作为标识符(共25个),例如:break、case、chan、const、continue、default、defer、else、fallthrough、for、func、go、goto、if、import、interface、map、package、range、return、select、struct、switch、type、var

注意事项:

  1. 包名和目录尽量保持一致,不要和标准库冲突
  2. 变量名、函数名、常量名采用驼峰法
  3. 如果变量名、函数名、常量名首字母大写,则可以被其他包访问;如果首字母小写,则只能在本包中使用;可以简单理解为首字母大写的是共有的,小写的是私有的

8. 运算符

注意:golang中没有三元运算符

1. 算术运算符

+ - * / % ++ --

// 如果运算的都是整数,除法运算以后,结果也是整数,小数部分被截取掉
// 结果为2
fmt.Println(10 / 4)

// 结果还是2
var n1 float32 = 10 / 4
fmt.Println(n1)

// 如果希望保留小数部分,则需要有浮点数参与运算
var n2 float32 = 10.0 / 4
fmt.Println(n2)

// 取模计算公式 a % b = a - a / b * b
fmt.Println("10 % 3=", 10%3)
fmt.Println("-10 % 3=", -10%3)
fmt.Println("10 % -3=", 10%-3)
fmt.Println("-10 % -3=", -10%-3)

// ++ 和 --只能独立作为一行语句使用,不能放在表达式中使用
// 只有 后++ 和 后--; 没有 前++ 和 前--

2. 关系运算符

== != > < >= <=

3. 逻辑运算符

&& || !

4. 赋值运算符

= += -= *= /= %=
func main() {

	// 两个变量 a 和 b,在不引入中间变量的情况下,交换 a 和 b的值
	a := 10
	b := 20
	fmt.Printf("交换前 a=%d b=%d \n", a, b)

	a = a + b
	b = a - b // b = a + b - b => b = a
	a = a - b // a = a + b - a
	fmt.Printf("交换后 a=%d b=%d \n", a, b)

}

5. 位运算符

// 按位与& : 两位全为1,结果为1,否则为0
// 按位或| : 两位有一个为1,结果为1,否则为0
// 按位异或^ : 两位一个为0,一个为1,结果为1,否则为0
// 右移>> : 低位溢出,符号位不变,并用符号位补溢出的高位
// 左移<< :符号位不变,低位补0

1. 原码、反码、补码

对于有符号的而言:

  • 二进制最高位是符号位:0表示整数,1表示负数
  • 正数的原码、反码、补码都一样
  • 负数的反码是原码的符号位不变,其他位取反
  • 负数的补码是它的反码+1
    1 => 原码[0000 0001] 反码[0000 0001] 补码[0000 0001]
    -1 => 原码[1000 0001] 反码[1111 1110] 补码[1111 1111]
  • 0的反码补码都是0
  • 计算机运算的时候,都是以补码的方式来运算的
    运算符
// 按位与& : 两位全为1,结果为1,否则为0
// 按位或| : 两位有一个为1,结果为1,否则为0
// 按位异或^ : 两位一个为0,一个为1,结果为1,否则为0
// 右移>> : 低位溢出,符号位不变,并用符号位补溢出的高位
// 左移<< :符号位不变,低位补0

1. 原码、反码、补码

对于有符号的而言:

  • 二进制最高位是符号位:0表示整数,1表示负数
  • 正数的原码、反码、补码都一样
  • 负数的反码是原码的符号位不变,其他位取反
  • 负数的补码是它的反码+1
    1 => 原码[0000 0001] 反码[0000 0001] 补码[0000 0001]
    -1 => 原码[1000 0001] 反码[1111 1110] 补码[1111 1111]
  • 0的反码补码都是0
  • 计算机运算的时候,都是以补码的方式来运算的