GO之基本语法

一、Golang变量

  一)变量的声明:使用var关键字

  Go语言是静态类型语言

  Go语言的基本类型有:

  • bool
  • string
  • int、int8、int16、int32、int64
  • uint、uint8、uint16、uint32、uint64、uintptr
  • byte // uint8 的别名
  • rune // int32 的别名 代表一个 Unicode 码
  • float32、float64
  • complex64、complex128
  1、标准格式:声明一个变量

  一般形式是使用 var 关键字:指定变量的类型

var name type
# var 变量名 变量类型

  其中,var 是声明变量的关键字,name 是变量名,type 是变量的类型。

  声明变量时将变量的类型放在变量的名称之后。这样做的好处就是可以避免像C语言中那样含糊不清的声明形式。

  2、批量格式

  使用关键字var和括号,可以将一组变量定义放在一起

var (
    a int
    b string
    c []float32
    d func() bool
    e struct {
        x int
    }
)
  3、简短格式

  还可以使用更简短的定义定义和初始化语法

名字 := 表达式

  需要注意的是,简短模式(short variable declaration)有以下限制:

  • 定义变量,同时显式初始化。
  • 不能提供数据类型。
  • 只能用在函数内部。

  范例

func main() {
   a1:=10
   x,y:=666, "abc"
}

  二)变量的初始化

  声明变量时,自动对变量对应的内存区域进行初始化操作。每个变量会初始化其类型的默认值

  • 整型和浮点型变量的默认值为 0 和 0.0。
  • 字符串变量的默认值为空字符串。
  • 布尔型变量默认为 bool。
  • 切片、函数、指针变量的默认为 nil。
  1、标准格式
var 变量名 类型 = 表达式

  范例

var price int = 100
  2、指定变量的类型,但是不赋值,使用默认值
    var num int
    fmt.Println(num)

  3、编译器推导类型的格式

  编译器根据等号右边的表达式推导 price 变量的类型

var price = 100

  等号右边的部分在编译原理里被称作右值(rvalue)

  4、短变量声明并初始化
price := 100

  Go语言的推导声明写法,编译器会自动根据右值类型推断出左值的对应类型。

  注意:由于使用了:=,而不是赋值的=,因此推导声明写法的左值变量必须是没有定义过的变量。若定义过,将会发生编译错误。

  net.Dial 提供按指定协议和地址发起网络连接,这个函数有两个返回值,一个是连接对象(conn),一个是错误对象(err)。如果是标准格式将会变成:

var conn net.Conn
var err error
conn, err = net.Dial("tcp", "127.0.0.1:8080")

  三)Go语言多个变量同时赋值

  编程最简单的算法之一,莫过于变量交换。交换变量的常见算法需要一个中间变量进行变量的临时保存

package main

import "fmt"

func main() {
    var a int = 100
    var b int = 200
    var t int

    t = a
    a = b
    b = t

    fmt.Println(a, b)
}

  这种变量交换,占用内存较多,往往是非常奢侈的。于是计算机“大牛”发明了一些算法来避免使用中间变量:

package main

import "fmt"

func main() {
    var a int = 100
    var b int = 200
    a = a ^ b
    b = b ^ a
    a = a ^ b
    fmt.Println(a, b)
}

  到了Go语言时,内存不再是紧缺资源,而且写法可以更简单。使用 Go 的“多重赋值”特性,可以轻松完成变量交换的任务

package main

import "fmt"

func main() {
    var a int = 100
    var b int = 200
    b, a = a, b
    fmt.Println(a, b)
}

  四)匿名变量

  匿名变量的特点是一个下画线“_”,“_”本身就是一个特殊的标识符,被称为空白标识符。

  它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。使用匿名变量时,只需要在变量声明的地方使用下画线替换即可  

package main

import "fmt"

func GetData() (int, int) {
    return 100, 200
}
func main(){
    a, _ := GetData()
    _, b := GetData()
    fmt.Println(a, b)
}

  GetData() 是一个函数,拥有两个整型返回值

  匿名变量不占用内存空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用。

  五)变量的作用域

  一个变量(常量、类型或函数)在程序中都有一定的作用范围,称之为作用域。

  根据变量定义位置的不同,可以分为以下三个类型:

  • 函数内定义的变量称为局部变量
  • 函数外定义的变量称为全局变量
  • 函数定义中的变量称为形式参数
  1、局部变量

  在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,函数的参数和返回值变量都属于局部变量。

  局部变量不是一直存在的,它只在定义它的函数被调用后存在,函数调用结束后这个局部变量就会被销毁。

package main

import (
    "fmt"
)

