بروید #006 – شرط و حلقه ها: جریان کنترل بدون درهم و برهمی

شرط ها و حلقه های Go با طراحی مینیمالیستی هستند ، اما سادگی آنها تصمیمات دقیق حافظه را پنهان می کند. بیایید کشف کنیم چگونه if
با switch
وت for
تعامل با بخش های پشته ، پشته و داده ها – و در جایی که تخصیص های پنهان در آن خزش می شوند.
رمز
package main
func main() {
// Conditional with stack-allocated variable
if score := 42; score > 50 {
// ...
}
// Loop with slice iteration
nums := []int{10, 20, 30} // Slice header (stack), backing array (heap)
for i, n := range nums {
// ...
}
// Loop closure capturing variable
for _, n := range nums {
func() {
println(n) // n escapes to heap
}()
}
}
تقسیم بندی حافظه تجسم شد
+-------------------+ Lowest Address
| Code |
|-------------------|
| main() | // Compiled logic for conditionals/loops
| range handling | // Machine code for slice iteration
+-------------------+
| Data |
|-------------------|
| (No constants) |
+-------------------+
| Stack |
|-------------------|
| score (int = 42) | // if-scoped variable
| nums (slice) | // Pointer to heap array
| i, n (loop vars) | // Per-iteration copies
+-------------------+
| Heap |
|-------------------|
| []int backing | // nums' array [10, 20, 30]
| n (int copies) | // Captured by closure (one per iteration)
+-------------------+ Highest Address
تجزیه رفتار حافظه
1. شرط بندی (if
با switch
)
-
متغیرهای اعلام شده در
if
/switch
:-
score
درif score := 42; ...
است ، پشتهبشر - به بلوک ، بلافاصله تمیز شد.
-
-
بدون تخصیص پشته ضمنی مگر اینکه متغیرها فرار کنند (به عنوان مثال ، به بسته شدن).
2. حلقه ها (for
با range
)
-
متغیرهای حلقه:
-
i
وتn
درfor i, n := range nums
هستند استفاده مجدد در هر تکرار (اختصاص داده شده). - مقادیر کپی می شوند ، بنابراین اصلاح
n
بر قطعه اصلی تأثیر نمی گذارد.
-
-
آرایه پشتی برش:
-
nums := []int{10, 20, 30}
هدر برش (اشاره گر ، لن ، کلاه) را روی پشتهبشر - آرایه واقعی
[10, 20, 30]
زندگی در پشتهبشر
-
3. بسته شدن در حلقه ها
-
متغیرهای ضبط شده:
-
n
در بسته شدنfunc() { println(n) }()
فرار به پشتهبشر - هر تکرار
n
برای افزایش تکرار حلقه در پشته کپی شده است.
-
-
با تجزیه و تحلیل فرار تأیید کنید:
go build -gcflags="-m" main.go
# ./main.go:12:3: ... n escapes to heap
چرا این مهم است
-
استفاده مجدد از متغیر حلقه: بروید همان آدرس حافظه را برای استفاده کنید
i
وتn
در هر تکرار-
گودا: goroutines در حلقه ها متغیرهای حلقه را ضبط می کنند (به عنوان مثال ،
go func() { ... }()
) اسیر آخرین ارزش ازi
/n
بشر رفع:
for _, n := range nums { n := n // Create a stack copy per iteration go func() { println(n) }() }
-
گودا: goroutines در حلقه ها متغیرهای حلقه را ضبط می کنند (به عنوان مثال ،
-
بازده برش: برش های بزرگ تخصیص پشته را برای پشتوانه آرایه ها مجبور می کنند. قبل از اندازه با
make
برای جلوگیری از محاصره.
نکات بهینه سازی
- از بسته شدن در حلقه های داغ خودداری کنید: برای نگه داشتن متغیرها در پشته ، از پارامترهای صریح استفاده کنید.
- برش های قبل از تخصیص:
data := make([]int, 0, 1000) // Heap-allocated but avoids reallocations
- در شرایط از رابط {} مراقب باشید:
var val interface{} = 42 // Heap escape
if val == 42 { ... } // Type check overhead
بینش کلیدی: برو for
حلقه ها برای به حداقل رساندن تخصیص از متغیرها استفاده مجدد می کنند – یک انتخاب طراحی که سریع است اما با تعطیلی و گوروها احتیاط می کند.