Go语言语法基础

Go语言语法基础

1.Go项目构建及编译

一个Go工程中主要包含以下三个目录:

go 复制代码
src:源代码文件
pkg:包文件
bin:相关bin文件

我们可以引用别人的包也可以发布自己的包,但是为了防止不同包的项目名冲突,我们通常使用顶级域名来作为包名的前缀,这样就不担心项目名冲突的问题了

因为不是每个个人开发者都拥有自己的顶级域名,所以目前流行的方式是使用个人的github用户名来区分不同的包:

举个例子:张三和李四都有一个名叫studygo的项目,那么这两个包的路径就会是:

go 复制代码
import "github.com/zhangsan/studygo"

和:

go 复制代码
import "github.com/lisi/studygo"

2.下划线的用处

++1、下划线在import中++

当导入一个包时,该包下的文件里所有init()函数都会被执行,然而,有些时候我们并不需要把整个包都导入进来,仅仅是是希望它执行init()函数而已。这个时候就可以使用 import 引用该包。即使用import _ 包路径只是引用该包,仅仅是为了调用init()函数,无法通过包名来调用包中的其他函数

++2、下划线在代码中++

下划线意思是忽略这个变量,意思是那个位置本应赋给某个值,但是咱们不需要这个值,这样编译器可以更好的优化,任何类型的单个值都可以丢给下划线,例如:

go 复制代码
package main

import (
    "os"
)

func main() {
    buf := make([]byte, 1024)
    f, _ := os.Open("/Users/***/Desktop/text.txt")
    defer f.Close()
    for {
        n, _ := f.Read(buf)
        if n == 0 {
            break    

        }
        os.Stdout.Write(buf[:n])
    }
}

3.Go语言变量与常量

变量命名规范

1、Go语言中的函数名、变量名、常量名、类型名、语句标号和包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线

2、大写字母和小写字母是不同的:heapSortHeapsort是两个不同的名字

3、如果一个名字是在函数内部定义,那么它就只在函数内部有效。如果是在函数外部定义,那么将在当前包的所有文件中都可以访问。

名字的开头字母的大小写决定了名字在包外的可见性。如果一个名字是大写字母开头的,那么它将是导出的,也就是说可以被外部的包访问,例如fmt包的Printf函数就是导出的,可以在fmt包外部访问。包本身的名字一般总是用小写字母

4、通常来说,如果一个名字的作用域比较大,生命周期也比较长,那么用长的名字将会更有意义

5、Go语言程序员推荐使用 驼峰式 命名,当名字由几个单词组成时优先使用大小写分隔,而不是优先用下划线分隔。因此,在标准库有QuoteRuneToASCIIparseRequestLine这样的函数命名

变量声明与初始化

Go语言主要有四种类型的声明语句:varconsttypefunc,分别对应变量、常量、类型和函数实体对象的声明

go语言中还支持批量变量声明,例如:

go 复制代码
var (
    a string
    b int
    c bool
    d float32
)

var声明语句可以创建一个特定类型的变量,然后给变量附加一个名字,并且设置变量的初始值。语法如下:

plain 复制代码
var 变量名字 类型 = 表达式

其中类型= 表达式两个部分可以省略其中的一个。如果省略的是类型信息,那么将根据初始化表达式来推导变量的类型信息。如果初始化表达式被省略,那么将用零值初始化该变量,例如:

go 复制代码
func main() {
    var s string
    var d = 12
    fmt.Println(s)
    fmt.Println(d)
}

在函数内部,有一种称为简短变量声明语句的形式可用于声明和初始化局部变量。它以"名字 := 表达式"形式声明变量,变量的类型根据表达式来自动推导,例如:

go 复制代码
func main() {
    i := 100
    fmt.Println(i)
}

因为简洁和灵活的特点,简短变量声明被广泛用于大部分的局部变量的声明和初始化

这里有一个比较微妙的地方:简短变量声明左边的变量可能并不是全部都是刚刚声明的。如果有一些已经在相同的词法域声明过了,那么简短变量声明语句对这些已经声明过的变量就只有赋值行为

例如:

在下面的代码中,第一个语句声明了in和err两个变量。在第二个语句只声明了out一个变量,然后对已经声明的err进行了赋值操作

go 复制代码
in, err := os.Open(infile)
// ...
out, err := os.Create(outfile)

常量

一个常量的声明语句定义了常量的名字,和变量的声明语法类似,常量的值不可修改,这样可以防止在运行期被意外或恶意的修改

