Golang_01_介绍及基础


目录:

GO语言的特点

  1. 从C语言中继承了较多理念,包括表达式语法,控制结构,基础数据结构,调用参数传值,指针等等;
  2. 引入包的概念,用于组织程序结构,Go语言的一个文件都要归属于一个包,而不能单独存在;
  3. 垃圾回收机制,内存自动回收;
  4. 天然并发。语言层面支持并发;goroutine,轻量级线程,可实现大并发处理,高效利用多核;基于CPS并发模型实现;
  5. 吸收了管道通信机制,形成Go语言特有的管道channel。通过管道channel可以实现不同的goroute之间相互通信;
  6. 函数支持返回多个值;
  7. 新的创新,例如切片slice,延时执行defer等;

开发环境(software Development Kit,SDK)搭建(windows)

https://go.dev/dl/安装包下载

环境变量

  1. GOROOT:指定SDK的安装路径,推荐d:/programs
  2. Path:添加SDK的/bin目录
  3. GOPATH:工作目录,go项目存放的路径

程序开发和基本结构说明

目录结构

package main  //表示该文件所在的包是main包,每个文件都必须归属于一个包 
import "fmt"  //引入一个包,包名fmt。引入该包后,就可以使用该包的函数,例如fmt.Println 

func main(){  // func为关键字,表示后续为一个函数。main是函数名,为主函数    
    fmt.Println("hello,world!")  // 调用fmt包的函数Println输出文本 
}

运行

go build -o myhello.exe hello.go  # 编译生成myhello.exe的可执行文件,命令行输入hello.exe即可运行 
go run hello.go  # 以脚本文件的形式直接运行源码

开发注意事项

  1. 执行入口为main()方法;
  2. 严格区分大小写;
  3. Go方法由一条条语句构成,默认在每个语句自动添加分号;
  4. 按行进行编译。不能将多条语句写在同一行;
  5. go定义的变量或者import的包如果没有使用到,代码不能编译通过;

转义字符

  1. \t 制表符
  2. \n 换行符
  3. \ 单个\
  4. \" 单个"
  5. \r 回车

编程指南

https://tour.go-zh.org/basics/1 # 官方编程网站

标识符命名规范

Golang对各种变量、方法、函数等命名时使用的字符序列(字符串)称为标识符。凡是可以自己起名字的地方都叫标识符

规则

  1. 由英文字母大小写,0-9和_组成;
  2. 不能以数字开头;
  3. 严格区分大小写,例如var num intvar Num int中,numNum是两个不同的变量;
  4. 标识符不能包含空格;
  5. 下划线_是一个特殊的标识符,称为空标识符。可以代表任何其他的标识符,但是它对应的值会被忽略(例如忽略某个返回值)。所以仅能被作为占位符使用,不能作为标识符使用
  6. 不能以系统保留关键字(共25个 )作为标识符,例如break,if等;

注意事项

  1. 包名:保持package的名字和目录一致,简短有意义,不要与标准库冲突;
  2. 变量名、函数名、常量名:采取驼峰法;
  3. 如果变量名、函数名、常量名首字母大写,则可以被其他包访问;如果首字母小写,则只能在本包中使用;

常量 & 变量

数学概念:在事务的特定运动过程中,某量若保持不变,则称之为常量;反之,则称之为变量。变量分为自变量和因变量,亦称函数。

常量是指在程序编译后运行时始终都不会变化的值,比如帧率、延迟。常量和变量声明一般出现在包级别。

常量声明后程序可以不使用,变量声明后程序至少要调用1次。

常量声明 & 常量计数器iota

常量声明的值必须是一个数字值、字符串或者一个固定的boolean值。iota是golang的常量计数器,只能在常量的表达式中使用。使用iota只需记住以下两点:

  1. iota在const关键字出现时将被重置为0
  2. const中每新增一行常数声明将使iota计数一次,在定义枚举时很有用
const [常量名] [常量类型] = [value]  // 常量的显式声明格式 
const (
    USD int = iota      // 美元,值 = 0    
    EUR                 // 欧元,值 = 1    
    RMB                 // 人民币,值 = 2 
) 
func main() {
    const a int = 3.1415  // 显式定义    
    const b = "Hello World"  // 隐式定义    
    fmt.Println(a,b)  // 输出'3.1415 Hello World'    
    symbol := [...]string{USD: "$", EUR: "€", GBP: "£", RMB: "¥"}    
    fmt.Println(RMB, symbol[RMB]) // 输出'3 ¥' 
}

