日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

指针不显示 upupw_Go高级编程:指针和内存分配详解

發(fā)布時(shí)間:2025/3/20 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 指针不显示 upupw_Go高级编程:指针和内存分配详解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

點(diǎn)擊上方藍(lán)色“Go語言中文網(wǎng)”關(guān)注我們,設(shè)個(gè)星標(biāo),每天學(xué)習(xí)?Go?語言

定義

了解指針之前,先講一下什么是變量。

每當(dāng)我們編寫任何程序時(shí),我們都需要在內(nèi)存中存儲(chǔ)一些數(shù)據(jù)/信息。數(shù)據(jù)存儲(chǔ)在特定地址的存儲(chǔ)器中。內(nèi)存地址看起來像0xAFFFF(這是內(nèi)存地址的十六進(jìn)制表示)。

現(xiàn)在,要訪問數(shù)據(jù),我們需要知道存儲(chǔ)它的地址。我們可以跟蹤存儲(chǔ)與程序相關(guān)的數(shù)據(jù)的所有內(nèi)存地址。但想象一下,記住所有內(nèi)存地址并使用它們?cè)L問數(shù)據(jù)會(huì)有非常困難。這就是為什么引入變量。

變量是一種占位符,用于引用計(jì)算機(jī)的內(nèi)存地址,可理解為內(nèi)存地址的標(biāo)簽。

什么是指針

指針是存儲(chǔ)另一個(gè)變量的內(nèi)存地址的變量。所以指針也是一種變量,只不過它是一種特殊變量,它的值存放的是另一個(gè)變量的內(nèi)存地址。

在上面的例子中,指針p包含值0x0001,該值是變量的地址a。

Go類型占用內(nèi)存情況

unsafe包可以獲取變量的內(nèi)存使用情況

Go語言提供以下基本數(shù)字類型:

  • 無符號(hào)整數(shù):uint8,uint16,uint32,uint64

  • 符號(hào)整數(shù):int8,int16,int32,int64

  • 實(shí)數(shù):float32,float64 Predeclared

  • 整數(shù)(依賴系統(tǒng)類型,跟系統(tǒng)有關(guān)):uint,int,uintptr (指針)

    • 32位系統(tǒng)

      uint=uint32 int=int32 uintptr為32位的指針

    • 64位系統(tǒng)

      uint=uint64 int=int64 uintptr為64位的指針

示例:

package main

import (
"fmt"
"unsafe"
)

func main() {
var uint8Value uint8
var uint16Value uint16
var uint32Value uint32
var uint64Value uint64
var int8Value int8
var int16Value int16
var int32Value int32
var int64Value int64

var float32Value float32
var float64Value float64

fmt.Println("uint8Value = Size:", unsafe.Sizeof(uint8Value)) //uint8Value = Size: 1
fmt.Println("uint16Value = Size:", unsafe.Sizeof(uint16Value)) //uint16Value = Size: 2
fmt.Println("uint32Value = Size:", unsafe.Sizeof(uint32Value)) //uint32Value = Size: 4
fmt.Println("uint64Value = Size:", unsafe.Sizeof(uint64Value))// uint64Value = Size: 8

fmt.Println("int8Value = Size:", unsafe.Sizeof(int8Value)) //int8Value = Size: 1
fmt.Println("int16Value = Size:", unsafe.Sizeof(int16Value))//int16Value = Size: 2
fmt.Println("int32Value = Size:", unsafe.Sizeof(int32Value))//int32Value = Size: 4
fmt.Println("int64Value = Size:", unsafe.Sizeof(int64Value)) //int64Value = Size: 8

fmt.Println("float32Value = Size:", unsafe.Sizeof(float32Value)) //float32Value = Size: 4
fmt.Println("float64Value = Size:", unsafe.Sizeof(float64Value))//float64Value = Size: 8

}

上面的是基本類型,接下來了解下復(fù)雜類型,以結(jié)構(gòu)體類型為例

type Example struct {
BoolValue bool
IntValue int16
FloatValue float32
}