所有常量的运算都可以在编译期完成,这样可以减少运行时的工作,也方便其他编译优化。当操作数是常量时,一些运行时的错误也可以在编译时被发现,例如整数除零、字符串索引越界、任何导致无效浮点数的操作等

常量间的所有算术运算、逻辑运算和比较运算的结果也是常量,对常量的类型转换操作或以下函数调用都是返回常量结果:len、cap、real、imag、complex和unsafe.Sizeof

iota 常量生成器

iota是go语言的常量计数器,只能在常量的表达式中使用。 iota在const关键字出现时将被重置为0。const中每新增一行常量声明将使iota计数一次

例如:周日将对应0,周一为1,如此等等

go 复制代码
type Weekday int

const (
    Sunday Weekday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)

我们也可以在复杂的常量表达式中使用iota,下面是一个更复杂的例子,每个常量都是1024的幂:

go 复制代码
const (
    _ = 1 << (10 * iota)
    KiB // 1024
    MiB // 1048576
    GiB // 1073741824
    TiB // 1099511627776             (exceeds 1 << 32)
    PiB // 1125899906842624
    EiB // 1152921504606846976
    ZiB // 1180591620717411303424    (exceeds 1 << 64)
    YiB // 1208925819614629174706176
)

4.Go语言基本数据类型

整型

Go语言同时提供了有符号和无符号类型的整数运算。有int8、int16、int32和int64四种截然不同大小的有符号整数类型,分别对应8、16、32、64bit大小的有符号整数,与此对应的是uint8、uint16、uint32和uint64四种无符号整数类型

还有两种一般对应特定CPU平台机器字大小的有符号和无符号整数intuint;其中int是应用最广泛的数值类型

无符号数往往只有在位运算或其它特殊的运算场景才会使用,就像bit集合、分析二进制文件格式或者是哈希和加密操作等。它们通常并不用于仅仅是表达非负数量的场合

最后,还有一种无符号的整数类型uintptr,没有指定具体的bit大小但是足以容纳指针。uintptr类型只有在底层编程时才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方

算术运算符+、-、*和/可以适用于整数、浮点数和复数,但是取模运算符%仅用于整数间的运算

对于每种类型T,如果转换允许的话,类型转换操作T(x)将x转换为T类型,例如:

go 复制代码
func main() {
    f := 3.141 // a float64
    i := int(f)
    fmt.Println(f, i) // "3.141 3"
    f = 1.99
    fmt.Println(int(f)) // "1"
}

任何大小的整数字面值都可以用以0开始的八进制格式书写,例如0666;或用以0x0X开头的十六进制格式书写,例如0xdeadbeef。十六进制数字可以用大写或小写字母。如今八进制数据通常用于POSIX操作系统上的文件访问权限标志,十六进制数字则更强调数字值的bit位模式

当使用fmt包打印一个数值时,我们可以用%d、%o或%x参数控制输出的进制格式,就像下面的例子:

go 复制代码
func main() {
    o := 0666
    fmt.Printf("%d %[1]o %#[1]o\n", o) // "438 666 0666"
    x := int64(0xdeadbeef)
    fmt.Printf("%d %[1]x %#[1]x %#[1]X\n", x)
    // Output:
    // 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF
}

请注意fmt的两个使用技巧:

1、通常Printf格式化字符串包含多个%参数时将会包含对应相同数量的额外操作数,但是%之后的[1]副词告诉Printf函数再次使用第一个操作数

2、第二,%后的#副词告诉Printf在用%o、%x或%X输出时生成0、0x或0X前缀

浮点型

Go语言提供了两种精度的浮点数,float32和float64

一个float32类型的浮点数可以提供大约6个十进制数的精度,而float64则可以提供约15个十进制数的精度;通常应该优先使用float64类型

小数点前面或后面的数字都可能被省略(例如.707或1.)。很小或很大的数最好用科学计数法书写,通过e或E来指定指数部分:

go 复制代码
const Avogadro = 6.02214129e23  // 阿伏伽德罗常数
const Planck   = 6.62606957e-34 // 普朗克常数

例子:打印e的幂,打印精度是小数点后三个小数精度和8个字符宽度:

go 复制代码
for x := 0; x < 8; x++ {
    fmt.Printf("x = %d e^x = %8.3f\n", x, math.Exp(float64(x)))
}

复数

Go语言提供了两种精度的复数类型:complex64complex128,分别对应float32float64两种浮点数精度。内置的complex函数用于构建复数,内建的realimag函数分别返回复数的实部和虚部:

