Go基本数据类型

Go语言的数据类型分为四类:

  1. 基本数据类型(包括数字、布尔以及字符串)
  2. 符合数据类型(包括数组和结构体,通过组合简单类型,来表达更加复杂的数据结构)
  3. 引用类型(包括指针、切片、管道、字典、函数,,虽然种类很多,但它们的共同之处在于,它们都是直接引用程序的变量或状态,因此,作用于一个引用的操作的效果会被该引用的所有副本观察到)
  4. 接口类型

整型

Go提供了有符号和无符号两种整型:

  1. 有符号位整型(int8,int16,int32,int64)
  2. 无符号整型(unit8,unit16,unit32,unit64)
  3. 两种特定与平台CPU的整型(int、unit,一般情况下,64位操作系统的int与unit对应的就是int64和unit64,32位操作系统类似,但是并不绝对,也与编译器相关)
  4. rune类型,他是一个Unicode码点,与int32类型等价
  5. byte类型,他与int8等价
  6. 无符号整数类型unitptr

算数运算、逻辑运算、比较运算的二元操作符的优先级:

    • / % << >> & &^
      • | ^
  1. == != < <= > >=
  2. &&
  3. ||
    同他优先级的自左到右执行
    括号可以提高优先级

PS:Go语言的%操作符与其他编程语言不太想死,它的结果的符号位,完全由被取模数的符号决定:

-5 % -2 = -1
-5 %  2 = -1
 5 % -2 =  1

Go提供了位操作符:

  • & 位运算 AND
  • | 位运算 OR
  • &^ 位清空 (AND NOT)
  • << 左移
  • >> 右移

浮点型

复数

布尔型

布尔型只有true和false
布尔值可以和&&或者||结合使用,并且可以有短路的行为
布尔值并不会隐式转换为0和1,反之亦然,我们需要使用条件判断来做转换:

    var b bool = true
    flag := 0
    if b {
        flag = 1;
    }

字符串

一个字符串是一个不可变的字节序列,字符串可能包含任意数据,包括值为0的byte,但通常包含的是人类可读的文本。
文本字符串通常被解释为采用UTF8编码的Unicode码点(rune)序列

    s1 := "123"
    s2 := "abc"
    s3 := "中国"
    len1 := len(s1)
    len2 := len(s2)
    len3 := len(s3)
    //'中' 的二进制格式是 11100100 10111000 10101101
    //1110 0100 = 228
    //1011 1000 = 184
    //1010 1101 = 173
    fmt.Println(s3[0]," ",s3[1]," ",s3[2]," ",s3[3]," ",s3[4]," ",s3[5]) //228   184   173   229   155   189

    fmt.Println("123的长度为 ; ",len1) //3
    fmt.Println("abc的长度为 ; ",len2)   //3
    fmt.Println("中国的长度为 ; ",len3) //6
    //字符串因为是序列,所以可以做切片
    zhong := s3[0:3]
    guo := s3[3:len3]
    fmt.Println("我爱你 ",zhong,guo)

Go内置的len(T)方法返回的是字节数目,而非字符数目
字符串可以使用像==和<这样的比较操作符来进行比较,而比较则是逐个字节进行比较
字符串是不可变的,因此对于字符串的修改删除操作都是不允许的:

    imu1 := "abcd"
    //imu1[0] = 'L'     //编译不过去
    imu2 := imu1
    fmt.Println(imu1[0])
    imu1 += " efg"
    fmt.Println("imu2 = ",imu2)
    fmt.Println("imu1 = ",imu1)

字符串不可变的特性,使得两个字符串共享相同的底层数据也是安全的,复制任意长度的字符串的成本也很廉价。同样,字符串可以其切片共享相同的数据,这是安全的。

因为Go源文件总是以UTF8编码的,而且Go的文本字符串也被解释为UTF8,因此我们可以在字符串的字面量中包含Unicode码点.

字符串字面量中不仅可以使用转移字符,而且通过使用十六进制或者八进制,还可以包含任意的byte(字节),使用方法如下:

byte_str_16 := "\x11" 

    byte_str_16 := "\x48" //  \xhh 表示16进制的字节,h是一个16进制数字,大小写随意
    fmt.Println(byte_str_16) //H
    byte_str_8 := "\110" //八进制比较简单,直接一斜杠起始,后面跟着三个8进制数字,但是需要注意的是,这三个数字不能大于\377,因为\377的八进制为011 111 111,而这就是一个字节的最大值了,即十进制的255
    fmt.Println(byte_str_8)