func main() {
    //声明局部变量 a 和 b 并赋值
    var a int = 3
    var b int = 4
    //声明局部变量 c 并计算 a 和 b 的和
    c := a + b
    fmt.Printf("a = %d, b = %d, c = %d\n", a, b, c)
}

  2、全局变量

  在函数体外声明的变量称之为全局变量,全局变量只需要在一个源文件中定义,就可以在所有源文件中使用,当然,不包含这个全局变量的源文件需要使用“import”关键字引入全局变量所在的源文件之后才能使用这个全局变量。

  全局变量声明必须以 var 关键字开头,如果想要在外部包中使用全局变量的首字母必须大写。

package main
import "fmt"
//声明全局变量
var c int
func main() {
    //声明局部变量
    var a, b int
    //初始化参数
    a = 3
    b = 4
    c = a + b
    fmt.Printf("a = %d, b = %d, c = %d\n", a, b, c)
}
  3、形式参数

  在定义函数时函数名后面括号中的变量叫做形式参数(简称形参)。形式参数只在函数调用时才会生效,函数调用结束后就会被销毁,在函数未被调用时,函数的形参并不占用实际的存储单元,也没有实际值。

  形式参数会作为函数的局部变量来使用。

package main

import (
    "fmt"
)

//全局变量 a
var a int = 13

func main() {
    //局部变量 a 和 b
    var a int = 3
    var b int = 4

    fmt.Printf("main() 函数中 a = %d\n", a)
    fmt.Printf("main() 函数中 b = %d\n", b)
    c := sum(a, b)
    fmt.Printf("main() 函数中 c = %d\n", c)
}

func sum(a, b int) int {
    fmt.Printf("sum() 函数中 a = %d\n", a)
    fmt.Printf("sum() 函数中 b = %d\n", b)
    num := a + b
    return num
}

  六)变量的生命周期

  变量的生命周期与变量的作用域有着不可分割的联系:

  • 全局变量:它的生命周期和整个程序的运行周期是一致的;
  • 局部变量:它的生命周期则是动态的,从创建这个变量的声明语句开始,到这个变量不再被引用为止;
  • 形式参数和函数返回值:它们都属于局部变量,在函数被调用的时候创建,函数调用结束后被销毁。

  堆和堆的区别在于:

  • 堆(heap):堆是用于存放进程执行中被动态分配的内存段。它的大小并不固定,可动态扩张或缩减。当进程调用 malloc 等函数分配内存时,新分配的内存就被动态加入到堆上(堆被扩张)。当利用 free 等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减);
  • 栈(stack):栈又称堆栈, 用来存放程序暂时创建的局部变量,也就是我们函数的大括号{ }中定义的局部变量。

  七)常量和const关键字

  1、常量使用关键字 const 

  常量使用关键字 const 定义,用于存储不会改变的数据,常量是在编译时被创建的,即使定义在函数内部也是如此,并且只能是布尔型、数字型(整数型、浮点型和复数)和字符串型。

  由于编译时的限制,定义常量的表达式必须为能被编译器求值的常量表达式。

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

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


  常量的值必须是能够在编译时就能够确定的,可以在其赋值表达式中涉及计算过程,但是所有用于计算的值必须在编译期间就能获得。

  • 正确的做法:const c1 = 2/3
  • 错误的做法:const c2 = getNumber() // 引发构建错误: getNumber() 用做值
const (
    e  = 2.7182818
    pi = 3.1415926
)
  2、iota 常量生成器

  常量声明可以使用 iota 常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。在一个 const 声明语句中,在第一个声明的常量所在的行,iota 将会被置为 0,然后在每一个有常量声明的行加一。

type Weekday int
const (
    Sunday Weekday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)
  3、无类型常量

  Go语言的常量有个不同寻常之处。虽然一个常量可以有任意一个确定的基础类型,例如 int 或 float64,或者是类似 time.Duration 这样的基础类型,但是许多常量并没有一个明确的基础类型。

  编译器为这些没有明确的基础类型的数字常量提供比基础类型更高精度的算术运算,可以认为至少有 256bit 的运算精度。这里有六种未明确类型的常量类型,分别是无类型的布尔型、无类型的整数、无类型的字符、无类型的浮点数、无类型的复数、无类型的字符串。

  通过延迟明确常量的具体类型,不仅可以提供更高的运算精度,而且可以直接用于更多的表达式而不需要显式的类型转换。

  对于常量面值,不同的写法可能会对应不同的类型。例如 0、0.0、0i 和 \u0000 虽然有着相同的常量值,但是它们分别对应无类型的整数、无类型的浮点数、无类型的复数和无类型的字符等不同的常量类型。同样,true 和 false 也是无类型的布尔类型,字符串面值常量是无类型的字符串类型。

