第四章 基本结构和基本数据类型

4.1 文件名、关键字和标识符

  • 以“.go”为后缀。
  • 由小写字母组成,如果以多个部分组成则使用下划线“_”隔开。
  • 以下标识符无效:

    1、lab(以数字开头)

    2、case(go语言关键字)

    3、a+b(包含运算符)

4.2 go语言的结构和基本要素

4.2.1 包的概念

每个go文件只属于一个包。

一个包可以由多个以“.go”为后缀的源文件组成。

必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main

4.2.2 包导入

使用import关键字

import "fmt"

多个包的导入 :

import "fmt"
import "os"

import "fmt"; import "os"

import (
    "fmt"
    "os"
)

import ("fmt"; "os")

推荐使用 

import (
    "fmt"
    "os"
)

包的文件路径

  • 包如果不以“./”或“/”开头,则go会在全局文件中查找,例如:fmt
  • 以“./”开头则会在相对目录中查找
  • 以“/”开头的会在绝对目录中查找  

包的规范和规则

  • 除了“_”符号,包中所有代码对象标识符必须唯一
  • 标识符(包括常量、类型、函数、结构字段等等)以大写开头 ,就可以被外部包的代码所使用。(类似java中的public)
  • 标识符以小写开头,则对外包不可见。(类似java的private)
  • 对外可见的标识符(大写开头),要以包名去访问。
  • 导入一个包如果没有使用,则会引发错误:imported and not used:

别名的使用

import fm "fmt"
// fm将作为fmt 的别名

4.2.3 函数

如果你学过java或者php,你可能搞不太清楚函数和方法的区别,但你如果学过python,你会发现go中函数和方法的区别很像,但又不一样。接下来先学习函数吧!

函数最简单格式:

func functionName(){}

规范格式:被外部调用的函数遵循Pascal命名法,即首字母大写。否则遵循驼峰命名法,即第一个字母小写,其余单词首字母大写。

func functionName(parameter_list)(return_value_ist){
       .........  
}

例如:

func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
    // 这里是处理逻辑代码
    // 返回多个值
    return value1, value2
}

入口函数 

main包下面的main函数,作为程序入口,该函数没有参数,也没有返回值。

4.2.4 注释

单行注释://

多行注释:/**/

几乎所有全局作用域的类型、常量、变量、函数和被导出的对象都应该有一个合理的注释。

关于注释的规范感兴趣的道友可以自行查取相关知识:godoc工具的使用,在第三章。

4.2.5 类型

类型可以是基本类型,如:int、float、bool、string;结构化的(复合的),如:struct、array、slice、 map、channel;只描述类型的行为的,如:interface。

4.2.6 程序的一般结构

  1. 首先要做的就是命名包
  2. 接着就导包,不用等到包就不需要导入
  3. 接下来就是申明函数、常量、变量等等。

需要注意的是一个程序只有一个main包并且只有一个main函数作为入口,一个文件中可以包含多个init函数,并且会依次经行初始化工作。

4.2.7 类型转换

go语言为了避免像C、C++、java等等语言那样,由于隐式转换可能带来数据的不确定性,因此所有的转换都必须显示说明。

类似转换可以看作是函数,当然我们也可以自定义自己的类型转换。

格式:

valueOfTypeB    =    typeB(valueOfTypeA)

示例:

a := 5.0
b := int(a)

 4.2.8 命名规范

干净、可读的代码和简洁性是 Go 追求的主要目标。通过 gofmt 来强制实现统一的代码风格。Go 语言中对象的命 名也应该是简洁且有意义的。像 Java 和 Python 中那样使用混合着大小写和下划线的冗长的名称会严重降低代码 的可读性。名称不需要指出自己所属的包,因为在调用的时候会使用包名作为限定符。返回某个对象的函数或方法的 名称一般都是使用名词,没有 Get... 之类的字符,如果是用于修改某个对象,则使用 SetName 。有必须要的话 可以使用大小写混合的方式,如 MixedCaps 或 mixedCaps,而不是使用下划线来分割多个名称。

4.3 常量

常量就是运行过程中值和地址不变的量。站在权限角度来看,这个常量是只读类型。

格式:

const identifier [type]    = value

go为了简化操作定义常量的方式主要有两种:

显式类型定义: 

const b string = "abc"

隐式类型定义:

 const b = "abc"

 注意:常量是在编译是就确定的量。

正确的:

const b = 1/2;

错误的:

const a = functionName() // 不能用函数的返回值作为常量值,因为返回值的地址是不确定的。

常量几种申明方式:

单个声明

const a = 1

多个申明:值得注意的是,类型必须一致

1、一般申明

const (
    b = 1
    a = 2      
)
const (
    a = 1
    b
    c = 3
    d = iota
)

示例

package main