原生字符串字面量以`来替换掉”,需要注意的是,原生字符串中的转移字符是无效的,内容全部作为字面量,包括反斜杠和换行,所以原生字符串可能会占用多行。唯一需要处理是删除回车,这样字符串的值在所有平台上都是相同的,包括像windows这样传统地将回车放在文本文件中的平台。
原生字符串字面量的作用一般用于正则表达式或者HTML模板、JSON字面量、命令展示行以及其他需要扩展到多行的场景。

Unicode

Unicode收集了世界上所有的字符,包括音符以及特别神秘的字符,并为每一个字符分配一个唯一的数字,我们称这个数字为Unicode码点,在Go语言中,也可以成为rune。
我们可以讲一个rune序列一int32序列的形式展示出来,但是带来的问题就是,每一个字符都占用4个字节(因为rune、int32格式占用的内存空间就是4个字节)

UTF-8

UTF-8将Unicode代码点编码为可变长度的字节。
UTF-8以1~4个字节来编码所有的Unicode码点。ASCII部分以一个字节表示,绝大多数的字符一2~3个字节表示。对码点(也可以说是rune)所编码首个字节的高位bit位标识了该编码占用几个字节。方法如下;

  1. 如果首位为0,那么表示该编码占用1个字节,首位后面的7个bit位用于编码ASCII
  2. 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

每个使用UTF-8存储的字符,除了第一个字节外,其余字节的头两个比特都是以”10”开始,使文字处理器能够较快地找出每个字符的开始位置。

在UTF-8文件的开首,很多时都放置一个U+FEFF字符(UTF-8以EF,BB,BF代表),以显示这个文本文件是以UTF-8编码(了解即可)

UTF-8的这些特质,保证了一个字符的字节序列不会包含在另一个字符的字节序列中。这确保了以字节为基础的部分字符串比对(sub-string match)方法可以适用于在文字中搜索字或词。有些比较旧的可变长度8位编码(如Shift JIS)没有这个特质,故字符串比对的算法变得相当复杂。虽然这增加了UTF-8编码的字符串的信息冗余,但是利多于弊。另外,数据压缩并非Unicode的目的,所以不可混为一谈。即使在发送过程中有部分字节因错误或干扰而完全丢失,还是有可能在下一个字符的起点重新同步,令受损范围受到限制.

虽然变长的字节编码使得我们无法通过索引来实现访问第n个字符这样的需求,但是他却带来很多其他的优点:

  1. Backward compatibility(向后兼容):完全的兼容ASCII
  2. Self-synchronization(自同步):
  3. Prefix code(前缀编码):字符编码的第一个字节表示了该字符所占的字节个数,从流中读取数据可以立即对每个接收到的序列进行解码,而无需首先等待下一个序列的第一个字节或流结束指示。多字节序列的长度很容易由人类决定,因为它只是前导字节中高阶bit位为1的数目。如果流在序列中间结束,不正确的字符将不会被解码。
  4. Fallback and auto-detection(回退和自动检测):
  5. Sorting order:通过对相应的字节序列进行排序,可以按照Unicode码点顺序对UTF-8字符串列表进行排序。

这里没有内嵌的NUL(ZERO)字节,这对于使用NUL作为终止字符串的编程语言来说很方便。

rune字面量可以使用Unicode转义:

    //首先,中字的Unicode16进制码点是4E2D
    //当我们需要转义的码点是2字节的话,那么我们使用\u + 2字节码点
    //当我们需要转义的码点是3个字节的话,那么我们需要使用\U + 4字节码点
    //PS:码点需找工具查,不要使用UTF8编码直接作为码点,因为UTF8中有前缀编码,
    // 所以不要直接使用,当然如果理解了UTF8编码规则的话,那么就可以从其中找到其Unicode码点
    rune1 := '中'
    var rune2 = '\u4E2D'

对于无法解析的码点,UTF8会以’\uFFFD’来替换,一般显示出来的话就是一个令牌,令牌内有个?

对UTF-8编码的字符串应用[]rune()转换,会返回该字符串所对应编码的码点序列:

    code_point_str := "123贾宝玉"
    //应用转换
    code_point_array := []rune(code_point_str)
    fmt.Printf("%x\n", code_point_array)
    code_point_str = string(code_point_array)
    fmt.Println(code_point_str)
输出:
[31 32 33 8d3e 5b9d 7389]
123贾宝玉

通过输出确实可以看到,[]rune是以码点来分解的字符串
字符串与切片

字符串是不可变的字节序列,而字节切片却是可以修改的:

    str := "贾宝玉"
    slices := []byte(str)
    fmt.Println(slices)
    newer_str := string(slices)
    fmt.Println(newer_str)
    输出
    [228 184 173 229 155 189]
    中国
    //从输出可以看出,[]byte是讲字符串的UTF8编码拆分为字节
    //[]byte(s)转换分配了一个新的字节数字来持有字符串s的字节的拷贝,然后生成一个切片来引用层的字节数组。但是在某些情况下,编译器优化会在某些情况下能够避免分配和复制,但这就需要保证对于slices的修改,不会影响到底层的字符串str
    //我们可以通过string(byte[])来讲切片转换为不可变的字符串

为了不免转换中不要的内存分配,bytes包和strings包提供可很多使用方法:

//strings包
func Contains(s, substr string) bool
func Count(s, sep string) int
func Fields(s string) []string
func HasPrefix(s, prefix string) bool
func Index(s, sep string) int
func Join(a []string, sep string) string
//bytes包
func Contains(b, subslice []byte) bool
func Count(s, sep []byte) int
func Fields(s []byte) [][]byte
func HasPrefix(s, prefix []byte) bool
func Index(s, sep []byte) int
func Join(s [][]byte, sep []byte) []byte

bytes包中有一个Buffer类型,用于字节切片的缓存。
初始的Buffer是空的,随着像其中添加字符串、字节、字节数组等,数据会慢慢变大
Buffer是不需要初始化的,对于它,零值也是有意义的:

    var buffer bytes.Buffer
    buffer.WriteByte('A')
    buffer.WriteRune('中')
    buf := buffer.String()
    fmt.Println(buf)//A中

可以通过WriteRune和wirteByte来向其中写入字符串、字节

字符串与数值之间的转换

数值 -> 字符串
方法一:

    num := 10
    itoa := strconv.Itoa(num)

方法二

    num := 10
    ss := fmt.Sprintf("%d", num)

数值二进制字符串转换:
方法一

    num := 10
    fmt.Println(strconv.FormatInt(int64(num), 2)) //1010 对应的10进制值正是10

方法二:

    bb := fmt.Sprintf("%b",int64(10))
    //这里看得出来,Springf的强大,通过%b,可以转为2进制字节形式,还有更多的%o%x需要我们来探讨

字符串 -> 数值
方法一:

    demo := "123"
    nums,err := strconv.Atoi(demo)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(nums)

方法二:

    //参数:
    //第一个参数填写的是字符串
    // base = 0,则字符串中自己加进制,如Ox123,那么转换后就是291,它是16进制,08转换后为8,它是八进制
    // base = 16,则字符串按照16进制来转换,
    // base = 8, 则字符串按照8进制来转换
    // base = 10, 则字符串按照10进制来转换

    //第三个参数表示的是转换出的int的类型,值值可以写0,8,16,32,64,对应int,int8,int16,int32,int64
    parseInt, err := strconv.ParseInt(demo, 10, 64)

fmt.Scanf()可以用于解析字符串与数字都在一行的情况

字符串 -> []byte

//将UTF-8的每一位转为字节
bs := []byte("哈哈")

常量

格式:

    const 常量标识符 = 常量值

多常量声明:

    const(
        常量标识符 = 常量值
        常量标识符 = 常量值
    )

所有的常量都在编译期间完成.
对于多常量的声明,如果声明的多个常量的值是一致的,那么除了第一个外其它的常量右边的初始化表达式都可以省略,具体如下:

    const (
        num1 = 10
        num2
        num3 = 20
        num4

    )
    fmt.Println(num2) //10
    fmt.Println(num4) //20
iota 常量生成器
    type Weekday int

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

iota的初始值是0,然后逐步自增1,那么到Saturday时候,它的值就是6了

无类型常量

Go支持无明确类型的为常量,并且编译器会为这些无明确类型的常量提供更高的精度的算数运算。
这里有六种无明确数据类型常量类型:

  1. 无类型的布尔
  2. 无类型的整数
  3. 无类型的字符(rune)
  4. 无类型的浮点数
  5. 无类型的复数
  6. 无类型的字符串
    无类型的常量不仅可以提高运算精度,而且可以直接用于更多的表达式而不需要类型转换。
    const Pi64 float64 = math.Pi
    fmt.Println(Pi64)
    var x float32 = float32(Pi64)
    fmt.Println(x)
    var y float64 = Pi64
    fmt.Println(y)
    var z complex128 = complex128(Pi64)
    fmt.Println(z)

3.141592653589793
3.1415927
3.141592653589793
(3.141592653589793+0i)

常量字面量的不同书写,对应着不同的类型。0, 0.0, 0i,和 ‘\u0000’字面量都对应着相同的值,但是却是不同的类型,分别是无类型整数,无类型浮点数,无类型复数,无类型字符(rune),相似的,true/false对应这无类型布尔,而字符串则对应着无类型字符串

只有常量才是无类型的,当将常量赋值给有类型的变量,无类型的值通常被隐式转为变量对应的类型:

var f float64 = 3 + 0i // untyped complex -> float64
f = 2 // untyped integer -> float64
f = 1e123 // untyped floating-point -> float64
f = 'a' // untyped rune -> float64

等同于

var f float64 = float64(3 + 0i)
f = float64(2)
f = float64(1e123)
f = float64('a')

对于没有显式类型的变量声明语法(包括短变量声明),无类型的常量会被隐式转换为默认的数据类型:

  1. 无类型的整数 -> int
  2. 无类型的布尔 -> bool
  3. 无类型的浮点 -> float64
  4. 无类型的复数 -> complex128
    i := 0 // untyped integer; implicit int(0)
    r := '\000' // untyped rune; implicit rune('\000')
    f := 0.0 // untyped floating-point; implicit float64(0.0)
    c := 0i // untyped complex; implicit complex128(0i)


    fmt.Printf("%T\n", 0) // "int"
    fmt.Printf("%T\n", 0.0) // "float64"
    fmt.Printf("%T\n", 0i) // "complex128"
    fmt.Printf("%T\n", '\000') // "int32" (rune)

如果将常量赋值给不同于默认类型的数据类型,方法如下:
方法一:

    const no  = 10
    var num8 = int8(no)

方法二:

    const no  = 10
    var num16 int16 = no
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值