該結(jié)構(gòu)代表復(fù)雜類型。它代表7個(gè)字節(jié),帶有三個(gè)不同的數(shù)字表示。bool是一個(gè)字節(jié),int16是2個(gè)字節(jié),float32增加4個(gè)字節(jié)。但是,在此結(jié)構(gòu)的內(nèi)存中實(shí)際分配了8個(gè)字節(jié)。

所有內(nèi)存都分配在對(duì)齊邊界上,以最大限度地減少內(nèi)存碎片整理。要確定對(duì)齊邊界Go用于您的體系結(jié)構(gòu),您可以運(yùn)行unsafe.Alignof函數(shù)。Go為64bit Darwin平臺(tái)的對(duì)齊邊界是8個(gè)字節(jié)。因此,當(dāng)Go確定結(jié)構(gòu)的內(nèi)存分配時(shí),它將填充字節(jié)以確保最終內(nèi)存占用量是8的倍數(shù)。編譯器將確定添加填充的位置。

什么是內(nèi)存對(duì)齊呢?

內(nèi)存對(duì)齊,也叫邊界對(duì)齊(boundary alignment),是處理器為了提高處理性能而對(duì)存取數(shù)據(jù)的起始地址所提出的一種要求。編譯器為了使我們編寫的C程序更有效,就必須最大限度地滿足處理器對(duì)邊界對(duì)齊的要求。

從處理器的角度來看,需要盡可能減少對(duì)內(nèi)存的訪問次數(shù)以實(shí)現(xiàn)對(duì)數(shù)據(jù)結(jié)構(gòu)進(jìn)行更加高效的操作。為什么呢?因?yàn)楸M管處理器包含了緩存,但它在處理數(shù)據(jù)時(shí)還得讀取緩存中的數(shù)據(jù),讀取緩存的次數(shù)當(dāng)然是越少越好!如上圖所示,在采用邊界對(duì)齊的情況下,當(dāng)處理器需要訪問a_變量和b_變量時(shí)都只需進(jìn)行一次存取(圖中花括號(hào)表示一次存取操作)。若不采用邊界對(duì)齊,a_變量只要一次處理器操作,而b_變量卻至少要進(jìn)行兩次操作。對(duì)于b_,處理器還得調(diào)用更多指令將其合成一個(gè)完整的4字節(jié),這樣無疑大大降低了程序效率。

以下程序顯示Go插入到Example類型struct的內(nèi)存占用中的填充:

package main

import (
"fmt"
"unsafe"
)

type Example struct {
BoolValue bool
IntValue int16
FloatValue float32
}

func main() {
example := &Example{
BoolValue: true,
IntValue: 10,
FloatValue: 3.141592,
}

exampleNext := &Example{
BoolValue: true,
IntValue: 10,
FloatValue: 3.141592,
}

alignmentBoundary := unsafe.Alignof(example)

sizeBool := unsafe.Sizeof(example.BoolValue)
offsetBool := unsafe.Offsetof(example.BoolValue)

sizeInt := unsafe.Sizeof(example.IntValue)
offsetInt := unsafe.Offsetof(example.IntValue)

sizeFloat := unsafe.Sizeof(example.FloatValue)
offsetFloat := unsafe.Offsetof(example.FloatValue)

sizeBoolNext := unsafe.Sizeof(exampleNext.BoolValue)
offsetBoolNext := unsafe.Offsetof(exampleNext.BoolValue)

fmt.Printf("example Size: %d\n", unsafe.Sizeof(example))

fmt.Printf("Alignment Boundary: %d\n", alignmentBoundary)

fmt.Printf("BoolValue = Size: %d Offset: %d Addr: %v\n",
sizeBool, offsetBool, &example.BoolValue)

fmt.Printf("IntValue = Size: %d Offset: %d Addr: %v\n",
sizeInt, offsetInt, &example.IntValue)

fmt.Printf("FloatValue = Size: %d Offset: %d Addr: %v\n",
sizeFloat, offsetFloat, &example.FloatValue)

fmt.Printf("Next = Size: %d Offset: %d Addr: %v\n",
sizeBoolNext, offsetBoolNext, &exampleNext.BoolValue)

}

輸出:

example Size: 8
Alignment Boundary: 8
BoolValue = Size: 1 Offset: 0 Addr: 0xc00004c080
IntValue = Size: 2 Offset: 2 Addr: 0xc00004c082
FloatValue = Size: 4 Offset: 4 Addr: 0xc00004c084
Next = Size: 1 Offset: 0 Addr: 0xc00004c088

類型結(jié)構(gòu)的對(duì)齊邊界是預(yù)期的8個(gè)字節(jié)。

大小值顯示將讀取和寫入該字段的內(nèi)存量。正如所料,大小與類型信息一致。

偏移值顯示進(jìn)入內(nèi)存占用的字節(jié)數(shù),我們將找到該字段的開頭。

地址是可以找到內(nèi)存占用內(nèi)每個(gè)字段的開頭的地方。

我們可以看到Go在BoolValue和IntValue字段之間填充1個(gè)字節(jié)。偏移值和兩個(gè)地址之間的差異是2個(gè)字節(jié)。您還可以看到下一個(gè)內(nèi)存分配是從結(jié)構(gòu)中的最后一個(gè)字段開始4個(gè)字節(jié)。

指針的使用

聲明一個(gè)指針

使用以下語法聲明類型為T的指針

var p *int

指針的零值是nil。這意味著任何未初始化的指針都將具有該值nil。讓我們看一個(gè)完整的例子

package main
import "fmt"

func main() {
var p *int
&p=1
}

注意:當(dāng)指針沒有指向的時(shí)候,不能對(duì)(*point)進(jìn)行操作包括讀取,否則會(huì)報(bào)空指針異常。

示例:

package main

func main() {
var p *int

*p = 1 //panic: runtime error: invalid memory address or nil pointer dereference
}

解決方法即給該指針分配一個(gè)指向,即初始化一個(gè)內(nèi)存,并把該內(nèi)存地址賦予指針變量

示例:

import "fmt"

func main() {
var p *int
var m int
p = &m
*p = 1
fmt.Println("m=", m)
fmt.Println("p=", p)
}

或還可以使用內(nèi)置new()函數(shù)創(chuàng)建指針。該new()函數(shù)將類型作為參數(shù),分配足夠的內(nèi)存以容納該類型的值,并返回指向它的指針。

import "fmt"

func main() {
var p *int

p = new(int)
*p = 1
fmt.Println("p=", *p)
}

初始化指針

您可以使用另一個(gè)變量的內(nèi)存地址初始化指針。可以使用&運(yùn)算符檢索變量的地址

var x = 100
var p *int = &x

注意我們?nèi)绾问褂?amp;帶變量的運(yùn)算符x來獲取其地址,然后將地址分配給指針p。

就像Golang中的任何其他變量一樣,指針變量的類型也由編譯器推斷。所以你可以省略p上面例子中指針的類型聲明,并像這樣寫

var p = &a

取消引用指針

您可以*在指針上使用運(yùn)算符來訪問存儲(chǔ)在指針?biāo)赶虻淖兞恐械闹怠_@被稱為解除引用或間接

package main
import "fmt"

func main() {
var a = 100
var p = &a

fmt.Println("a = ", a)
fmt.Println("p = ", p)
fmt.Println("*p = ", *p)
}

輸出:

a = 100
p = 0xc00004c080
*p = 100

您不僅可以使用*運(yùn)算符訪問指向變量的值,還可以更改它。以下示例a通過指針設(shè)置存儲(chǔ)在變量中的值p

package main
import "fmt"

func main() {
var a = 1000
var p = &a

fmt.Println("a (before) = ", a)

// Changing the value stored in the pointed variable through the pointer
*p = 2000

fmt.Println("a (after) = ", a)
}

輸出:

a (before) = 1000
a (after) = 2000

指針指向指針

指針可以指向任何類型的變量。它也可以指向另一個(gè)指針。以下示例顯示如何創(chuàng)建指向另一個(gè)指針的指針

package main
import "fmt"