import "fmt"
const (
    a = 1
    b
    c = 3
    d = iota
    e
    f
)
func main() {
    fmt.Println("a=", a)
    fmt.Println("b=", b)
    fmt.Println("c=", c)
    fmt.Println("d=", d)
    fmt.Println("e=", e)
    fmt.Println("f=", f)
}

结果:

a= 1
b= 1
c= 3
d= 3
e= 4
f= 5

4.4 变量

申明格式:var identifier type

申明用类型多个变量

var a, b *int;

 多个变量声明

var a int
var b bool
var c string

简化var

var (
       a int
       b bool
       c string    
)
d := 1 // := 函数局部变量中使用
a, b, c := 1, "a", false
 

4.4.1 值类型和引用类型

基本类型(int、float、string、bbool)属于值类型,使用这些类型的时候会直接指向内存中的值。

引用类型(指针),指向的是一块内存地址。

本质上来说,值类型和引用类型都是直接指向内存中的值,不同在于值类型指向的是值,而引用类型指向的是值的引用。

比如:a := "string", 使用&a会得到a的地址,这个地址的值就称为a的引用,而应用类型的变量就是保存这些地址的值。

 下面用一个例子说明一下

package main

import "fmt"

func main() {
    a := "string"
    b := &a
    fmt.Println("a的值等于:", a)
    fmt.Println("a的地址等于:", &a)
    fmt.Println("b的值等于:", b)
    fmt.Println("b指向的值等于:", *b)
    fmt.Println("b的地址等于:", &b)
    fmt.Println("分割线----------")
    // 同步改变变量的值
    a = "new String"
    fmt.Println("a的值等于:", a)
    fmt.Println("a的地址等于:", &a)
    fmt.Println("b的值等于:", b)
    fmt.Println("b指向的值等于:", *b)
    fmt.Println("b的地址等于:", &b)
    fmt.Println("分割线----------")
    // 引用类型可以修改引用的地址值
    c := "ttt"
    b = &c
    fmt.Println("c的值等于:", c)
    fmt.Println("c的地址等于:", &c)
    fmt.Println("b的值等于:", b)
    fmt.Println("b指向的值等于:", *b)
    fmt.Println("b的地址等于:", &b)
}

结果:

a的值等于: string
a的地址等于: 0xc00003a1f0
b的值等于: 0xc00003a1f0
b指向的值等于: string
b的地址等于: 0xc000006028
分割线----------
a的值等于: new String
a的地址等于: 0xc00003a1f0
b的值等于: 0xc00003a1f0
b指向的值等于: new String
b的地址等于: 0xc000006028
分割线----------
c的值等于: ttt
c的地址等于: 0xc00003a240
b的值等于: 0xc00003a240
b指向的值等于: ttt
b的地址等于: 0xc000006028

 可能使用过java的人看到a重新赋值后地址依然没有感到疑惑,解释一下,在java中string类型是引用类型(类似例子中的b)。

 在这个例子中我们同样看到,使用“&”能够获得该变量的地址,即为引用,使用“*”能从引用中获取到指向的值。

 4.4.2 init函数

变量除了可以在全局声明中初始化,也可以在 init 函数中初始化。这是一类非常特殊的函数,它不能够被人为调 用,而是在每个包完成初始化后自动执行,并且执行优先级比 main 函数高。

每一个源文件都可以包含一个或多个 init 函数。初始化总是以单线程执行,并且按照包的依赖关系顺序执行。

示例:

package main

var PI float64
func main() {

}

func init(){
    PI = 122333.5666
}
func init()  {
    print("PI", PI, "\n")
}
func init()  {
    PI = 11111.11111
}
func init()  {
    print("PI=", PI, "\n")
}

4.5 基本类型和运算

bool类型

bool类型的取值只有两个:true和false;可以通过运算符比较获得。

整型(整数)

  • int8(-128 -> 127)
  • int16(-32768 -> 32767)
  • int32(-2,147,483,648 -> 2,147,483,647)
  • int64(-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807)