变量声明

变量(变量=变量名+数值+数据类型)表示内存中的一个存储区域。该区域有自己的名称(变量名)和类型(数据类型)。

变量使用的两种方式:

  1. 指定变量类型,声明后若不赋值,使用默认值;
  2. 根据值自行判定变量类型(类型推导,有点类似python);
func main(){
    var q, w int  // 定义int类型的变量,类似于JS    
    q = 10  // 为i赋值,若无赋值默认值为0    
    fmt.Println("q =", q, "w =", w)  // 控制台输出'i = 10' 
}

细节

同一个作用域中不能重复定义;

在定义变量时若没有赋初值,int类型默认为0,string类型默认为空字符串,小数类型默认为0;

数据类型

整型

Go数据类型包含基本数据类型和派生/复杂数据类型,主要做一下复杂数据类型的笔记,基本数据类型各语言类似。

整数类型使用细节:

  1. 各整数类型分为有符号无符号;int uint的大小和系统有关;
  2. 默认声明为int
  3. 如何在程序查看某个变量的字节大小和数据类型;
import unsafe 
fmt.Printf("n2 的类型 %T n2占用的字节数是 %d", n2, unsafe.Sizeof(n2))  // unsafe.Sizeof方法使用较多
  1. 遵守保小不保大的原则(在保证程序正确运行下,尽量使用占用空间小的数据类型,如年龄);
  2. 计算机中的最小存储单位是bit;基本存储单元是byte;1byte=8bit;
func main(){
    var n1 = 100    
    fmt.Printf("n1 的类型 %T",n1)  // %T可查看数据类型 
}

浮点类型

类型 占用存储空间 表数范围
单精度float32 4字节 -3.403E38~3.403E38
双精度float64 8字节 -1.798E308~1.798E308

浮点数在机器中存放形式,浮点数=符号位+指数位+尾数位

  1. 浮点数都是有符号(正负符号)的;
  2. 尾数部分可能丢失,造成精度损失,若对精度有要求,推荐使用float64;
  3. 默认声明为float64;

字符(char)类型

Golang中没有专门的字符类型,如果要存储单个字符(字母),一般使用byte来保存。

字符型存储到计算机中,需要将字符对应的码值找出来。例如存储时,将字符 --> 对应码值 --> 二进制 --> 存储的流程。读取时反向对应。

  1. 字符常量使用单引号('')括起来的单个字符。例如var c1 byte = 'a' var c2 int = '中' var c3 byte = '9'
  2. 允许使用转义字符'\'来将气候的字符转变为特殊字符型常量。例如var c3 char= '\n' // '\n'标识换行符
  3. 默认使用UTF-8编码(包含ASCII码)
  4. 在Go中,字符的本质是一个整数,直接输出时,是该字符对应的UTF-8编码的码值;
  5. 可以直接给变量赋一个数值,然后按格式化输出 %c,会输出该数字对应的unicode字符
  6. 字符类型是可以进行运算的,相当于一个整数,因为它都有对应的ASCII码;
var c1 byte = 'a' 
fmt.Println("c1=",c1)  // 输出c1= 97 
fmt.Printf("c1=%c c1对应码值=%d \n",c1,c1)  // 输出c1=a c1对应码值=97 
var c3 = 10 + 'a' 
fmt.Println("c3=",c3,"\n")  // 输出c3= 107

布尔类型

  1. 只能取true或者false,不能有其他值(包括0和1);
  2. 占1个字节

字符串string类型

字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节(byte)连接起来的。Go的字符串的字节使用UTF-8编码标识Unicode文本。

  1. 字符串一旦赋值,字符串就不能再更改了,在Go中字符串是不可变的
  2. 使用双引号会识别转义字符;使用反引号会以字符串的原生格式输出,包括换行和特殊字符,可以防止攻击;
  3. 拼接时,需要将+放在上一行,因为Go会自动在每行后面添加;

基本数据类型默认值

数值类型 默认值
int整型 0
float浮点型 0
bool布尔型 false
string字符串 ""

基本数据类型的转换

Go在不同类型的变量之间赋值时需要显式转换。也就是说在Golang中数据类型不能自动转换,必须强制转换。

被转换的是变量存储的数据,原变量本身的数据类型并没有发生变化。