go 复制代码
func main() {
    var x complex128 = complex(1, 2) // 1+2i
    var y complex128 = complex(3, 4) // 3+4i
    fmt.Println(x * y)               // "(-5+10i)"
    fmt.Println(real(x * y))         // "-5"
    fmt.Println(imag(x * y))         // "10"
}

上面x和y的声明语句还可以简化:

go 复制代码
x := 1 + 2i
y := 3 + 4i

如果一个浮点数面值或一个十进制整数面值后面跟着一个i,例如3.141592i或2i,它将构成一个复数的虚部,复数的实部是0:

go 复制代码
fmt.Println(1i * 1i) // "(-1+0i)"

另外我,复数也可以用==和!=进行相等比较。只有两个复数的实部和虚部都相等的时候它们才是相等的

布尔型

一个布尔类型的值只有两种:true和false

在Go语言中,布尔值并不会隐式转换为数字值0或1

指针

一个指针的值是另一个变量的地址。一个指针对应变量在内存中的存储位置。通过指针,我们可以直接读或更新对应变量的值,而不需要知道该变量的名字

如果用"var x int"声明语句声明一个x变量,那么&x表达式(取x变量的内存地址)将产生一个指向该整数变量的指针,指针对应的数据类型是*int,指针被称之为"指向int类型的指针"

go 复制代码
func main() {
    x := 1
    p := &x
    fmt.Println(*p) // "1"
    *p = 2
    fmt.Println(x) // "2"
}

任何类型的指针的零值都是nil。如果p指向某个有效变量,那么p != nil测试为真。指针之间也是可以进行相等测试的,只有当它们指向同一个变量或全部是nil时才相等

go 复制代码
func main() {
    var x, y int
    fmt.Println(&x == &x, &x == &y, &x == nil) // "true false false"
}

字符串

窥探字符串类型

因为字符串是不可修改的,因此尝试修改字符串内部数据的操作也是被禁止的,例如:

go 复制代码
s[0] = 'L'

在一个双引号包含的字符串中,可以用以反斜杠\开头的转义序列插入任意的数据。下面的换行、回车和制表符等是常见的ASCII控制代码的转义方式:

plain 复制代码
\a      响铃
\b      退格
\f      换页
\n      换行
\r      回车
\t      制表符
\v      垂直制表符
\'      单引号
\"      双引号
\\      反斜杠

使用反引号代替双引号,就可以定义一个多行字符串:

go 复制代码
func main() {
    const GoUsage = `Go is a tool for managing Go source code.

Usage:
    go command [arguments]
...`
    fmt.Println(GoUsage)
}

字符串内建函数

例如内置的len函数可以返回一个字符串中的字节数目:

go 复制代码
func main() {
    s := "hello, world"
    fmt.Println(len(s)) // "12"
    d := "你好,世界"
    fmt.Println(len(d)) // "15"
}

常用操作:

方法 介绍
len(str) 求长度
+或fmt.Sprintf 拼接字符串
strings.Split 分割
strings.Contains 判断是否包含
strings.HasPrefix,strings.HasSuffix 前缀/后缀判断
strings.Index(),strings.LastIndex() 子串出现的位置
strings.Join(a[]string, sep string) join操作

字符串和数字的转换

将一个整数转为字符串,一种方法是用fmt.Sprintf返回一个格式化的字符串;另一个方法是用strconv.Itoa

go 复制代码
func main() {
    x := 123
    y := fmt.Sprintf("%d", x)
    fmt.Println(y, strconv.Itoa(x)) // "123 123"
}

如果要将一个字符串解析为整数,可以使用strconv包的Atoi或ParseInt函数,还有用于解析无符号整数的ParseUint函数:

go 复制代码
func main() {
    x, _ := strconv.Atoi("123")
    y, _ := strconv.ParseInt("123", 10, 64)
    fmt.Println(x)
    fmt.Println(y)
}
相关推荐
yuanbenshidiaos1 分钟前
c++------------------函数
开发语言·c++
程序员_三木13 分钟前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
是小崔啊23 分钟前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴
tianmu_sama29 分钟前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
黄公子学安全32 分钟前
Java的基础概念(一)
java·开发语言·python
liwulin050633 分钟前
【JAVA】Tesseract-OCR截图屏幕指定区域识别0.4.2
java·开发语言·ocr
jackiendsc37 分钟前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
Yuan_o_38 分钟前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
Oneforlove_twoforjob42 分钟前
【Java基础面试题027】Java的StringBuilder是怎么实现的?
java·开发语言
羚羊角uou44 分钟前
【C++】优先级队列以及仿函数
开发语言·c++