GO语言入门详解(二)之 基础数据类型

一、前言

        本章讲解go语言的基础数据类型,在 Go 语言中,基础数据类型(或称为基本类型)是指内置的、预定义的数据类型,开发者可以直接使用它们。这些类型是构建其他复杂数据结构和自定义类型的基础。Go语言的数值类型包括几种不同大小的整数、浮点数和复数。每种数值类型都决定了对应的大小范围和是否支持正负符号。

二、基础数据类型

2.1、整型

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

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

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

        下面是关于int、uint、uintptr的区别

1. int

  • 定义int 是一个有符号整数类型,其大小依赖于平台(32 位或 64 位)。
  • 用途:通常用于表示整数值,适合大多数计算和计数操作。
  • 范围:在 32 位系统上,int 的范围是 -2,147,483,6482,147,483,647;在 64 位系统上,范围是 -9,223,372,036,854,775,8089,223,372,036,854,775,807

2. uint

  • 定义uint 是一个无符号整数类型,大小同样依赖于平台。
  • 用途:用于表示非负整数,适合需要确保值为正的场景。
  • 范围:在 32 位系统上,uint 的范围是 04,294,967,295;在 64 位系统上,范围是 018,446,744,073,709,551,615

3. uintptr

  • 定义uintptr 是一个无符号整数类型,大小与指针的大小相同,适用于存储指针的整数表示。
  • 用途:主要用于将指针转换为整数值,以便进行某些低级操作,例如指针算术或与系统调用的交互。
  • 特点uintptr 不能安全地用作普通整数值,因为它的语义与指针相关。将指针转换为 uintptr 后,如果不谨慎,可能会导致安全问题。
func main() {
	var a int = -42
	var b uint = 42
	var c uintptr = uintptr(unsafe.Pointer(&a)) // 将指针转换为 uintptr

	fmt.Println("int:", a) //-42
	fmt.Println("uint:", b) //42
	fmt.Println("uintptr:", c) //824633999064
}

下面是Go语言中关于算术运算、逻辑运算和比较运算的二元运算符,它们按照优先级递减的顺序排列:

*      /      %      <<       >>     &       &^
+      -      |      ^
==     !=     <      <=       >      >=
&&
||

        一个算术运算的结果,不管是有符号或者是无符号的,如果需要更多的bit位才能正确表示的话,就说明计算结果是溢出了。超出的高位的bit位部分将被丢弃。如果原始的数值是有符号类型,而且最左边的bit位是1的话,那么最终结果可能是负的,例如int8的例子:

var u uint8 = 255
fmt.Println(u, u+1, u*u) // "255 0 1"

var i int8 = 127
fmt.Println(i, i+1, i*i) // "127 -128 1"

1. u + 1 的情况

  • u 的值为 255,执行 u + 1 时,计算结果为 256
  • uint8 的最大值是 255,所以 256 超出了这个范围。
  • 在无符号整数中,溢出会导致结果回绕到 0。计算过程如下:
    • 256 % 256 = 0
  • 因此,u + 1 的结果是 0

2. u * u 的情况

  • u 的值为 255,执行 u * u 时,计算结果为 255 * 255 = 65025
  • 65025 超出了 uint8 的范围(最大为 255),需要进行溢出处理。
  • 在无符号整数中,溢出同样会导致结果回绕。计算过程如下:
    • 65025 % 256 = 1(因为 256uint8 的最大值)
  • 因此,u * u 的结果是 1

2.2、浮点数 

        Go语言提供了两种精度的浮点数,float32和float64。它们的算术规范由IEEE754浮点数国际标准定义,该浮点数规范被所有现代的CPU支持。