func main() {
var a = 7.98
var p = &a
var pp = &p

fmt.Println("a = ", a)
fmt.Println("address of a = ", &a)

fmt.Println("p = ", p)
fmt.Println("address of p = ", &p)

fmt.Println("pp = ", pp)

// Dereferencing a pointer to pointer
fmt.Println("*pp = ", *pp)
fmt.Println("**pp = ", **pp)
}

Go中沒有指針?biāo)阈g(shù)

如果您使用過C / C ++,那么您必須意識(shí)到這些語言支持指針?biāo)惴ā@?#xff0c;您可以遞增/遞減指針以移動(dòng)到下一個(gè)/上一個(gè)內(nèi)存地址。您可以向/從指針添加或減去整數(shù)值。您也可以使用關(guān)系運(yùn)算符比較兩個(gè)三分球==,<,>等。

但Go不支持對(duì)指針進(jìn)行此類算術(shù)運(yùn)算。任何此類操作都將導(dǎo)致編譯時(shí)錯(cuò)誤

package main

func main() {
var x = 67
var p = &x

var p1 = p + 1 // Compiler Error: invalid operation
}

但是,您可以使用==運(yùn)算符比較相同類型的兩個(gè)指針的相等性。

package main
import "fmt"

func main() {
var a = 75
var p1 = &a
var p2 = &a

if p1 == p2 {
fmt.Println("Both pointers p1 and p2 point to the same variable.")
}
}

Go中傳遞簡(jiǎn)單類型

import "fmt"

func main() {
p := 5
change(&p)
fmt.Println("p=", p)//p= 0
}
func change(p *int) {
*p = 0
}

Go中所有的都是按值傳遞,對(duì)于復(fù)雜類型,傳的是指針的拷貝

package main

import "fmt"

func main() {
var m map[string]int
m = map[string]int{"one": 1, "two": 2}
n := m
fmt.Printf("%p\n", &m) //0xc000074018
fmt.Printf("%p\n", &n) //0xc000074020
fmt.Println(m) // map[two:2 one:1]
fmt.Println(n) //map[one:1 two:2]
changeMap(m)
fmt.Printf("%p\n", &m) //0xc000074018
fmt.Printf("%p\n", &n) //0xc000074020
fmt.Println(m) //map[one:1 two:2 three:3]
fmt.Println(n) //map[one:1 two:2 three:3]
}
func changeMap(m map[string]int) {
m["three"] = 3
fmt.Printf("changeMap func %p\n", m) //changeMap func 0xc000060240
}

直接傳指針 也是傳指針的拷貝

package main

import "fmt"

func main() {
var m map[string]int
m = map[string]int{"one": 1, "two": 2}
n := m
fmt.Printf("%p\n", &m) //0xc000074018
fmt.Printf("%p\n", &n) //0xc000074020
fmt.Println(m) // map[two:2 one:1]
fmt.Println(n) //map[one:1 two:2]
changeMap(&m)
fmt.Printf("%p\n", &m) //0xc000074018
fmt.Printf("%p\n", &n) //0xc000074020
fmt.Println(m) //map[one:1 two:2 three:3]
fmt.Println(n) //map[two:2 three:3 one:1]
}
func changeMap(m *map[string]int) {
//m["three"] = 3 //這種方式會(huì)報(bào)錯(cuò) invalid operation: m["three"] (type *map[string]int does not support indexing)
(*m)["three"] = 3 //正確
fmt.Printf("changeMap func %p\n", m) //changeMap func 0x0
}

總結(jié)

  • Go 不能進(jìn)行指針運(yùn)算。

  • 指針傳遞是很廉價(jià)的,只占用 4 個(gè)或 8 個(gè)字節(jié)。當(dāng)程序在工作中需要占用大量的內(nèi)存,或很多變量,或者兩者都有,使用指針會(huì)減少內(nèi)存占用和提高效率。

  • 指針也是一種類型,不同于一般類型,指針的值是地址,這個(gè)地址指向其他的內(nèi)存,通過指針可以讀取其所指向的地址所存儲(chǔ)的值。

  • 函數(shù)方法的接受者,也可以是指針變量。簡(jiǎn)單類型和復(fù)雜類型在傳遞的時(shí)候不同,復(fù)雜類型傳值或傳指針都是指針拷貝。

  • 只聲明未賦值的變量,golang都會(huì)自動(dòng)為其初始化為零值,基礎(chǔ)數(shù)據(jù)類型的零值比較簡(jiǎn)單,引用類型和指針的零值都為nil,nil類型不能直接賦值,因此需要通過new開辟一個(gè)內(nèi)存,或指向一個(gè)變量。