var i int32 = 100 
var n1 float64 = float(i)  // 显式转换 
fmt.Printf("i=%v n1=%v", i, n1)  // %v表示原值输出 
// 在转换中,如果将int64的值转换成int8,编译时不会报错,只是将转换结果按溢出处理 
var num1 int64 = 999999  // 对应二进制1111 0100 0010 0011 1111 
var num2 int8 = int8(num1)  // 只取8位数值,结果转换为10进制时输出63 
fmt.Printf("num1=%v num2=%v \n",num1,num2)  // 输出num1=999999 num2=63

⭐基本数据类型和string转换

//  将其他数据类型转换为string,格式:fmt.Sprintf("%参数", 表达式) ,方式一 
var mycahr byte = 'h' 
var str string = fmt.Sprintf("%c",mycahr)  // %c的含义查看golang fmt方法的定义 
fmt.Printf("str type %T str=%q",str,str) 
// 方式二 
import "strconv" 
var num1 int = 99 
var num2 float64 = 23.456 
var b bool = true 
var string1 string = strconv.FormatInt(int64(num1),10) 
fmt.Printf("string1 type %T string1=%q\n",string1,string1)  // string1 type string string1="99" 
var string2 string = strconv.FormatFloat(num2,'f',10,64) 
fmt.Printf("string2 type %T string2=%q\n",string2,string2)  // string2 type string string2="23.4560000000" 
var string3 string = strconv.FormatBool(b) 
fmt.Printf("string3 type %T string3=%q\n",string3,string3)  // string3 type string string3="true" // 将string转换为其他数据类型 
var str1 string = "true" 
var str2 string = "123456" 
var str3 string = "123.4567789" 
var b1 bool b1 , _ = strconv.ParseBool(str1) fmt.Printf("b1 tyue %T b1=%v\n",b1,b1)  // b1 tyue bool b1=true 
var num3 int64 num3, _ = strconv.ParseInt(str2,10,64) 
fmt.Printf("num3 tyue %T num3=%v\n",num3,num3)  // num3 tyue int64 num3=123456 
var float float64 float, _ = strconv.ParseFloat(str3, 64) 
fmt.Printf("float type %T float=%v\n",float,float)  // float type float64 float=123.4567789 
var float32 float32 = float32(float)  // 默认转换为float64,若有转换成float32的需求强制转换一下即可 
fmt.Printf("float32 type %T float32=%v\n",float32,float32)  // float32 type float32 float32=123.45678

派生/复杂数据类型

指针(Pointer)

  • 基本数据类型,变量存的就是值,也叫值类型;
  • 值类型,都有对应的指针类型,形式为*数据类型。例如int对应的指针就是*int

  • 获取变量的地址,用&。例如var num int中,获取num的地址:&num

  • 指针类型,变量存的是一个地址,这个地址指向的空间存的才是值。例如var ptr *int = &num
  • 获取指针类型所指向的值,使用*。例如var ptr *int,使用*ptr获取p指向的值
var i int = 10  
fmt.Println("i的地址为", &i)  // &i获取变量的地址 
fmt.Println("i的值为", i) 
var ptr *int = &i  // ptr为int值类型指针变量指向&i的地址 
fmt.Printf("ptr指向的地址=%v\n",ptr) 
fmt.Printf("ptr自己的地址=%v\n",&ptr)  // &ptr指地址  
fmt.Printf("ptr指向的值=%v\n",*ptr)  // *ptr解地址

⭐值类型和引用类型

说明

  1. 值类型包括int系列,float系列,bool,string,数组结构体struct
  2. 引用类型包括指针,slice切片,map,管道chan,interface

使用特点

  1. 值类型变量直接存储值,内存通常在中分配
  2. 引用类型变量存储的是一个地址,这个地址对应的空间才存储真正的值,内存通常在上分配。当没有任何变量引用这个地址时,该地址对应的数据空间就成为了垃圾,由GC来回收
  3. 不管是值传递和引用传递,传递给函数的都是变量的副本。

运算符

与各编程语言及MySQL的运算符大差不差,所以只记录不同的运算符。

算数运算符

与Python相同。不过另有自增和自减。当a=2 a++时,a的结果为3。++或--只能独立使用

func main(){    
    var i int = 8     
    var a int    
    i++  // i++只能独立使用,不能a = i++    
    a = i    
    fmt.Println(a) 
}

细节说明:

  1. 当整数之间做除法时,只保留整数部分而舍弃小数部分;
  2. 当对一个数取模时,可以等价a%b=a-a/b*b

关系(比较)运算符

