golang学习随便记2-原始数据类型

原始数据类型

整数

有符号整数int8、int16、int32、int64,无符号整数uint8、uint16、uint32、uint64,还有平台依赖的 int 和 uint (可能是32位,也可能是64位),使用平台依赖的整数的理由是效率最高(因为等于机器字长)。

rune 表示 Unicode码点(code point),其实就是 int32. 而 byte 就是 uint8.

还有大小不明确的无符号整数 uintptr,用来底层编程和C程序库交互,差不多就是 void *

golang的位运算和C略有不同,golang没有取反运算符~,而是用异或运算符^作为单元运算符表示取反(可以理解为 ^0b10100101 = 0b11111111^0b10100101),另外,golang多一个位清空运算符(AND NOT)&^,^和&^的差别是,对于a^b和a&^b,前者当b中的位为1时,将a中对应位取反,后者将a中对应位取0,对于b中位为0,两者相同(即不变)

	var a, b uint8
	a, b = 0b10101101, 0b00001010
	fmt.Printf("%08b\n", a^b)
	fmt.Printf("%08b\n", a&^b)
	fmt.Printf("%08b\n", ^a)
	fmt.Printf("%08b\n", ^b)

输出

10100111
10100101
01010010
11110101

无符号整数通常只会用于特定领域或场合(如位运算、加密解密、压缩等),对于字符串长度、数组长度等,通常都是用有符号整数,因为对于无符号整数 i,执行 i-- 操作是不可能让 i >= 0 false的,这在循环中会带来严重错误

浮点

float32、float64,float32的有效数字大约6位(例如 float32(1 << 24) == float32(1 << 24 +1)),所以绝大多数情况应该用float64 (有效数字15位)

复数

和C语言不同,golang内在支持复数:complex64 和 complex128,分别由 float32 和 float64 构成。构造一个复数需要用内置函数 complex(x, y),内置函数 real(z)imag(z)则提取复数z的实部和虚部,对于字面复数值,也可以直接自然写 1+2i 3-4i 这样的值。math/cmplx包提供了各种复数运算。

布尔型

布尔值为 true 或 false,golang并不能自动把true和false隐式转换成1和0,也不能显式强制转换,确实需要时,只能编写函数  (以下代码,注释掉方式的编译通不过)

package main

func main() {
	x := true
	k := 1
	//n := int(x)
	n := btoi(x)
	//y := bool(k)
	y := itob(k)
	println(n)
	println(y)
}

func btoi(b bool) int {
	if b {
		return 1
	}
	return 0
}

func itob(i int) bool { return i != 0 }

字符串

golang字符串和C风格SZ字符串不同,golang字符串就是不可变的字节序列,它是可以包含0值字节的(更像包含长度信息的Pascal字符串)!

通常,golang文本字符串被解读成按UTF-8编码的Unicode码点(一个码点对应一个文字符号)序列,内置 len(s) 函数返回的是字节数而不是文字符号数目,下标访问操作 s[i] 则取得第 i 个字符(对于非ASCII字符,第i个字符并非第i个字节)

golang字符串是支持切片操作的,s[i:j] 表示 [i, j) 下标范围字符构成的子串,起始位置或终止位置不指定时,默认对应0 和 len(s),例如 s[:5]、s[7:]、s[:]。支持切片可以节省内存。

golang字符串用 + 实现串接操作,用 == < 等实现比较操作(按字节比较,大小按字典序),所以,golang需要类似strlen()的len(),却不需要strcat()、strcmp()那样的函数,用C++理解就是内在已经帮你重载好了运算符 + == < 之类。

C风格SZ字符串仅仅是字符数组,里面的字符是可以修改的(mutable),而golang字符串是无法改变的(immutable)。

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

上面的操作中,并非字符串 left foot 被修改了,而是字符串left foot 和 , right foot 通过 + 操作串接产生一个新的字符串,并将新字符串赋值给s,当然,新的字符串和原来的字符串是可以部分共用底层内存的(t和s复用前面这些字节的内存)。golang字符串的特性使得字符串复制和切片的开销非常小。

