十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
int is a signed integer type that is at least 32 bits in size. It is a distinct type, however, and not an alias for, say, int32.
站在用户的角度思考问题,与客户深入沟通,找到綦江网站设计与綦江网站推广的解决方案,凭借多年的经验,让设计与互联网技术结合,创造个性化、用户体验好的作品,建站类型包括:成都网站制作、成都做网站、企业官网、英文网站、手机端网站、网站推广、域名申请、网络空间、企业邮箱。业务覆盖綦江地区。
int 是带符号整数类型,其大小至少为32位。 它是一种确切的类型,而不是 int32 的别名。
int 不是int32,那 int 在内存站多少字节呢?官方没有明确,让我们测试下。
GOARCH="amd64"
package main
import (
"fmt"
"unsafe"
)
func main() {
i := int(1)
fmt.Println(unsafe.Sizeof(i)) // 4
j := 1
fmt.Println(unsafe.Sizeof(j)) // 4
u := uint(1)
fmt.Println(unsafe.Sizeof(u)) // 4
}
可以认为 int 是4字节么?我不敢这样认为,GoLang支持多种平台架构。如果对size有明确要求,那就用 int32 之类的吧。
补充:随Go版本的变化,这个的确是在变化,所以到底占用多少字节,还是看具体版本吧
Go中的binary包实现了简单的数字与字节序列的转换以及变长值的编解码
package main
import ( "fmt" "bytes" "encoding/binary" ) func main(){ n := 0x12345678 bytesBuffer := bytes.NewBuffer([]byte{}) //BigEndian 大端顺序存储 LittleEndian小端顺序存储 binary.Write(bytesBuffer, binary.BigEndian, int32(n)) data:=bytesBuffer.Bytes() fmt.Printf("[0]: %#x addr:%#x\n",data[0],data[0]) fmt.Printf("[0]: %#x addr:%#x\n",data[1],data[1]) fmt.Printf("[0]: %#x addr:%#x\n",data[2],data[2]) fmt.Printf("[0]: %#x addr:%#x\n",data[3],data[3]) }
输出
[0]: 0x12 addr:0xc042010248 [1]: 0x34 addr:0xc042010249 [2]: 0x56 addr:0xc04201024a [3]: 0x78 addr:0xc04201024b
也可以使用下面的方式
n := 0x12345678 var data []byte = make([]byte,4) //操作的都是无符号整型 binary.BigEndian.PutUint32(data,uint32(n))
可以使用下面的方式判断当前系统的字节序类型
const INT_SIZE int = int(unsafe.Sizeof(0))
//判断我们系统中的字节序类型 func systemEdian() { var i int = 0x1 bs := (*[INT_SIZE]byte)(unsafe.Pointer(i)) if bs[0] == 0 { fmt.Println("system edian is little endian") } else { fmt.Println("system edian is big endian") } }
用golang解析二进制协议时,其实没必要管结构体的字段的对齐规则,何况语言规范也没有规定如何对齐,也就是没有规则。用encoding/binary.Read函数直接读入struct里就行,struct就像c那样写
type Data struct {
Size, MsgType uint16
Sequence uint32
// ...
}
golang编译器加不加padding,Read都能正常工作,runtime知道Data的布局的,不像C直接做cast所以要知道怎样对齐。
用unsafe.Alignof可以知道每个field的对齐长度,但没必要用到。
package main
/*
#include stdint.h
#pragma pack(push, 1)
typedef struct {
uint16_t size;
uint16_t msgtype;
uint32_t sequnce;
uint8_t data1;
uint32_t data2;
uint16_t data3;
} mydata;
#pragma pack(pop)
mydata foo = {
1, 2, 3, 4, 5, 6,
};
int size() {
return sizeof(mydata);
}
*/
import "C"
import (
"bytes"
"encoding/binary"
"fmt"
"log"
"unsafe"
)
func main() {
bs := C.GoBytes(unsafe.Pointer(C.foo), C.size())
fmt.Printf("len %d data %v\n", len(bs), bs)
var data struct {
Size, Msytype uint16
Sequence uint32
Data1 uint8
Data2 uint32
Data3 uint16
}
err := binary.Read(bytes.NewReader(bs), binary.LittleEndian, data)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%v\n", data) // {1 2 3 4 5 6}
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, data)
fmt.Printf("%d %v\n", buf.Len(), buf.Bytes()) // 15 [0 1 0 2 0 0 0 3 4 0 0 0 5 0 6]
}
struct是Go中的关键字,用于定义结构类型。
例如:
struct {}是一个无元素的结构体类型,通常在没有信息存储时使用。优点是大小为0,不需要内存来存储struct {}类型的值。
struct {} {}是一个复合字面量,它构造了一个struct {}类型的值,该值也是空。
go中可以使用 unsafe.Sizeof 计算出一个数据类型实例需要占用的字节数。我们验证一下:
也就是说空结构体实例不占用任何内存空间。
Go 语言标准库没有提供 Set 的实现,通常使用 map 来代替。事实上,对于集合来说,只需要 map 的键,而不需要值。
声明为声明为 map[string]struct{} ,由于struct{}是空,不关心内容,这样map便改造为set 。
map可以通过“comma ok”机制来获取该key是否存在,例如 _, ok := map["key"] ,如果没有对应的值,ok为false。可以通过定义成 map[string]struct{} 的形式,值不再占用内存。其值仅有两种状态,有或无。如果定义的是 map[string]bool ,则结果有true、false或没有三种状态,而且即使是将值设置为 bool 类型,也会多占据 1 个字节。因此呢,将 map 作为集合(Set)使用时,可以将值类型定义为空结构体,仅作为占位符使用即可。
基于channels发送消息有两个重要方面:发了消息、发了什么消息。一个强调了通讯的发生,一个强调了通讯的内容。当我们更希望强调通讯发生的时刻时,我们将它称为 消息事件 。有些消息事件并不携带额外的信息,它仅仅是用作两个goroutine之间的同步,这时候我们可以用 struct{} 空结构体作为channels元素的类型。用来通知子协程(goroutine)执行任务,或只用来控制协程并发度。
在部分场景下,结构体只包含方法,不包含任何的字段。这时候我们就可以使用空结构体。
其实,上面的calculateInt 可以是任何类型,如 type calculateInt bool ,但是struct{}不占用任何空间,逻辑上也更合理,因此还是它最好。