go语言怎么 控制一个变量输入的范围_四周入门Go语言(week01)

  • Post author:
  • Post category:其他


f21f8685b2a5ab6fa6bcc62921389293.png

【零】Go语言特点与输入输出

「特点」

1. 风格统一:同一件事只有一种实现方法,不同的人写出的代码风格应一致。例如:定义的变量必须要使用,if语句括号位置固定,只有for循环,fmt包能自动对齐代码

2. 易上手:简单易用是设计者的初衷

3. 编译型语言:直接编译成二进制码(Python等解释型语言编译成字节码后,需要通过解释器变成二进制码),因此更快

4. 从底层原生支持并发,从而能显著提高并发效率

5. 应用前景:目前字节跳动、b站、知乎均在较大规模的使用,腾讯新业务开始使用go

下载网址:https://golang.org/dl/

编辑器:

  • VSCode:https://code.visualstudio.com/Download

  • Goland:https://www.jetbrains.com/go/download

f5a7f5bf752d8eecf0347b939c31d9ab.png

本人使用Goland,但VSCode更轻

「输入输出」

基于fmt包提供的接口来实现,使用方式同c语言的scanf和printf

  • 常用占位符:

%d

整型(十进制)

%t

布尔型

%f

浮点型、复数

%T

值的类型

%c

字符型

%v

结构体的值(%+v:字段名+值)

%s

字符串

%b

整型(二进制)

关于 %v 与 %+v的区别:

type Human struct {    Name string}var people = Human{Name:"zhangsan"} fmt.Printf("%v", people)   //输出 :zhangsanfmt.Printf("%+v", people)   //输出 :name:zhangsan
  • 转义字符:以反斜杆 \  起始,常用的有 \r 回车,\n换行

【一】数据类型与变量

数据类型

  • 整型:按长度有int8、int16、int32、int64,数字代表其所占的位长度(如C++中的int是4个字节,对于此处即int32),对应的无符号整型:uint8、uint16、uint32、uint64。有时会简写为int/uint,此时32位操作系统上就是int32/uint32,64位操作系统上就是int64/uint64

  1. 浮点型:float32和float64

  2. 复数型:complex64和complex128,例如

var c1 complex64 = 1 + 2i
  1. 布尔型:bool

  2. 字符:包含byte、rune两种

    1. byte:代表了ASCII码的一个字符,大小为一个字节

    2. rune:代表一个 UTF-8字符,如一个中文字符为多个字节,需要用rune类型,其大小为四个字节

  3. 字符串:string,底层为byte数组,不可修改

    • 若修改:需要转换为切片byte[]或rune[],修改完成后转回string。如:

s := "big"// 强制转换S := []byte(s)S[0] = 'p'fmt.Println(string(S))



变量

  • 变量声明

var 变量名 变量类型

支持批量声明

var (    a string    b int    c bool    d float32)
  • 变量初始化

未初始化的变量,均取对应数据类型的默认值,如整型,浮点型为0,字符串为空字符串“”,布尔型为false,切片、函数、指针变量默认为nil

var 变量名 类型 = 表达式

如:

var name string = "xiaomin"var age int = 18

Go支持类型推导,上述可简化为

var name = "xiaomin"var age  = 18

多个变量可一次性初始化:

var name, age = "xiaomin", 18

在函数内部,可以使用短声明:

func(){        name, age := "xiaomin", 18}
  • 匿名变量:由于go支持一个表达式为多个变量同时赋值,如函数的返回值可以是多个。由于go中定义的变量必须要使用,当我们只需要使用部分返回值时,可以利用将不需要的值赋给匿名变量_。匿名变量不占用命名空间,不会分配内存,仅占位使用

func cpt() (x int, y int ) {        return a+b, a-b}func main() {        sum, _ := cpt() }

【二】控制结构

分支结构

