」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > Go語言結構體深度解析

Go語言結構體深度解析

發佈於2025-04-21
瀏覽:669

In Go, struct is an aggregate type used for defining and encapsulating data. It allows combining fields of different types. Structs can be seen as custom data types similar to classes in other languages, but they do not support inheritance. Methods are functions associated with a specific type (often a struct) and can be called using an instance of that type.

Defining and Initializing Structs

Defining a Struct

Structs are defined using the type and struct keywords. Here's an example of a simple struct definition:

type User struct {
  Username    string
  Email       string
  SignInCount int
  IsActive    bool
}

Initializing a Struct

Structs can be initialized in various ways.

Initializing with Field Names

user1 := User{
  Username:    "alice",
  Email:       "[email protected]",
  SignInCount: 1,
  IsActive:    true,
}

Initializing with Default Values

If some fields are not specified, they are initialized to their zero values for the respective types.

user2 := User{
  Username: "bob",
}

In this example, Email will be initialized to an empty string (""), SignInCount to 0, and IsActive to false.

Initializing with a Pointer

A struct can also be initialized using a pointer.

user3 := &User{
  Username: "charlie",
  Email:    "[email protected]",
}

Methods and Behavior of Structs

In Go, structs are not only for storing data but can also have methods defined for them. This enables structs to encapsulate behavior related to their data. Below is a detailed explanation of struct methods and behavior.

Defining Methods for Structs

Methods are defined using a receiver, which is the first parameter of the method and specifies the type the method belongs to. The receiver can be either a value receiver or a pointer receiver.

Value Receiver

A value receiver creates a copy of the struct when the method is called, so modifications to fields do not affect the original struct.

type User struct {
  Username string
  Email    string
}

func (u User) PrintInfo() {
  fmt.Printf("Username: %s, Email: %s\n", u.Username, u.Email)
}

Pointer Receiver

A pointer receiver allows the method to modify the original struct fields directly.

func (u *User) UpdateEmail(newEmail string) {
  u.Email = newEmail
}

Method Sets

In Go, all methods of a struct form its method set. The method set for a value receiver includes all methods with value receivers, while the method set for a pointer receiver includes all methods with both pointer and value receivers.

Interfaces and Struct Methods

Struct methods are often used with interfaces to achieve polymorphism. When defining an interface, you specify the methods a struct must implement.

type UserInfo interface {
  PrintInfo()
}

// User implements the UserInfo interface
func (u User) PrintInfo() {
  fmt.Printf("Username: %s, Email: %s\n", u.Username, u.Email)
}

func ShowInfo(ui UserInfo) {
  ui.PrintInfo()
}

Memory Alignment in Structs

In Go, memory alignment for structs is designed to improve access efficiency. Different data types have specific alignment requirements, and the compiler may insert padding bytes between struct fields to meet these requirements.

What is Memory Alignment?

Memory alignment means that data in memory must be located at addresses that are multiples of certain values. The size of a data type determines its alignment requirement. For example, int32 requires alignment to 4 bytes, and int64 requires alignment to 8 bytes.

Why is Memory Alignment Necessary?

Efficient memory access is critical for CPU performance. If a variable is not properly aligned, the CPU may need multiple memory accesses to read or write data, leading to performance degradation. By aligning data, the compiler ensures efficient memory access.

Rules for Struct Memory Alignment

  • Field alignment: Each field's address must meet its type's alignment requirements. The compiler may insert padding bytes between fields to ensure proper alignment.
  • Struct alignment: The size of a struct must be a multiple of the largest alignment requirement among its fields.

Example:

package main

import (
  "fmt"
  "unsafe"
)

type Example struct {
  a int8   // 1 byte
  b int32  // 4 bytes
  c int8   // 1 byte
}

func main() {
  fmt.Println(unsafe.Sizeof(Example{}))
}

Output: 12

Analysis:

  • a is int8, occupying 1 byte, aligned to 1.
  • b is int32, requiring alignment to 4 bytes. The compiler inserts 3 padding bytes between a and b to align b's address to 4.
  • c is int8, requiring 1 byte, but the struct's total size must be a multiple of 4 (the largest alignment requirement). The compiler adds 3 padding bytes at the end.