逻辑运算符

运算符 描述 实例(假定A为True,B为False)
&& 逻辑 运算符。如果两边的操作数均为True,则为True,否则为False (A && B) 为False
|| 逻辑 运算符。如果两边的操作有一个为True,则有True,否则为False (A || B)为True
! 逻辑 运算符。如果条件为True,则逻辑为False,否则为True !(A && B)为True
func main(){    
    var age int = 40    
    if age > 30 && age <= 40{        
        fmt.Println("666")    
    } 
}

细节说明:

  1. &&也叫短路与。如果第一个条件为false,则第二个条件不会判断,最终结果为false
  2. ||也叫短路或。如果第一个条件为true,则第二个条件不会判断,最终结果为true

赋值运算符

赋值运算符就是将某个运算后的值,赋给指定的变量。与Python相同

// a += 1 与a = a + 1的结果相同。 
// 且支持 =,+=,-=,*=,/=,%=等多种赋值运算符

位运算符

计算机中的计算都是通过位运算符和移位运算符中的补码实现计算。

运算符 描述 运算规则
& 按位与运算符"&"是双目运算符。其功能是参与运算的两数各对应的二进位相与 同时为1,结果为1,否则为0
| 按位或运算符"|"是双目运算符。其功能是参与运算的两数各对应的二进位相或 有一个为1,结果为1,否则为0
^ 按位异或运算符"^"是双目运算符。其功能是参与运算的二进位相异或 当二进位不同时,结果为1,否则为0
<< 左移运算符"<<"是双目运算符。其功能把"<<"左边的运算数的各二进位全部左移若干位,高位抛弃,低位补0.左移n位就是乘以2的n次方 符号位不变,低位补0
>> 右移运算符">>"是双目运算符。其功能把">>"左边的运算数的各二进位全部右移若干位,右移n位就是乘以2的n次方 低位溢出,符号位不变,并用符号位补溢出的高位

原码、反码、补码

对于有符号的数:

  1. 二进制的最高位是符号位,0表示正数,1表示负数;1转换为二进制为0000 00001,-1转换为二进制是1000 0001
  2. 正数的原码,反码,补码都一样;
  3. 负数的反码=它的原码符号位不变,其他位取反
  4. 1的原码[0000 0001],反码[0000 0001],补码[0000 0001]
  5. -1的原码[1000 0001],反码[1111 1110],补码[1111 1111]

  6. 负数的补码=它的反码+1

  7. 0的反码,补码都是0
  8. 在计算机运算的时候,都是以补码的方式来运算的

位运算符和移位运算符

2的原码[0000 0010] 2的反码[0000 0010] 2的补码[0000 0010] 
3的原码[0000 0011] 3的反码[0000 0011] 3的补码[0000 0011] 
2&3的结果=2  // &代表两个二进制相同时时取1 
2|3的结果=3  // |代表只要有1个就取1 
2^3的结果=1  // ^代表两个二进制不同时取1 

-5的原码[1000 0101] -5的反码[1111 1010] 
-5的补码[1111 1011] 8的原码[0000 1000]  
8的反码[0000 1000]  8的补码[0000 1000] 
-5&8的补码=0000 1000反码=0000 1000,原码=0000 1000 十进制结果=8 
-5|8 的补码=1111 1011反码=1111 1010,原码=1000 0101  十进制结果=-5 
-5^8的补码=1111  0011 反码=1111 0010,原码=1000 1101 十进制结果=-13 

-5的原码[1000 0101] -5的反码[1111 1010] -5的补码[1111 1011] 
4的原码[0000 0100]  4的反码[0000 0100]  4的补码[0000 0100] 
-5&4的补码=0000 0000反码=0000 0000,原码=0000 0000 十进制结果=0 
-5|4 的补码=1111 1111反码=1111 1110,原码= 1000 0001 十进制结果=-1  
-5^4的补码=1111 1111反码=1111 1110,原码= 1000 0001 十进制结果=-1

其他运算符

运算符 描述 实例
& 返回变量存储地址 &a;将给出变量的实际地址
* 指针变量 *a;是一个指针变量

运算符优先级*

只有单目运算符、赋值运算符是从后向左运算的。优先级一览表如下:

大致的顺序如下:

  1. 括号,++,--
  2. 单目运算
  3. 算术运算符
  4. 移位运算
  5. 关系运算符
  6. 位运算符
  7. 逻辑运算符
  8. 赋值运算符
  9. 逗号