一、基础语法
1.变量定义
package main
import "fmt"
func main(){
//定义变量:var
//常量定义:const
//01-先定义变量,再赋值, var 变量名 数据类型
var name string
name = "chenran"
var age int
age = 20
fmt.Println("name",name)
fmt.Printf("name is: %s, age is:%d\n", name,age)
//02-定义时直接赋值
var gender = "man"
fmt.Println("gender:" ,gender)
//03-定义直接赋值,使用自动推导,(最常用)
address := "成都"
fmt.Println("address:", address)
//04-平行赋值
i, j := 10,20
fmt.Println("i:", i , "j:",j)
i, j = j, i
fmt.Println("i:", i , "j:",j)
}
2.基础数据类型
int, int8,int16, int32, int64
uint ...uint64
float32, float64
true/false
3.自增语法
i++, i-- ,自增语法必须单独一行
package main
import "fmt"
func main(){
i := 20
i++
fmt.Println("i:",i)
}
4.指针
package main
import "fmt"
func main(){
//使用指针时会使用垃圾回收机制(gc:garbage collector),开发人员不需要手动释放内存
//可以返回栈上的指针,编译时就确定了变量的分配位置
//编译的时候,如果发现有必要的话,就将变量分配到堆上
name := "lily"
ptr := &name
fmt.Println("name:", name)
fmt.Println("name ptr:", ptr)
//02-使用new关键字定义,提前定义一个指针,对指针赋值
name2ptr := new(string)
*name2ptr = "duck"
fmt.Println("name2:", *name2ptr)
fmt.Println("name2 ptr:", name2ptr)
//可以返回栈上的指针,编译器编译程序时,会自动判断这段代码,将city变量分配到堆上
res := testptr()
fmt.Println("res city:", *res, "address:", res)
//空指针:nil
//if两端不用加(),加{}
if res == nil{
fmt.Println("res是空,nil")
}else{
fmt.Println("res是非空")
}
}
//定义一个函数,返回一个string类型指针,go语言返回写在参数列表后面
func testptr() *string {
city := "上海"
ptr := &city
return ptr
}
5.string
package main
import "fmt"
func main(){
//01-定义
name := "duck"
//需要换行,原生输出字符串时,使用反引号``
usage := `./a.out <option>
-h help
-a xxxx`
fmt.Println("name:",name)
fmt.Println("usage:",usage)
//02-长度,访问
//string没有length方法,可以使用自由函数len()
ll := len(name)
fmt.Println("ll:",ll)
//不需要加()
for i :=0; i < len(name); i++{
fmt.Printf("i: %d, v: %c\n",i, name[i])
}
//03-拼接
i, j := "hello","world"
fmt.Println("i+j=", i+j)
//使用const修饰为常量,不能修改
const address = "beijing"
//address = ’上海‘
fmt.Println("address:", address)
}
*6 数组和切片(Go 语言切片面试真题 8 连问)
1、数组和切片的区别
- a.数组是固定长度,不能动态扩容,在编译期就确定大小。
- b.切片是对数组的抽象(动态数组),切片长度不固定,可以追加元素。切片不是数组,切片描述的是一块数组。切片可以使用append追加元素,当cap不足时动态扩容。
2、拷贝大切片代价比拷贝小切片代价大吗?
不会。
切片本质内部结构如下:如果发生拷贝,本质上就是拷贝下面的三个字断。type SliceHeader struct { Data uintptr Len int Cap int }
3、深拷贝和浅拷贝
本质区别:复制出来的对象与原对象是否会指向同一地址切片拷贝三种方式:
- a.使用“=”拷贝,浅拷贝
- b.使用“[:]”下标的方式复制切片,浅拷贝
- c.使用内置函数copy()进行切片拷贝,深拷贝
4、零切片、空切片、nil切片是什么
- 零切片:切片内部数组都是零值或者底层数组的内容全是nil的切片;使用make创建的、长度、容量都不为零的切片就是零切片。
- nil切片:nil切片的长度和容量都为0,且和nil比较的结果都为true;采用直接创建切片的方式、new创建切片的方式都可以创建nil切片。
- 空切片:空切片的长度和容量都是0,但是和nil的比较结果是false,因为所有空切片的数据指针都是指向同一个地址;使用字面量、make可以创建空切片。
5、切片的扩容策略
切片在扩容时会进行内容对齐,这个和内存分配策略有关。当原slice容量小于1024时,新slice容量变为原来的2倍;当原slice容量超过1024时,新slice容量变成原来的1.25倍。6、参数传递切片和切片指针有什么区别?
当切片作为参数传递时,其实就是一个结构体的传递,因为Go语言参数传递只有值传递,传递一个切片就会浅拷贝原切片,但因为底层数据的地址没有改变,所以在函数内对切片的修改,也会影响到函数外的切片。
例外:如果指向底层数组的指针被覆盖或者修改(copy、重分配、append触发扩容),此时函数内部对数据的修改将不再影响到外部的切片,代表长度的len和容量cap也均不会被修改。因为函数外的切片已经指向了一个新的底层数组。func modifySlice(s []string) { s[0] = "song" s[1] = "Golang" fmt.Println("out slice: ", s) } func main() { s := []string{"asong", "Golang梦工厂"} modifySlice(s) fmt.Println("inner slice: ", s) } // 运行结果 out slice: [song Golang] inner slice: [song Golang] //特例 func appendSlice(s []string) { s = append(s, "快关注!!") fmt.Println("out slice: ", s) } func main() { s := []string{"asong", "Golang梦工厂"} appendSlice(s) fmt.Println("inner slice: ", s) } // 运行结果 out slice: [asong Golang梦工厂 快关注!!] inner slice: [asong Golang梦工厂]
7、range遍历切片有什么要注意
range关键字用于for循环中迭代数组(array)、切片(slice)、通道(channel)、集合(map)的元素。两种使用方式:
使用range遍历切片时会先拷贝一份,然后再遍历拷贝数据。
面试官:go中for-range使用过吗?这几个问题你能解释一下原因吗?for k,v := range _ { } //遍历下标和对应值。 for k := range _ { } //只遍历下标
6.定长数组
package main
import "fmt"
func main(){
//1-定义一个具有10个数字的数组
//nums := [10]int{1,2,3,4}
//var nums = [10]int{1,2,3,4}
nums := [10]int{1,2,3,4,5}
//2-遍历
for i :=0; i<len(nums); i++{
fmt.Println("i:", i, "j:", nums[i])
}
//方式二:for range ===>python支持
//value全程是一个临时变量,不断重新赋值,修改它不会修改原始数组
for key,value := range nums{
fmt.Println("key:",key, "value:",value)
}
//想忽略一个值,可以使用_
//如果想忽略两个,不能使用:=,使用=, 有:表示两边有新东西
for _, value := range nums{
fmt.Println("忽略key,value:",value)
}
//不定长数组
//3-使用make创建数组
}
7.不定长数组(切片,slice)
切片1
package main
import "fmt"
func main(){
//切片:slice,它的底层是数组,可以动态改变长度
//定义一个切片,包含多个地名
//names := []string{"北京","上海","广州","深圳"}
names := []string{"北京","上海","广州","深圳"}
for i ,v := range names{
fmt.Println("i:",i, "v:",v)
}
//1.追加数据
fmt.Println("追加元素之前,names的长度:",len(names),",容量:",cap(names))
names = append(names,"海南")
fmt.Println("names追加元素后赋值给自己:",names)
fmt.Println("追加元素之后,names的长度:",len(names),",容量:",cap(names)