Optimizing Memory Alignment

You can rearrange struct fields to minimize padding and reduce memory usage.

type Optimized struct {
  b int32  // 4 bytes
  a int8   // 1 byte
  c int8   // 1 byte
}

Output: 8

In this optimized version, b is placed first, aligning it to 4 bytes. a and c are placed consecutively, making the total size 8 bytes, which is more compact than the unoptimized version.

Summary

  • Struct fields in Go are allocated memory based on their alignment requirements, with potential padding bytes.
  • Adjusting the order of fields can minimize padding and optimize memory usage.
  • Use unsafe.Sizeof to determine the actual memory size of a struct.

Nested Structs and Composition

In Go, nested structs and composition are powerful tools for code reuse and organizing complex data. Nested structs allow a struct to include another struct as a field, enabling the creation of complex data models. Composition, on the other hand, creates new structs by including other structs, facilitating code reuse.

Nested Structs

Nested structs enable one struct to include another struct as a field. This makes data structures more flexible and organized. Here's an example of a nested struct:

package main

import "fmt"

// Define the Address struct
type Address struct {
  City    string
  Country string
}

// Define the User struct, which includes the Address struct
type User struct {
  Username string
  Email    string
  Address  Address // Nested struct
}

func main() {
  // Initialize the nested struct
  user := User{
    Username: "alice",
    Email:    "[email protected]",
    Address: Address{
      City:    "New York",
      Country: "USA",
    },
  }

  // Access fields of the nested struct
  fmt.Printf("User: %s, Email: %s, City: %s, Country: %s\n", user.Username, user.Email, user.Address.City, user.Address.Country)
}

Struct Composition

Composition allows multiple structs to be combined into a new struct, enabling code reuse. In composition, a struct can include multiple other structs as fields. This helps build more complex models and share common fields or methods. Here's an example of struct composition:

package main

import "fmt"

// Define the Address struct
type Address struct {
  City    string
  Country string
}

// Define the Profile struct
type Profile struct {
  Age int
  Bio string
}

// Define the User struct, which composes Address and Profile
type User struct {
  Username string
  Email    string
  Address  Address // Composes the Address struct
  Profile  Profile // Composes the Profile struct
}

func main() {
  // Initialize the composed struct
  user := User{
    Username: "bob",
    Email:    "[email protected]",
    Address: Address{
      City:    "New York",
      Country: "USA",
    },
    Profile: Profile{
      Age: 25,
      Bio: "A software developer.",
    },
  }

  // Access fields of the composed struct
  fmt.Printf("User: %s, Email: %s, City: %s, Age: %d, Bio: %s\n", user.Username, user.Email, user.Address.City, user.Profile.Age, user.Profile.Bio)
}

Differences Between Nested Structs and Composition

  • Nested Structs: Used to combine structs together, where a field's type in one struct is another struct. This approach is often employed to describe data models with hierarchical relationships.
  • Composition: Allows a struct to include fields from multiple other structs. This method is used to achieve code reuse, enabling a struct to have more complex behaviors and attributes.

Summary

Nested structs and composition are powerful features in Go that help organize and manage complex data structures. When designing data models, using nested structs and composition appropriately can make your code clearer and more maintainable.

Empty Struct

An empty struct in Go is a struct with no fields.

Size and Memory Address

An empty struct occupies zero bytes of memory. However, its memory address may or may not be equal under different circumstances. When memory escape occurs, the addresses are equal, pointing to runtime.zerobase.

// empty_struct.go
type Empty struct{}

//go:linkname zerobase runtime.zerobase
var zerobase uintptr // Using the go:linkname directive to link zerobase to runtime.zerobase