if 表达式1 {  //这个括号必须放在if一行    分支1} else if 表达式2 { // else等必须在上个分支结尾括号一行    分支2} else{    分支3}

循环结构

for 初始语句;条件表达式;结束语句{    循环体语句}

另外有键值循环:

使用for range遍历数组、切片、字符串、map 及通道(channel)。通过for range遍历的返回值有以下规律:

  1. 数组、切片、字符串返回索引和值

  2. map返回键和值

  3. 通道(channel)只返回通道内的值

例如:下面的代码为检查data中首个value为空的key

func check(data map[string]string) string {    for k, v := range data {      if v == "" {         return k      }   }   return nil}

【三】线性数据结构

数 组

  • 定义

var 数组变量名 [数组长度]数据类型
  • 初始化

var id [3]int //默认全零var name = [3]string{"a","b","c"} //定义同时初始化var score = [...]int{1,2,3} //自动推断长度weight = [...]int{1:150, 4:200} //对特定索引赋值,得到的数组为:[0,150,0,0,200]
  • 遍历:使用range能有效避免索引越界

Data := [...]int{1,2,3} for i:=0; i<len(Data); i++{    fmt.println(Data[i])}for i,v := range Data{    fmt.println(v)}
  • 二维数组:采用数组嵌套的定义方式

data := [3][2]string {   {"a","b"},   {"c","d"},   {"e","f"},}for _,v1 := range data{   for _,v2 := range v1 {      fmt.Printf("%s\t",v2)   }   fmt.Println()}

切 片

  • 本质

切片本质是对数组的引用,记录起始指针,len(元素个数)表示相对起始指针的偏移量,cap(容量)表示偏移量的上限值。这句话有如下内涵:

    • 引用意味着切片并不复制元素,只记录起始指针+偏移量+最大偏移量

    • 每个切片都有一个底层数组,它在初始化切片的时候创建。不同切片可能共用一个底层数组,对数组的修改,会同步到所有引用该数组的切片

  • 定义方式

    • 直接定义

var 切片名 []数据类型

如:

var s1 = []int    //定义一个切片,此时为nilvar s2 = []int{} //定义并初始化,此时虽没有元素,但不为nil,因为内存中已经创建一个对应的数组

因此,不能根据是否等于nil来判断切片是否为空,应使用len查看当前元素长度是否为0

    • 基于数组的定义

a := [8]int{0,1,2,3,4,5,6,7}b := a[1:7]   fmt.Println(b)// 结果为 [1 2 3 4 5 6]

即切取a数组下标 [1 , 7) 部分的元素【注意:左闭右开】,当索引边界未指定时,左边界默认为0,右边界默认为len(a)

    • 切片的切片

a := [...]string{"甲", "乙", "丙", "丁", "春", "夏","秋","冬"}fmt.Printf("a:%v  len:%d  cap:%d\n", a, len(a), cap(a))b := a[1:3]fmt.Printf("b:%v  len:%d  cap:%d\n", b, len(b), cap(b))c := b[0:5]fmt.Printf("c:%v  len:%d  cap:%d\n", c, len(c), cap(c))

输出为:

a:[甲 乙 丙 丁 春 夏 秋 冬]  len:8  cap:8 b:[乙 丙]  len:2  cap:7c:[乙 丙 丁 春 夏]  len:5  cap:7

原因:切片本质是对底层数组对引用,因此,切取 [L , R)范围内元素时,len = R-L,cap 取数组a[L]至其末尾的总个数。切片再切片时,c从b[1]对应的数组位置(这是切片b的唯一作用)开始切取len个元素,切到了b没有切到的元素。但要注意,索引不能超过原数组的长度,否则会因索引越界造成程序panic。

b26922dd2bffed659030a4a35b501962.png

切片再切片的底层逻辑

    • make()函数定义

make([]数据类型, len, cap) 
  • 追加:append()函数

新切片 = append(原切片,追加元素)

追加的本质是,利用切片的指针信息计算出目标位置,然后将其赋值为追加的元素值。有三点要注意:

  1. 追加元素时,若目标位置没有超过数组范围,则会覆盖对应位置上原来的值

23a67acb792722ac5a181a96d08085bd.png

追加未超过原数组长度

  1. 追加元素时,若底层数组容量不够,数组将进行二倍扩容(当数组规模较大时,每次1.25扩容),注意是将cap*2,重新开辟一块数组,将原来无需改变的复制过来,将追加的元素放到后面

e2a026c1a51a4009f7d5abbb55169906.png

追加超过原数组长度

  1. 追加的元素可以是:一个,多个,切片

var citySlice []stringcitySlice = append(citySlice, "北京")// 追加一个元素citySlice = append(citySlice, "上海", "广州", "深圳")// 追加多个元素a := []string{"成都", "重庆"} // 追加切片citySlice = append(citySlice, a...)fmt.Println(citySlice) //[北京 上海 广州 深圳 成都 重庆]
  • 复制:copy()函数

由于切片为引用,共用同一个底层数组会互相影响。可以使用copy函数,将在内存中复制一个相同的数组,从而避免互相影响

copy(新切片, 原切片)


未完待续…


如有帮助,请点在看!


往期文章:

【字节跳动】日常/暑期实习招人啦(可转正)


情人节,用代码比个心


2019年offer待遇信息汇总——互联网篇


我的互联网转行之路

ffeb54d847f8f62188eac854d4e7d572.png

欢迎关注听涛记