參考資料

  • http://golang.org/doc/faq#Pointers

  • https://www.callicoder.com/golang-pointers/

  • https://www.ardanlabs.com/blog/2013/07/understanding-pointers-and-memory.html

  • https://www.ardanlabs.com/blog/2013/07/understanding-type-in-go.html

喜歡本文的朋友,歡迎關(guān)注“Go語言中文網(wǎng)”,第一時(shí)間接收后續(xù)好文!

總結(jié)

以上是生活随笔為你收集整理的指针不显示 upupw_Go高级编程:指针和内存分配详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产靠逼视频 | 亚洲第一国产 | 69成人网| 六月丁香激情综合 | 99re在线 | 中文字幕日韩在线视频 | 日韩欧美亚洲 | 日韩最新中文字幕 | 色屁屁一区二区三区视频 | 成人播放器| 欧美日韩一级在线观看 | 亚洲精品视频网 | 天天干天天操天天 | 国产88av| 久久久精品美女 | 亚洲欧洲视频在线观看 | 豆花视频成人 | 亚洲精选久久久 | 色偷偷av男人的天堂 | 亚洲国产片 | 99久久99久久久精品棕色圆 | 欧美三级韩国三级日本三斤在线观看 | 成人深夜视频 | 成人国产精品一区二区 | 日韩中文字幕综合 | 久草资源站 | 国产视频一区在线 | 在线免费观看亚洲视频 | 91精品国产91久久久 | 欧美日本一区 | 九热这里只有精品 | 国产91在线亚洲 | 中文字幕一区二区三区波野结 | www.99re. | 成人宗合网| 日韩在线观看视频一区 | 成人一区av| 岛国伊人 | 成熟的女同志hd | 香蕉91视频 | 亚洲高清av在线 | 精品成人一区二区 | 日本精品久久久久 | 亚洲精品66 | 日本少妇xxxx | 国产午夜一区二区三区 | 精品一区二区三区免费毛片 | 久久发布国产伦子伦精品 | 先锋av资源网站 | 在线看亚洲 | 中文字幕有码在线观看 | 久久免费精品 | a级小视频 | 久久午夜夜伦鲁鲁一区二区 | 国产人妻777人伦精品hd | 欧美视频免费在线 | 在线你懂的 | 日产mv免费观看 | 亚洲美女操 | 亚洲av无码成人精品国产 | 影音先锋亚洲成aⅴ人在 | 91久久伊人 | 1024手机看片国产 | 超碰在线91| 国产女主播自拍 | 中文字幕亚洲高清 | 激情五月在线观看 | 亚洲大逼 | 色哟哟入口| 成年视频在线观看 | 欧美精品一区二 | 亚洲成人网络 | 欧美理伦片在线播放 | www香蕉| 性色一区二区三区 | av免费在线电影 | 日韩网红少妇无码视频香港 | 亚洲AV无码乱码国产精品色欲 | 露脸啪啪清纯大学生美女 | 婷婷色小说 | 91九色在线视频 | 亚洲国产精品久久久久婷婷老年 | 人妻精品久久久久中文字幕69 | 成年人在线视频网站 | 奶水旺盛的女人伦理 | 新婚夫妇白天啪啪自拍 | 两口子交换真实刺激高潮 | 亚洲午夜激情视频 | 人人爽人人爱 | 懂色av蜜臀av粉嫩av分享吧 | 久久久久久电影 | 天天射天天干天天操 | 亚洲香蕉在线视频 | 香蕉精品视频在线观看 | 久久人人爽天天玩人人妻精品 | 欧美熟妇精品一区二区蜜桃视频 | 日韩免费中文字幕 | 午夜天堂在线观看 | 污视频网站免费在线观看 |