func main() {
  a := Empty{}
  b := struct{}{}

  fmt.Println(unsafe.Sizeof(a) == 0) // true
  fmt.Println(unsafe.Sizeof(b) == 0) // true
  fmt.Printf("%p\n", &a)             // 0x590d00
  fmt.Printf("%p\n", &b)             // 0x590d00
  fmt.Printf("%p\n", &zerobase)      // 0x590d00

  c := new(Empty)
  d := new(Empty) // Forces c and d to escape
  fmt.Sprint(c, d)
  println(c)   // 0x590d00
  println(d)   // 0x590d00
  fmt.Println(c == d) // true

  e := new(Empty)
  f := new(Empty)
  println(e)   // 0xc00008ef47
  println(f)   // 0xc00008ef47
  fmt.Println(e == f) // false
}

From the output, variables a, b, and zerobase share the same address, all pointing to the global variable runtime.zerobase (runtime/malloc.go).

Regarding escape scenarios:

  • Variables c and d escape to the heap. Their addresses are 0x590d00, and they compare equal (true).
  • Variables e and f have different addresses (0xc00008ef47) and compare unequal (false).

This behavior is intentional in Go. When empty struct variables do not escape, their pointers are unequal. After escaping, the pointers become equal.

Space Calculation When Embedding Empty Structs

An empty struct itself occupies no space, but when embedded in another struct, it might consume space depending on its position:

  • When it is the only field in the struct, the struct occupies no space.
  • When it is the first or intermediate field, it occupies no space.
  • When it is the last field, it occupies space equal to the previous field.
type s1 struct {
  a struct{}
}

type s2 struct {
  _ struct{}
}

type s3 struct {
  a struct{}
  b byte
}

type s4 struct {
  a struct{}
  b int64
}

type s5 struct {
  a byte
  b struct{}
  c int64
}

type s6 struct {
  a byte
  b struct{}
}

type s7 struct {
  a int64
  b struct{}
}

type s8 struct {
  a struct{}
  b struct{}
}

func main() {
  fmt.Println(unsafe.Sizeof(s1{})) // 0
  fmt.Println(unsafe.Sizeof(s2{})) // 0
  fmt.Println(unsafe.Sizeof(s3{})) // 1
  fmt.Println(unsafe.Sizeof(s4{})) // 8
  fmt.Println(unsafe.Sizeof(s5{})) // 16
  fmt.Println(unsafe.Sizeof(s6{})) // 2
  fmt.Println(unsafe.Sizeof(s7{})) // 16
  fmt.Println(unsafe.Sizeof(s8{})) // 0
}

When empty structs are elements of arrays or slices:

var a [10]int
fmt.Println(unsafe.Sizeof(a)) // 80

var b [10]struct{}
fmt.Println(unsafe.Sizeof(b)) // 0

var c = make([]struct{}, 10)
fmt.Println(unsafe.Sizeof(c)) // 24, the size of the slice header

Applications

The zero-size property of empty structs allows them to be used for various purposes without extra memory overhead.

Prevent Unkeyed Struct Initialization

type MustKeyedStruct struct {
  Name string
  Age  int
  _    struct{}
}

func main() {
  person := MustKeyedStruct{Name: "hello", Age: 10}
  fmt.Println(person)

  person2 := MustKeyedStruct{"hello", 10} // Compilation error: too few values in MustKeyedStruct{...}
  fmt.Println(person2)
}

Implementing a Set Data Structure

package main

import (
  "fmt"
)

type Set struct {
  items map[interface{}]emptyItem
}

type emptyItem struct{}

var itemExists = emptyItem{}

func NewSet() *Set {
  return &Set{items: make(map[interface{}]emptyItem)}
}

func (set *Set) Add(item interface{}) {
  set.items[item] = itemExists
}

func (set *Set) Remove(item interface{}) {
  delete(set.items, item)
}

func (set *Set) Contains(item interface{}) bool {
  _, contains := set.items[item]
  return contains
}

func (set *Set) Size() int {
  return len(set.items)
}

func main() {
  set := NewSet()
  set.Add("hello")
  set.Add("world")
  fmt.Println(set.Contains("hello"))
  fmt.Println(set.Contains("Hello"))
  fmt.Println(set.Size())
}

Signal Transmission via Channels

Sometimes, the content of the data transmitted through a channel is irrelevant, serving only as a signal. For instance, empty structs can be used in semaphore implementations:

var empty = struct{}{}