二、Golang数据类型

  一)基本数据类型 

  1、概述和基本使用
  • 布尔型:bool
  • 字符串:string
  • 数值型:
    • 整数类型
      • 有符号的整数类型(正负):int8(-27~27-1)、int16、int32、int64
      • 无符号的整数类型:uint8(0~255)、uint16、uint32、uint64、uintptr
      • 其他整数类型
        • int
          • 32位系统——4字节:-231~231-1
          • 64位系统——8字节:-263~263-1
        • uint
          • 32位系统——4字节:231-1
          • 64位系统——8字节:263-1
        • rune // int32 的别名 代表一个 Unicode 码
        • byte // uint8 的别名
    • 浮点类型(和操作系统无关):float32(4字节)、float64(8字节)
  • complex64、complex128

    注意:

  整数型:默认值 0

  • Golang的整数类型,默认声明为int
  • Golang程序中整型变量在使用时,遵守保小不保大的原则:即在保证程序正确运行下,尽量使用占用空间小的整数类型

  浮点型:默认值 0

  • Golang的浮点类型,默认声明为float64
  • 底层存储空间和操作系统无关
  • 浮点类型底层存储:浮号为+指数位+尾数位。所以尾数位只存了一个大概,很可能会出现精度的损失

  布尔类型:默认值 false

  • 布尔类型也叫bool类型,bool类型数据只允许取值true和false
  • 布尔类型占一个字节
  • 布尔类型适于逻辑运算,一般用于程序流程控制(使用f包含flag的变量名)

  字符串:默认值 ""

  • 字符串:就是一串固定长度的字符连接起来的字符序列
  • 字符串是不可变的,指的是字符串一旦定义好,其中的字符的值不能改变(通过索引改变)
  • 字符串的表示形式
    • 若字符串中没有特殊字符,字符串的表示形式用双引号
    • 若字符串中有特殊字符,字符串形式用反引号 ``
  • 字符串拼接: 使用加号
    • 当一个字符串过长时,注意 + 保留在上一行的最后
   2、数据类型转换
    1)通用数据类型转换

  Golang在不同类型的变量之间赋值时需要显示转换,并且只有显示转换(强制转换)

  语法:

表达式:T(v) 将值v转换为类型T

T:就是数据类型
v:就是需要转换的变量

注意:v的类型其实还是原来的类型,只是将v的值v转换为了T
    2)基本数据类型和string的转换介绍

  在程序开发中,经常需要将基本数据类型转成string类型。或者将string类型转换成基本数据类型

  • 基本数据类型转string类型

  方式一:fmt.Sprintf("%参数",表达式)  ##推荐方式

   Go语言标准库文档中文版 | Go语言中文网 | Golang中文社区 | Golang中国

import "fmt"

  方式二:使用strconv包的函数(比较繁琐)

     3)string类型转基本类型

  方式:使用strconv包的函数

  import "strconv"

  strconv包实现了基本数据类型和其字符串表示的相互转换。

  二)派生数据类型/复杂数据类型

  1、指针

  指针就是内存地址。

  最重要的的就是两个符号

  • & 取内存地址
  • * 根据地址取值
    1)指针的四个细节
  1. 可以提高指针改变指向值
  2. 指针变量接收的一定是地址值
  3. 指针变量的地址不可以不匹配
  4. 基本数据类型(又叫值类型),都有对应的指针类型,形式为 * 数据类型 :比如 int 的对应的指针就是 *int;float32对应的指针类型就是 *float32
  2、数组
  3、结构体
  4、管道
  5、函数
  6、切片
  7、接口
  8、map

  三)标识符

  1、什么是标识符

  变量、方法等,只要是起名字的地方。那个名字就是标识符

  2、标识符的定义规则

    1)三个可以组成(组成部分):数字、字母、下划线_
    2)四个注意:
  • 不可以以数字开头
  • 严格区分大小写
  • 不能包含空格
  • 不可以使用Go中保留关键字
    3)见名知意:增加可读性
    4)下滑线"_"本身在Go中是一个特殊的标识符,称为空标识符。

  可以代表任何其他的标识符,但是它对应的值会被忽略(比如 忽略某个返回值)。仅能被作为占位符使用,不能单独作为标识符使用

  声明main包,程序的入口包

    5)长度无限制,但是不建议太长
    6)起名规则
  • 包名:尽量保持package的名字和目录名保持一致,尽量采取有意义的包名(简短、有意义),不要和标准库名冲突
  • 变量名、函数名、常量名:采用驼峰法 例:stuMailAddress
  • 若变量名、函数名首字母大写;则可以被其他的包访问
    • 若首字母小写,则只能在本包中使用

  注意:

  • import 导入语句通常放在文件开头包申明语句的下面
  • 导入的包需要双引号包裹起来
  • 包名是从$GOPATH/src/后开始计算的,使用/进行路径分隔
  3、go的25个关键字和36个标识符

  25个关键字

  其中13个关键字与C++相同(break、default、switch、case、if、else、const、continue、for、return、map、struct、goto),用法也是类似。
  其它的12个不同(func、interface、select、defer、go、chan、package、falthrough、range、type、import、var)。

  

  36个预定标识符

  

  四)运算符

  

  1、算术运算符:+,-,*,/,%,++,--

  算符运算符是对数值类型的变量进行运算的。

  +加号的应用场景

  • 表示正数
  • 相加参数
  • 字符串拼接

  / 除号