无符号整数:

  • uint8(0 -> 255)
  • uint16(0 -> 65,535)
  • uint32(0 -> 4,294,967,295)
  • uint64(0 -> 18,446,744,073,709,551,615

int和uint在32位操作系统上4个字节,64位系统8个字节

float类型(小数)

  • float32(+- 1e-45 -> +- 3.4 * 1e38)
  • float64(+- 5 1e-324 -> 107 1e308)

复数

字符类型

var ch byte = 'A'

  • 判断是否为字母: unicode.IsLetter(ch)
  • 判断是否为数字: unicode.IsDigit(ch)
  • 判断是否为空白符号: unicode.IsSpace(ch)

字符串

单引号和双引号的区别:单引号只能写一个字符或者是ASCII码,双引号是字符串,这个定义跟C语言一样,和c/c++不一样的是Go字符串根据长度限定,不是特殊字符“\0”。\0一般作为C语言字符数组的结束符。

说起字符串还得要说一下转义字符,这些常用于字符串中,主要转义字符如下:

  • \n :换行符
  • \r :回车符
  • \t :tab 键
  • \u 或 \U :Unicode 字符
  • \\ :反斜杠自身

两个字符串连接使用"+"

运算符优先级

有些运算符拥有较高的优先级,二元运算符的运算方向均是从左至右。下表列出了所有运算符以及它们的优先级,由 上至下代表优先级由高到低:

优先级 运算符
7 ^(按位取反)、!(非)
6 *(乘)、/(除)、%(求余)、<<(位运算左移)、>>(位运算右移)、&(按位与)
5 +(加)、-(减)、|(按位或)、^(按位异或)
4 ==(相等)、!=(不等)、>(大于)、>= (大于等于)、<(小于)、<=(小于等于)
3 <- (管道传值)
2 &&(与)
1 ||(或)

大家可以自行查询关于运算符
类型别名

type TZ int

package main
type TZ int
func main() {
    var a, b TZ = 3, 4
    c := a + b
    print(c)
}

4.6 strings包

判断前缀:strings.HasPerfix(s, prefix string) bool

判断是否以“s”开头

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "string"
    fmt.Println(strings.HasPrefix(s, "s"))  // true
    fmt.Println(strings.HasPrefix(s, "t"))  // false
}

判断后缀:strings.HasSuffix(s, suffix string) bool

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "string"
    fmt.Println(strings.HasSuffix(s, "g"))  // true
    fmt.Println(strings.HasSuffix(s, "t"))  // false
}

包含子字符串: strings.Contains(s, substr string) bool

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "string"
    fmt.Println(strings.Contains(s, "ri"))  // true
    fmt.Println(strings.Contains(s, "2e"))  // false
}

判断子字符串的索引位置,找不到返回-1:strings.index(s, substr string) int

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "my name is hardy,last name is kay"
    fmt.Println(strings.Index(s, "hardy")) // 11
    fmt.Println(strings.Index(s, "is"))  // 8
    fmt.Println(strings.Index(s, "hello"))  // -1
}

字符串替换: strings.Replace(str, old, new string, n int)  string

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "my name is hardy,last name is kay"
    fmt.Println(strings.Replace(s, "hardy", "hello", 2)) // my name is hello,last name is kay
    fmt.Println(strings.Replace(s, "is", "hello", 2)) // my name hello hardy,last name hello kay
}

统计子字符串出现的次数: strings.Count(s, substr string) int

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "my name is hardy,last name is kay"
    fmt.Println(strings.Count(s, "hardy")) // 1
    fmt.Println(strings.Count(s, "hello")) // 0
    fmt.Println(strings.Count(s, "is")) // 2
    fmt.Println(strings.Count(s, "s")) // 3
}

重复字符串: strings.Repeat(s, 10) string

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "hardy"
    fmt.Println(strings.Repeat(s, 10)) // hardyhardyhardyhardyhardyhardyhardyhardyhardyhardy
}

全部转为大写:ToUpper(s string) string

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "my name is hardy,last name is kay"
    fmt.Println(strings.ToUpper(s)) // MY NAME IS HARDY,LAST NAME IS KAY
}

全部转为小写:ToLower(s string) string

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "MY NAME IS HARDY,LAST NAME IS KAY"
    fmt.Println(strings.ToLower(s)) // my name is hardy,last name is kay
}

剔除开头或结尾的字符:Trim(s string, cutset string) string

类似的操作函数还有:TrimSpace 来剔除字符串开头和结尾的空白符号,剔除开头或者结尾的字符串,则可以使用 TrimLeft 或者 TrimRight 来实现。

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "my name is hardy, my last name is kay my"
    fmt.Println(strings.Trim(s, "my")) //  name is hardy, my last name is kay
}

 分割字符串

空白字符分割:Fields(s string) []string

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "my name is hardy              last name is kay my"
    r := strings.Fields(s)
    for k,v := range r{
        fmt.Println("k=", k, ", v=", v)
    }
}

结果:

k= 0 , v= my
k= 1 , v= name
k= 2 , v= is
k= 3 , v= hardy
k= 4 , v= last
k= 5 , v= name
k= 6 , v= is
k= 7 , v= kay
k= 8 , v= my

 指定字符串分割:Split(s, sep string) []string

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "my name is hardy last name is kay my"
    r := strings.Split(s, "i")
    for k,v := range r{
        fmt.Println("k=", k, ", v=", v)
    }
}

 结果:

k= 0 , v= my name
k= 1 , v= s hardy last name
k= 2 , v= s kay my

 拼接slice到字符串

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "my name is hardy last name is kay my"
    r := strings.Fields(s)
    fmt.Println(strings.Join(r, ",")) //my,name,is,hardy,last,name,is,kay,my 
}

猜你喜欢

转载自www.cnblogs.com/hardykay/p/12901820.html