type Semaphore chan struct{}

func (s Semaphore) P(n int) {
  for i := 0; i 





We are Leapcell, your top choice for deploying Go projects to the cloud.

Deep Dive into Go Struct

Leapcell is the Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis:

  1. Multi-Language Support
  • Develop with JavaScript, Python, Go, or Rust.
  1. Deploy unlimited projects for free
  • pay only for usage — no requests, no charges.
  1. Unbeatable Cost Efficiency
  • Pay-as-you-go with no idle charges.
  • Example: $25 supports 6.94M requests at a 60ms average response time.
  1. Streamlined Developer Experience
  • Intuitive UI for effortless setup.
  • Fully automated CI/CD pipelines and GitOps integration.
  • Real-time metrics and logging for actionable insights.
  1. Effortless Scalability and High Performance
  • Auto-scaling to handle high concurrency with ease.
  • Zero operational overhead — just focus on building.

Explore more in the Documentation!

Follow us on X: @LeapcellHQ


Read on our blog

版本聲明 本文轉載於:https://dev.to/leapcell/deep-dive-into-go-struct-mf6?1如有侵犯,請聯繫[email protected]刪除
最新教學 更多>
  • C++成員函數指針正確傳遞方法
    C++成員函數指針正確傳遞方法
    如何將成員函數置於c [&& && && && && && && && && && &&&&&&&&&&&&&&&&&&&&&&&華儀的函數時,在接受成員函數指針的函數時,要在函數上既要提供指針又可以提供指針和指針到函數的函數。需要具有一定簽名的功能指針。要通過成員函數,您需要同時提供對象指針(此...
    程式設計 發佈於2025-07-16
  • 大批
    大批
    [2 數組是對象,因此它們在JS中也具有方法。 切片(開始):在新數組中提取部分數組,而無需突變原始數組。 令ARR = ['a','b','c','d','e']; // USECASE:提取直到索引作...
    程式設計 發佈於2025-07-16
  • 如何在其容器中為DIV創建平滑的左右CSS動畫?
    如何在其容器中為DIV創建平滑的左右CSS動畫?
    通用CSS動畫,用於左右運動 ,我們將探索創建一個通用的CSS動畫,以向左和右移動DIV,從而到達其容器的邊緣。該動畫可以應用於具有絕對定位的任何div,無論其未知長度如何。 問題:使用左直接導致瞬時消失 更加流暢的解決方案:混合轉換和左 [並實現平穩的,線性的運動,我們介紹了線性的轉換。...
    程式設計 發佈於2025-07-16
  • 為什麼我在Silverlight Linq查詢中獲得“無法找到查詢模式的實現”錯誤?
    為什麼我在Silverlight Linq查詢中獲得“無法找到查詢模式的實現”錯誤?
    查詢模式實現缺失:解決“無法找到”錯誤在Silverlight應用程序中,嘗試使用LINQ建立LINQ連接以錯誤而實現的數據庫”,無法找到查詢模式的實現。”當省略LINQ名稱空間或查詢類型缺少IEnumerable 實現時,通常會發生此錯誤。 解決問題來驗證該類型的質量是至關重要的。在此特定實例...
    程式設計 發佈於2025-07-16
  • Go語言垃圾回收如何處理切片內存?
    Go語言垃圾回收如何處理切片內存?
    Garbage Collection in Go Slices: A Detailed AnalysisIn Go, a slice is a dynamic array that references an underlying array.使用切片時,了解垃圾收集行為至關重要,以避免潛在的內存洩...
    程式設計 發佈於2025-07-16
  • 同實例無需轉儲複製MySQL數據庫方法
    同實例無需轉儲複製MySQL數據庫方法
    在同一實例上複製一個MySQL數據庫而無需轉儲在同一mySQL實例上複製數據庫,而無需創建InterMediate sqql script。以下方法為傳統的轉儲和IMPORT過程提供了更簡單的替代方法。 直接管道數據 MySQL手動概述了一種允許將mysqldump直接輸出到MySQL cli...
    程式設計 發佈於2025-07-16
  • 如何從PHP中的Unicode字符串中有效地產生對URL友好的sl。
    如何從PHP中的Unicode字符串中有效地產生對URL友好的sl。
    為有效的slug生成首先,該函數用指定的分隔符替換所有非字母或數字字符。此步驟可確保slug遵守URL慣例。隨後,它採用ICONV函數將文本簡化為us-ascii兼容格式,從而允許更廣泛的字符集合兼容性。 接下來,該函數使用正則表達式刪除了不需要的字符,例如特殊字符和空格。此步驟可確保slug僅包...
    程式設計 發佈於2025-07-16
  • 如何解決AppEngine中“無法猜測文件類型,使用application/octet-stream...”錯誤?
    如何解決AppEngine中“無法猜測文件類型,使用application/octet-stream...”錯誤?
    appEngine靜態文件mime type override ,靜態文件處理程序有時可以覆蓋正確的mime類型,在錯誤消息中導致錯誤消息:“無法猜測mimeType for for file for file for [File]。 application/application/octet...
    程式設計 發佈於2025-07-16
  • PHP SimpleXML解析帶命名空間冒號的XML方法
    PHP SimpleXML解析帶命名空間冒號的XML方法
    在php 很少,請使用該限制很大,很少有很高。例如:這種技術可確保可以通過遍歷XML樹和使用兒童()方法()方法的XML樹和切換名稱空間來訪問名稱空間內的元素。
    程式設計 發佈於2025-07-16
  • 如何修復\“常規錯誤:2006 MySQL Server在插入數據時已經消失\”?
    如何修復\“常規錯誤:2006 MySQL Server在插入數據時已經消失\”?
    How to Resolve "General error: 2006 MySQL server has gone away" While Inserting RecordsIntroduction:Inserting data into a MySQL database can...
    程式設計 發佈於2025-07-16
  • 如何限制動態大小的父元素中元素的滾動範圍?
    如何限制動態大小的父元素中元素的滾動範圍?
    在交互式接口中實現垂直滾動元素的CSS高度限制問題:考慮一個佈局,其中我們具有與用戶垂直滾動一起移動的可滾動地圖div,同時與固定的固定sidebar保持一致。但是,地圖的滾動無限期擴展,超過了視口的高度,阻止用戶訪問頁面頁腳。 $("#map").css({ margin...
    程式設計 發佈於2025-07-16
  • 為什麼使用固定定位時,為什麼具有100%網格板柱的網格超越身體?
    為什麼使用固定定位時,為什麼具有100%網格板柱的網格超越身體?
    網格超過身體,用100%grid-template-columns 為什麼在grid-template-colms中具有100%的顯示器,當位置設置為設置的位置時,grid-template-colly修復了? 問題: 考慮以下CSS和html: class =“ snippet-code”> ...
    程式設計 發佈於2025-07-16
  • `console.log`顯示修改後對象值異常的原因
    `console.log`顯示修改後對象值異常的原因
    foo = [{id:1},{id:2},{id:3},{id:4},{id:id:5},],]; console.log('foo1',foo,foo.length); foo.splice(2,1); console.log('foo2', foo, foo....
    程式設計 發佈於2025-07-16
  • 為什麼不````''{margin:0; }`始終刪除CSS中的最高邊距?
    為什麼不````''{margin:0; }`始終刪除CSS中的最高邊距?
    在CSS 問題:不正確的代碼: 全球範圍將所有餘量重置為零,如提供的代碼所建議的,可能會導致意外的副作用。解決特定的保證金問題是更建議的。 例如,在提供的示例中,將以下代碼添加到CSS中,將解決餘量問題: body H1 { 保證金頂:-40px; } 此方法更精確,避免了由全局保證金重置...
    程式設計 發佈於2025-07-16
  • 解決MySQL插入Emoji時出現的\\"字符串值錯誤\\"異常
    解決MySQL插入Emoji時出現的\\"字符串值錯誤\\"異常
    Resolving Incorrect String Value Exception When Inserting EmojiWhen attempting to insert a string containing emoji characters into a MySQL database us...
    程式設計 發佈於2025-07-16

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3