package main
import "fmt" 
func main(){
    fmt.Println( 10 / 3 )
    fmt.Println(10.0/3)
}
  • 两个int类型数据运算,结果为int
  • 浮点类型参与运算,结果为float

  %取模:等价公式 a%b=a-a/b*b

  ++自增操作,--自减操作

  注意:

  • Golang中的++,-- 是操作非常简单,只能单独使用,不能参与参与到运算中
  • go语言中,++,--只能在变量的后面,不能在变量的前面
  2、赋值运算符:=,+=,-=,*=,/=,%=

  赋值运算符就是将某个运算后的值,赋值给制定的变量

  3、关系运算符:==,!=,>,<,,>=,<=

  关系运算符的结果都是bool型,不是true,就是false

  关系运算符经常用在流程控制中。

  4、逻辑运算符:&&(逻辑与/短路与),||(逻辑或/短路或),!(逻辑非)

  逻辑运算符用来进行逻辑运算的

  && 逻辑与:两个数值/表达式只要有一侧是false,结果一定为false

       && 短路与:只要第一个数值或表达式结果为false,那么后面的表达式就不用算了,直接结果为false(提交运算效率)

  || 逻辑或:两个数值/表达式只要有一侧是true,结果一定为true

  || 短路或:只要第一个数值/表达式结果为true,那么后面的表达式就不用运算了,直接结果为true(提高运算效率)

  ! 逻辑非:取反的结果

  5、位运算符:&,|,^ 

  位运算符用的少,了解即可

  6、其他运算符:&,*

  &:返回变量的存储地址

  *:取指针变量对应的数值

  五)获取用户输入

  Go语言标准库文档中文版 | Go语言中文网 | Golang中文社区 | Golang中国

 

func Scanf

 
func Scanf(format string, a ...interface{}) (n int, err error)

Scanf从标准输入扫描文本,根据format 参数指定的格式将成功读取的空白分隔的值保存进成功传递给本函数的参数。返回成功扫描的条目个数和遇到的任何错误。

 

func Fscanf

func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)

Fscanf从r扫描文本,根据format 参数指定的格式将成功读取的空白分隔的值保存进成功传递给本函数的参数。返回成功扫描的条目个数和遇到的任何错误。

 

func Sscanf

func Sscanf(str string, format string, a ...interface{}) (n int, err error)

Sscanf从字符串str扫描文本,根据format 参数指定的格式将成功读取的空白分隔的值保存进成功传递给本函数的参数。返回成功扫描的条目个数和遇到的任何错误。

func Scanln

func Scanln(a ...interface{}) (n int, err error)

Scanln类似Scan,但会在换行时才停止扫描。最后一个条目后必须有换行或者到达结束位置。

 

func Fscanln

func Fscanln(r io.Reader, a ...interface{}) (n int, err error)

Fscanln类似Fscan,但会在换行时才停止扫描。最后一个条目后必须有换行或者到达结束位置。

 

func Sscanln

func Sscanln(str string, a ...interface{}) (n int, err error)

Sscanln类似Sscan,但会在换行时才停止扫描。最后一个条目后必须有换行或者到达结束位置。

  1、方式一:使用Scanln
    var age int
    fmt.Println("请录入学生的年龄:")
    fmt.Scanln(&age)

    var name string
    fmt.Println("请录入学生的名字:")
    fmt.Scanln(&name)

    var score float32
    fmt.Println("请录入学生的成绩:")
    fmt.Scanln(&score)

    var isVIP bool
    fmt.Println("请录入学生是否为VIP:")
    fmt.Scanln(&isVIP)
//    将上面的数据在控制台打印输出
    fmt.Printf("学生的年龄为:%v, 姓名为:%v. 成绩为:%v, 是否为VIP:%v", age, name, score, isVIP )

  

  2、方式二:使用Scanf
    var age int
    var name string
    var score float32
    var isVIP bool
    fmt.Println("请输入学生的年龄,姓名,成绩,是否为VIP,使用空格进行分割")
    fmt.Scanf("%d, %s, %f, %t", &age, &name, &score, &isVIP)
    fmt.Printf("学生的年龄为:%v, 姓名为:%v. 成绩为:%v, 是否为VIP:%v", age, name, score, isVIP )

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值