golang源码总是utf-8编码,golang字符串也按UTF-8解读,我们可以直接将Unicode码点写入字符串字面量,如 "Hello, 世界",还可以用转义符 \ 将转义序列写入字符串字面量,\n 、\t、 \"和\\都和一般理解相同,\xhh 和 \ooo 形式可以插入数值指定的Unicode码点。如果希望非转义的字符串字面量,可以用反引号。使用反引号还可以将字符串写成多行(唯一的处理是回车被删除,换行则保留,从而所有平台都一致)。反引号字符串最常见的场合是正则表达式和多行文本(提示信息、HTML模板、JSON等)

golang中,一个Unicode码点,就是一个int32值,称之为 rune,即 rune 为 int32 别名。

UTF-8实现了对Unicode字符的变长编码:

0xxxxxxx  兼容 ASCII,1比特开始的必然不是 ASCII 字符,用 10xxxxxx 字节表示一种后续字节,而 110xxxxx 、1110xxxx、11110xxx 字节表示起始字节,分别对应后面还有1个字节(共2个字节)、后面还有2个字节(共3个字节)、后面还有3个字节(共4个字节)。这些字节区分用了前缀编码方式。

各种复杂的字符串(字符):

"世界"
"\xe4\xb8\x96\xe7\x95\x8c"
"\u4e16\u754c"
"\U00004e16\U0000754c"
'世'   '\u4e16'  '\U00004e16'

利用golang字符串切片操作、字符串比较运算符==和UTF-8特性,判断一个字符串是否是另一个的子串非常方便:

package main

func main() {
	s1 := "abcdefgh"
	s2 := "cde"
	println(Contains(s1, s2)) // true
}

func HasPrefix(s, prefix string) bool {
	return len(s) >= len(prefix) && s[:len(prefix)] == prefix
}

func Contains(s, substr string) bool {
	for i := 0; i < len(s); i++ {
		if HasPrefix(s[i:], substr) {
			return true
		}
	}
	return false
}

码点和字符例子

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
	s := "Hello, 世界"
	fmt.Println(len(s))                    // 13 个字节
	fmt.Println(utf8.RuneCountInString(s)) // 9 个字符(码点)
	println()
	for i := 0; i < len(s); {
		r, size := utf8.DecodeRuneInString(s[i:])
		fmt.Printf("%d\t%c\t%q\t%x\n", i, r, r, r)
		i += size
	}
	println()
	for i, r := range "Hello, 世界" {
		fmt.Printf("%d\t%c\t%q\t%x\n", i, r, r, r)
	}
}

上面程序后面两段功能一样,码点是可以被遍历的,所以输出为:

13
9

0       H       'H'     48
1       e       'e'     65
2       l       'l'     6c
3       l       'l'     6c
4       o       'o'     6f
5       ,       ','     2c
6               ' '     20
7       世      '世'    4e16
10      界      '界'    754c

0       H       'H'     48
1       e       'e'     65
2       l       'l'     6c
3       l       'l'     6c
4       o       'o'     6f
5       ,       ','     2c
6               ' '     20
7       世      '世'    4e16
10      界      '界'    754c

注意:'世'的码点为4e16,即 0100 1110 0001 0110,用UTF-8存放时用了3个字节,从而起始半字节为 1110,即“模板”为1110 xxxx 10xx xxxx 10xx xxxx,将对应比特填入,即得 1110 0100 1011 1000 1001 0110

字符串和字节slice可以显式转换,转换时会产生副本

s := "abc"
b := []byte(s)
s2 := string(b)

字符串和数字相互转换可以直接使用strconv包

常量

常量声明是直观的。C语言中的枚举类型可以用 iota 常量生成器实现:iota从0开始

type Weekday int

const (
  Sunday Weekday = iota  // 0
  Monday
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday              // 6
)
type Flags uint

const (
  FlagUp Flags = 1 << iota   // 1 << 0
  FlagBroadcast              // 1 << 1
  FlagLoopback
  FlagPointToPoint
  FlagMulticast              // 16
)
const (
  _ = 1 << (10 * iota)
  KiB    // 1024
  MiB    // 1024*1024
  GiB
  TiB
  PiB
  EiB
  ZiB
  YiB
)

golang 常量可以是无类型的或者叫字面的(例如上面最后一个,没有一个整型能放下后面那种大数,但 YiB/ZiB 这样的操作的确可以进行)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值