var f float32 = 16777216 // 1 << 24
fmt.Println(f == f+1)    // "true"!
  • 这行代码检查 f 是否等于 f + 1
  • 由于浮点数的精度限制,float32 只能精确表示一定范围内的整数。当 f 的值达到 16777216 时,float32 不能再精确表示比 16777216 大的整数,尤其是 16777216 + 1
  • 具体而言,float32 在这个值之后的最小可表示增量大约是 2^24,即 16777216。因此,f + 1 的结果仍然是 16777216,因为它不能精确表示 16777217

 2.3、复数

        Go 语言支持复数类型,复数由实部和虚部组成,通常表示为 a + bi 的形式,其中 a 是实部,b 是虚部。Go 提供了内置的复数类型,支持复数的基本运算。

  • complex64:由两个 float32 类型的值表示,分别为实部和虚部。
  • complex128:由两个 float64 类型的值表示,分别为实部和虚部。
	var c1 complex64 = complex(1.0, 2.0) // 实部为 1.0,虚部为 2.0
	var c2 complex128 = complex(3.0, 4.0) // 实部为 3.0,虚部为 4.0

	fmt.Println("c1:", c1)    //c1: (1+2i)
	fmt.Println("c2:", c2)    //c2: (3+4i)

 2.4、布尔型

        一个布尔类型的值只有两种:true和false。if和for语句的条件部分都是布尔类型的值,并且==和<等比较操作也会产生布尔型的值。一元操作符!对应逻辑非操作,因此!true的值为false,更罗嗦的说法是(!true==false)==true,虽然表达方式不一样,不过我们一般会采用简洁的布尔表达式,就像用x来表示x==true

        如果需要经常做类似的转换,包装成一个函数会更方便:

// btoi returns 1 if b is true and 0 if false.
func btoi(b bool) int {
    if b {
        return 1
    }
    return 0
}

 2.5、字符串

        一个字符串是一个不可改变的字节序列。字符串可以包含任意的数据,包括byte值0,但是通常是用来包含人类可读的文本。文本字符串通常被解释为采用UTF8编码的Unicode码点(rune)序列,我们稍后会详细讨论这个问题。

s := "hello, world"
fmt.Println(len(s))     // "12"
fmt.Println(s[0], s[7]) // "104 119" ('h' and 'w')

        如果试图访问超出字符串索引范围的字节将会导致panic异常: 

c := s[len(s)] // panic: index out of range

        字符串可以通过下标去进行截取 

fmt.Println(s[0:5]) // "hello"

        不管i还是j都可能被忽略,当它们被忽略时将采用0作为开始位置,采用len(s)作为结束的位置。 

fmt.Println(s[:5]) // "hello"
fmt.Println(s[7:]) // "world"
fmt.Println(s[:])  // "hello, world"

         其中+操作符将两个字符串连接构造一个新字符串:

fmt.Println("goodbye" + s[5:]) // "goodbye, world"

         字符串的值是不可变的:一个字符串包含的字节序列永远不会被改变,当然我们也可以给一个字符串变量分配一个新字符串值。可以像下面这样将一个字符串追加到另一个字符串:

s := "left foot"
t := s
s += ", right foot"

fmt.Println(s)    //left foot, right foot
fmt.Println(t)    //left foot

 2.6、常量

         常量表达式的值在编译期计算,而不是在运行期。每种常量的潜在类型都是基础类型:boolean、string或数字。

        一个常量的声明语句定义了常量的名字,和变量的声明语法类似,常量的值不可修改,这样可以防止在运行期被意外或恶意的修改。例如,常量比变量更适合用于表达像π之类的数学常数,因为它们的值不会发生变化:

//定义一个
const pi = 3.14159 // approximately; math.Pi is a better approximation

//定义多个常量
const (
    e  = 2.71828182845904523536028747135266249775724709369995957496696763
    pi = 3.14159265358979323846264338327950288419716939937510582097494459
)

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

        如果是批量声明的常量,除了第一个外其它的常量右边的初始化表达式都可以省略,如果省略初始化表达式则表示使用前面常量的初始化表达式写法,对应的常量类型也一样的。例如:

const (
    a = 1
    b
    c = 2
    d
)

fmt.Println(a, b, c, d) // "1 1 2 2"

b

这里定义了一个常量 b,但没有显式赋值。

如果在同一个常量组中,某个常量没有显式赋值,它会使用前一个常量的值。因此,b 的值将等于 a 的值,即 1

2.6.1、iota 常量生成器

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

        下面是来自time包的例子,它首先定义了一个Weekday命名类型,然后为一周的每天定义了一个常量,从周日0开始。在其它编程语言中,这种类型一般被称为枚举类型。

type Weekday int

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

func main() {
	fmt.Println(Monday)    //  1
	fmt.Println(Tuesday)   //  2
}

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

你可能感兴趣的:(golang,开发语言,后端)