Loading...
Go指针的使用 package main
import "fmt"
func exchange(c, d *int) { //t := *c //*c = *d //*d = t fmt.Printf("%p,%p \n", c, d) //0xc0000a6058,0xc0000a6070 fmt.Printf("%d,%d \n", *c, *d) //1,2 c, d = d, c fmt.Printf("%p,%p \n", c, d) // 0xc0000a6070,0xc0000a6058 fmt.Printf("%d,%d \n", *c, *d) // 2,1 }
func main() {
a, b := 1, 2 exchange(&a, &b) fmt.Println(a, b) //1,2
}
c和d变量值确实被交换了,但是c和d关联的两个变量并没有实际关联,所以print a和b的值依然是1和2
结构体标签(Tag) Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。
Tag在结构体字段的后方定义,由一对反引号包裹起来,具体的格式如下:
`key1:"value1" key2:"value2"` 结构体标签由一个或多个键值对组成,键与值使用冒号分隔,值用双引号括起来,键值对之间使用一个空格分隔。
注意事项: 为结构体编写Tag时,必须严格遵守键值对的规则。结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。
例如不要在key和value之间添加空格。
例如我们为Student结构体的每个字段定义json序列化时使用的Tag:
//Student 学生 type Student struct { ID int `json:"id"` //通过指定tag实现json序列化该字段时的key Gender string //json序列化是默认使用字段名作为key name string //私有不能被json包访问 }
func main() { s1 := Student{ ID: 1, Gender: "女", name: "pprof", } data, err := json.Marshal(s1) if err != nil { fmt.Println("json marshal failed!") return } fmt.Printf("json str:%s\n", data) //json str:{"id":1,"Gender":"女"} }
结构体的匿名字段 结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就称为匿名字段。
//Person 结构体Person类型 type Person struct { string int }
func main() { p1 := Person{ "pprof.cn", 18, } fmt.Printf("%v\n", p1) //输出结果:main.Person{string:"pprof.cn", int:18} fmt.Println(p1.string, p1.int) //pprof.cn 18 } 匿名字段默认采用类型名作为字段名,结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。
构造函数 Go语言的结构体没有构造函数,我们可以自己实现。
例如,下方的代码就实现了一个person的构造函数。
因为struct是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以该构造函数返回的是结构体指针类型。
func newPerson(name, city string, age int8) *person { return &person{ name: name, city: city, age: age, } } 调用构造函数
p9 := newPerson("pprof.cn", "测试", 90) fmt.Printf("%v\n", p9)
只有当结构体实例化时,才会真正地分配内存,也就是必须实例化后才能使用结构体的字段。
结构体本身也是一种类型,我们可以像声明内置类型一样使用var关键字声明结构体类型。
var 结构体实例 结构体类型 实例 type person struct { name string city string age int8 }
func main() { var p1 person p1.name = "pprof.cn" p1.city = "北京" p1.age = 18 //p1={pprof.cn 北京 18} fmt.Printf("p1=%v\n", p1)
//p1=main.person{name:"pprof.cn", city:"北京", age:18} fmt.Printf("p1=%v\n", p1) } 我们通过.来访问结构体的字段(成员变量),例如p1.name和p1.age等。
使用键值对初始化 使用键值对对结构体进行初始化时,键对应结构体的字段,值对应该字段的初始值。
p5 := person{ name: "pprof.cn", city: "北京", age: 18, } fmt.Printf("p5=%v\n", p5) //输出结果:p5=main.person{name:"pprof.cn", city:"北京", age:18} 也可以对结构体指针进行键值对初始化,例如:
p6 := &person{ name: "pprof.cn", city: "北京", age: 18, } fmt.Printf("p6=%v\n", p6) //输出结果:p6=&main.person{name:"pprof.cn", city:"北京", age:18} 当某些字段没有初始值的时候,该字段可以不写。此时,没有指定初始值的字段的值就是该字段类型的零值。
p7 := &person{ city: "北京", } fmt.Printf("p7=%v\n", p7) //输出结果:p7=&main.person{name:"", city:"北京", age:0}
匿名结构体 在定义一些临时数据结构等场景下还可以使用匿名结构体。
package main
import ( "fmt" )
func main() { //声明了一个匿名结构体 普通声明:type structName struct{} var user struct{Name string; Age int} user.Name = "pprof.cn" user.Age = 18 fmt.Printf("%v\n", user) }
Go语言
今天踩了一个坑,不知道什么原因. 源码丢到Centos服务器上返回的数据无论如何都是个看起来像未编码的utf-8,可是不管你怎么给他设定,或者你查看默认编码,他都是utf8,但是返回的就是没编码前的乱码状态.这是第一次使用httpx踩到的大坑,具体不知道什么原因,记录一下
与 requests不同,HTTPX 在默认情况下不遵循重定向。
我们在这里的行为不同,因为自动重定向可以很容易地掩盖不必要的网络调用。
您仍然可以启用行为以自动跟踪重定向,但您需要明确执行此操作...
response = client.get(url, follow_redirects=True) 或者实例化客户端,默认情况下启用重定向跟随...
client = httpx.Client(follow_redirects=True)
HTTPX 必须为每个请求建立新连接(不会重用连接)。随着对主机的请求数量的增加,这很快就会变得低效。
另一方面,Client实例使用 HTTP 连接池。这意味着,当您向同一主机发出多个请求时,Client将重用底层 TCP 连接,而不是为每个请求重新创建一个连接。
与使用顶级 API 相比,这可以带来显著的性能改进,包括:
减少跨请求的延迟(无握手)。 降低了 CPU 使用率和往返行程。 减少网络拥塞。
Brotli是开源的一种新型压缩算法,Brotli压缩比智能压缩性能更好。开启Brotli压缩功能后,CDN节点会对资源进行智能压缩后返回,缩小传输文件大小,提升文件传输效率,减少带宽消耗。
背景信息 压缩分为Gzip压缩和Brotli压缩,智能压缩功能主要针对Gzip压缩,智能压缩详情请参见Gzip压缩。 当源站文件的大小超过1 KB时,您可以使用智能压缩或Brotli压缩来压缩文件(即1 KB以下的文件不做压缩)。 Brotli压缩支持的文件类型有text/xml、text/plain、text/css、application/javascript、application/x-javascript、application/rss+xml、text/javascript、image/tiff、image/svg+xml、application/json、application/xml。 服务端响应携带响应头Content-Encoding: br:服务端响应的内容是经过Brotli压缩后的资源。 客户端请求携带请求头Accept-Encoding: br:客户端希望获取对应资源时进行Brotli压缩。 注意事项 CDN对静态文件进行压缩时,会改变文件的MD5值,如果源站文件配置了MD5校验机制,请关闭智能压缩功能。 源站开启了压缩功能,且服务端响应中携带了content_encoding,则CDN的压缩功能将不再生效。 同时开启Brotli压缩和智能压缩,且客户端请求头Accept-Encoding同时携带br和gzip时,仅Brotli压缩生效。 如果您同时开启了页面优化和压缩功能(智能压缩或者Brotli压缩),页面优化功能将会失效,CDN只会对文件进行压缩。 Brotli压缩只兼容部分浏览器,您可以根据业务需要查询浏览器的兼容情况。 常见的图片文件类型(PNG、JPG、JPEG等)和视频文件类型(MP4、AVI、WMV等)已经做了内容的压缩处理,开启智能压缩或者Brotli压缩没有效果,建议您关闭压缩功能。如果您需要进一步减小图片文件的体积可以使用图像处理功能,如果您需要进一步减小视频文件的体积可以使用视频转码功能。“图像处理”和“视频转码”都会影响文件清晰度。
众所周知,Splash并不支持Windows,而Playwright并不支持Centos,很明显它们不能共存,至少它们不能完美的共存.
注意在 Windows 上不支持 mitmproxy 的控制台接口,但是可以使用 mitmdump 和 mitmweb。
指针地址和指针类型 每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行“取地址”操作。 Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:*int、*int64、*string等。
取变量指针的语法如下:
ptr := &v // v的类型为T 其中:
v:代表被取地址的变量,类型为T ptr:用于接收地址的变量,ptr的类型就为*T,称做T的指针类型。*代表指针。 举个例子:
func main() { a := 10 b := &a // a:10 ptr:0xc00001a078 fmt.Printf("a:%d ptr:%p\n", a, &a) // b:0xc00001a078 type:*int fmt.Printf("b:%p type:%T\n", b, b) // 0xc00000e018 fmt.Println(&b) }
Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。
传递数据使用指针,而无须拷贝数据。类型指针不能进行偏移和运算。
Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)。
golang slice data[:6:8] 两个冒号的理解。
常规slice , data[6:8],从第6位到第8位(返回6, 7),长度len为2, 最大可扩充长度cap为4(6-9)。
另一种写法: data[:6:8] 每个数字前都有个冒号, slice内容为data从0到第6位,长度len为6,最大扩充项cap设置为8。
a[x:y:z] 切片内容 [x:y] 切片长度: y-x 切片容量:z-x。
string本身是不可变的,因此要改变string中字符。需要如下操作: 英文字符串: package main
func main() { str := "Hello world" //中文字符需要用[]rune(str) s := []byte(str) s[6] = 'G' s = s[:8] s = append(s, '!') str = string(s) fmt.Println(str) } 输出结果: Hello Go!
func main() { str := "你好,世界!hello world!" s := []rune(str) s[3] = '够' s[4] = '浪' s[12] = 'g' s = s[:14] str = string(s) fmt.Println(str) } 输出结果:你好,够浪!hello go
你好,够浪!hello go
GO语言 内置函数 len 和 cap 都返回数组长度 (元素数量)。
func main() { a := [2]int{} println(len(a), cap(a)) }
输出结果:
2 2
21 条评论
Go指针的使用
package main
import "fmt"
func exchange(c, d *int) {
//t := *c
//*c = *d
//*d = t
fmt.Printf("%p,%p \n", c, d) //0xc0000a6058,0xc0000a6070
fmt.Printf("%d,%d \n", *c, *d) //1,2
c, d = d, c
fmt.Printf("%p,%p \n", c, d) // 0xc0000a6070,0xc0000a6058
fmt.Printf("%d,%d \n", *c, *d) // 2,1
}
func main() {
a, b := 1, 2
exchange(&a, &b)
fmt.Println(a, b) //1,2
}
c和d变量值确实被交换了,但是c和d关联的两个变量并没有实际关联,所以print a和b的值依然是1和2
结构体标签(Tag)
Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。
Tag在结构体字段的后方定义,由一对反引号包裹起来,具体的格式如下:
`key1:"value1" key2:"value2"`
结构体标签由一个或多个键值对组成,键与值使用冒号分隔,值用双引号括起来,键值对之间使用一个空格分隔。
注意事项: 为结构体编写Tag时,必须严格遵守键值对的规则。结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。
例如不要在key和value之间添加空格。
例如我们为Student结构体的每个字段定义json序列化时使用的Tag:
//Student 学生
type Student struct {
ID int `json:"id"` //通过指定tag实现json序列化该字段时的key
Gender string //json序列化是默认使用字段名作为key
name string //私有不能被json包访问
}
func main() {
s1 := Student{
ID: 1,
Gender: "女",
name: "pprof",
}
data, err := json.Marshal(s1)
if err != nil {
fmt.Println("json marshal failed!")
return
}
fmt.Printf("json str:%s\n", data) //json str:{"id":1,"Gender":"女"}
}
结构体的匿名字段
结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就称为匿名字段。
//Person 结构体Person类型
type Person struct {
string
int
}
func main() {
p1 := Person{
"pprof.cn",
18,
}
fmt.Printf("%v\n", p1)
//输出结果:main.Person{string:"pprof.cn", int:18}
fmt.Println(p1.string, p1.int) //pprof.cn 18
}
匿名字段默认采用类型名作为字段名,结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。
构造函数
Go语言的结构体没有构造函数,我们可以自己实现。
例如,下方的代码就实现了一个person的构造函数。
因为struct是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以该构造函数返回的是结构体指针类型。
func newPerson(name, city string, age int8) *person {
return &person{
name: name,
city: city,
age: age,
}
}
调用构造函数
p9 := newPerson("pprof.cn", "测试", 90)
fmt.Printf("%v\n", p9)
只有当结构体实例化时,才会真正地分配内存,也就是必须实例化后才能使用结构体的字段。
结构体本身也是一种类型,我们可以像声明内置类型一样使用var关键字声明结构体类型。
var 结构体实例 结构体类型
实例
type person struct {
name string
city string
age int8
}
func main() {
var p1 person
p1.name = "pprof.cn"
p1.city = "北京"
p1.age = 18
//p1={pprof.cn 北京 18}
fmt.Printf("p1=%v\n", p1)
//p1=main.person{name:"pprof.cn", city:"北京", age:18}
fmt.Printf("p1=%v\n", p1)
}
我们通过.来访问结构体的字段(成员变量),例如p1.name和p1.age等。
使用键值对初始化
使用键值对对结构体进行初始化时,键对应结构体的字段,值对应该字段的初始值。
p5 := person{
name: "pprof.cn",
city: "北京",
age: 18,
}
fmt.Printf("p5=%v\n", p5)
//输出结果:p5=main.person{name:"pprof.cn", city:"北京", age:18}
也可以对结构体指针进行键值对初始化,例如:
p6 := &person{
name: "pprof.cn",
city: "北京",
age: 18,
}
fmt.Printf("p6=%v\n", p6)
//输出结果:p6=&main.person{name:"pprof.cn", city:"北京", age:18}
当某些字段没有初始值的时候,该字段可以不写。此时,没有指定初始值的字段的值就是该字段类型的零值。
p7 := &person{
city: "北京",
}
fmt.Printf("p7=%v\n", p7)
//输出结果:p7=&main.person{name:"", city:"北京", age:0}
匿名结构体
在定义一些临时数据结构等场景下还可以使用匿名结构体。
package main
import (
"fmt"
)
func main() {
//声明了一个匿名结构体 普通声明:type structName struct{}
var user struct{Name string; Age int}
user.Name = "pprof.cn"
user.Age = 18
fmt.Printf("%v\n", user)
}
Go语言
今天踩了一个坑,不知道什么原因. 源码丢到Centos服务器上返回的数据无论如何都是个看起来像未编码的utf-8,可是不管你怎么给他设定,或者你查看默认编码,他都是utf8,但是返回的就是没编码前的乱码状态.这是第一次使用httpx踩到的大坑,具体不知道什么原因,记录一下
与 requests不同,HTTPX 在默认情况下不遵循重定向。
我们在这里的行为不同,因为自动重定向可以很容易地掩盖不必要的网络调用。
您仍然可以启用行为以自动跟踪重定向,但您需要明确执行此操作...
response = client.get(url, follow_redirects=True)
或者实例化客户端,默认情况下启用重定向跟随...
client = httpx.Client(follow_redirects=True)
HTTPX 必须为每个请求建立新连接(不会重用连接)。随着对主机的请求数量的增加,这很快就会变得低效。
另一方面,Client实例使用 HTTP 连接池。这意味着,当您向同一主机发出多个请求时,Client将重用底层 TCP 连接,而不是为每个请求重新创建一个连接。
与使用顶级 API 相比,这可以带来显著的性能改进,包括:
减少跨请求的延迟(无握手)。
降低了 CPU 使用率和往返行程。
减少网络拥塞。
Brotli是开源的一种新型压缩算法,Brotli压缩比智能压缩性能更好。开启Brotli压缩功能后,CDN节点会对资源进行智能压缩后返回,缩小传输文件大小,提升文件传输效率,减少带宽消耗。
背景信息
压缩分为Gzip压缩和Brotli压缩,智能压缩功能主要针对Gzip压缩,智能压缩详情请参见Gzip压缩。
当源站文件的大小超过1 KB时,您可以使用智能压缩或Brotli压缩来压缩文件(即1 KB以下的文件不做压缩)。
Brotli压缩支持的文件类型有text/xml、text/plain、text/css、application/javascript、application/x-javascript、application/rss+xml、text/javascript、image/tiff、image/svg+xml、application/json、application/xml。
服务端响应携带响应头Content-Encoding: br:服务端响应的内容是经过Brotli压缩后的资源。
客户端请求携带请求头Accept-Encoding: br:客户端希望获取对应资源时进行Brotli压缩。
注意事项
CDN对静态文件进行压缩时,会改变文件的MD5值,如果源站文件配置了MD5校验机制,请关闭智能压缩功能。
源站开启了压缩功能,且服务端响应中携带了content_encoding,则CDN的压缩功能将不再生效。
同时开启Brotli压缩和智能压缩,且客户端请求头Accept-Encoding同时携带br和gzip时,仅Brotli压缩生效。
如果您同时开启了页面优化和压缩功能(智能压缩或者Brotli压缩),页面优化功能将会失效,CDN只会对文件进行压缩。
Brotli压缩只兼容部分浏览器,您可以根据业务需要查询浏览器的兼容情况。
常见的图片文件类型(PNG、JPG、JPEG等)和视频文件类型(MP4、AVI、WMV等)已经做了内容的压缩处理,开启智能压缩或者Brotli压缩没有效果,建议您关闭压缩功能。如果您需要进一步减小图片文件的体积可以使用图像处理功能,如果您需要进一步减小视频文件的体积可以使用视频转码功能。“图像处理”和“视频转码”都会影响文件清晰度。
众所周知,Splash并不支持Windows,而Playwright并不支持Centos,很明显它们不能共存,至少它们不能完美的共存.
注意在 Windows 上不支持 mitmproxy 的控制台接口,但是可以使用 mitmdump 和 mitmweb。
指针地址和指针类型
每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行“取地址”操作。 Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:*int、*int64、*string等。
取变量指针的语法如下:
ptr := &v // v的类型为T
其中:
v:代表被取地址的变量,类型为T
ptr:用于接收地址的变量,ptr的类型就为*T,称做T的指针类型。*代表指针。
举个例子:
func main() {
a := 10
b := &a
// a:10 ptr:0xc00001a078
fmt.Printf("a:%d ptr:%p\n", a, &a)
// b:0xc00001a078 type:*int
fmt.Printf("b:%p type:%T\n", b, b)
// 0xc00000e018
fmt.Println(&b)
}
Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。
传递数据使用指针,而无须拷贝数据。类型指针不能进行偏移和运算。
Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)。
golang slice data[:6:8] 两个冒号的理解。
常规slice , data[6:8],从第6位到第8位(返回6, 7),长度len为2, 最大可扩充长度cap为4(6-9)。
另一种写法: data[:6:8] 每个数字前都有个冒号, slice内容为data从0到第6位,长度len为6,最大扩充项cap设置为8。
a[x:y:z] 切片内容 [x:y] 切片长度: y-x 切片容量:z-x。
string本身是不可变的,因此要改变string中字符。需要如下操作: 英文字符串:
package main
import (
"fmt"
)
func main() {
str := "Hello world"
//中文字符需要用[]rune(str)
s := []byte(str)
s[6] = 'G'
s = s[:8]
s = append(s, '!')
str = string(s)
fmt.Println(str)
}
输出结果:
Hello Go!
func main() {
str := "你好,世界!hello world!"
s := []rune(str)
s[3] = '够'
s[4] = '浪'
s[12] = 'g'
s = s[:14]
str = string(s)
fmt.Println(str)
}
输出结果:你好,够浪!hello go
你好,够浪!hello go
GO语言
内置函数 len 和 cap 都返回数组长度 (元素数量)。
package main
func main() {
a := [2]int{}
println(len(a), cap(a))
}
输出结果:
2 2