ฉันจะทำลิเทอรัล * int64 ใน Go ได้อย่างไร


116

ฉันมีประเภทโครงสร้างที่มี*int64ฟิลด์

type SomeType struct {
    SomeField *int64
}

เมื่อถึงจุดหนึ่งในรหัสของฉันฉันต้องการประกาศตัวอักษรของสิ่งนี้ (เช่นเมื่อฉันรู้ว่าค่าควรเป็น 0 หรือชี้ไปที่ 0 คุณก็รู้ว่าฉันหมายถึงอะไร)

instance := SomeType{
    SomeField: &0,
}

... ยกเว้นไม่ได้ผล

./main.go:xx: cannot use &0 (type *int) as type *int64 in field value

ก็เลยลองทำตามนี้

instance := SomeType{
    SomeField: &int64(0),
}

... แต่ก็ไม่ได้ผลเช่นกัน

./main.go:xx: cannot take the address of int64(0)

ฉันต้องทำอย่างไร ทางออกเดียวที่ฉันคิดได้คือการใช้ตัวแปรตัวยึดตำแหน่ง

var placeholder int64
placeholder = 0

instance := SomeType{
    SomeField: &placeholder,
}

หมายเหตุ: &0ไวยากรณ์ทำงานได้ดีเมื่อเป็น * int แทนที่จะเป็น*int64ไฟล์. แก้ไข: ไม่มันไม่ ขออภัยเกี่ยวกับเรื่องนี้

แก้ไข:

เห็นได้ชัดว่าคำถามของฉันมีความคลุมเครือมากเกินไป ฉันกำลังมองหาวิธีระบุ*int64. สิ่งนี้สามารถใช้ภายในคอนสตรัคเตอร์หรือเพื่อระบุค่าโครงสร้างตามตัวอักษรหรือแม้กระทั่งเป็นอาร์กิวเมนต์ของฟังก์ชันอื่น ๆ แต่ฟังก์ชันตัวช่วยหรือการใช้งานประเภทอื่นไม่ใช่วิธีแก้ปัญหาที่ฉันกำลังมองหา


1
ตัวชี้ไปยัง int เป็นสิ่งที่น่าเสียดายเนื่องจาก int ใช้พื้นที่เท่ากับตัวชี้ดังนั้นคุณจึงไม่ประหยัดพื้นที่ เพียงแค่เพิ่มค่า NULL ซึ่งโดยปกติจะมีความซับซ้อนมากกว่าที่ควรจะเป็น ในกรณีส่วนใหญ่ 0 จะใช้ได้ หากคุณต้องการค่าพิเศษบูล "IsValidSomeField" ก็ใช้งานได้เช่นกันและหากคุณตั้งชื่อบูลที่ดีกว่านั้นก็สามารถบอกได้เพิ่มเติมว่าทำไมคุณถึงต้องการค่าพิเศษนั้นซึ่งเหมาะสำหรับการอ่าน
voutasaurus

3
คุณสามารถใช้แพคเกจตัวชี้ตัวอย่างเช่น:var _ *int64 = pointer.Int64(64)
Xor

นี่เป็นเรื่องน่าเสียดายมากที่เราต้องเขียนฟังก์ชันหรือแม้แต่ไลบรารีเพื่อให้สามารถทำสิ่งนี้ได้
มะพร้าว

คำตอบ:


236

The Go ภาษาสเปกสินค้า ( ผู้ประกอบการที่อยู่ ) ไม่อนุญาตให้ใช้ที่อยู่ของตัวเลขคงที่ (ไม่นั้นuntypedมิได้ของพิมพ์คงที่)

ตัวถูกดำเนินการจะต้องระบุแอดเดรสได้นั่นคือตัวแปรการชี้ทิศทางของตัวชี้หรือการดำเนินการจัดทำดัชนีสไลซ์ หรือตัวเลือกฟิลด์ของตัวดำเนินการโครงสร้างแอดเดรส หรือการดำเนินการจัดทำดัชนีอาร์เรย์ของอาร์เรย์แอดเดรส ยกเว้นข้อกำหนดความสามารถในการระบุตำแหน่งx[ในนิพจน์&x] อาจเป็นตัวอักษรผสม (อาจเป็นวงเล็บ) ก็ได้

สำหรับเหตุผลว่าทำไมถึงไม่ได้รับอนุญาตให้ดูคำถามที่เกี่ยวข้อง: ค้นหาที่อยู่ของอย่างต่อเนื่องในการเดินทาง คำถามที่คล้ายกัน (ไม่อนุญาตให้ใช้ที่อยู่ในทำนองเดียวกัน): ฉันจะจัดเก็บข้อมูลอ้างอิงถึงผลลัพธ์ของการดำเนินการใน Go ได้อย่างไร?

ตัวเลือกของคุณ (ลองทั้งหมดบนGo Playground ):

1) ด้วย new()

คุณสามารถใช้new()ฟังก์ชันbuiltin เพื่อจัดสรรค่าศูนย์ใหม่int64และรับที่อยู่:

instance := SomeType{
    SomeField: new(int64),
}

แต่โปรดทราบว่าสิ่งนี้สามารถใช้เพื่อจัดสรรและรับตัวชี้ไปที่ค่าศูนย์ของประเภทใดก็ได้เท่านั้น

2) ด้วยตัวแปรตัวช่วย

ง่ายที่สุดและแนะนำสำหรับองค์ประกอบที่ไม่ใช่ศูนย์คือการใช้ตัวแปรตัวช่วยที่สามารถรับแอดเดรสได้:

helper := int64(2)
instance2 := SomeType{
    SomeField: &helper,
}

3) ด้วยฟังก์ชันตัวช่วย

หมายเหตุ:ฟังก์ชันตัวช่วยในการรับตัวชี้ไปยังค่าที่ไม่ใช่ศูนย์มีอยู่ในgithub.com/icza/goxไลบรารีของฉันในgoxแพ็กเกจดังนั้นคุณไม่จำเป็นต้องเพิ่มสิ่งเหล่านี้ในโปรเจ็กต์ทั้งหมดที่คุณต้องการ

หรือถ้าคุณต้องการหลาย ๆ ครั้งคุณสามารถสร้างฟังก์ชันตัวช่วยซึ่งจัดสรรและส่งคืน*int64:

func create(x int64) *int64 {
    return &x
}

และใช้มัน:

instance3 := SomeType{
    SomeField: create(3),
}

โปรดทราบว่าเราไม่ได้จัดสรรอะไรเลยคอมไพเลอร์ Go ทำเช่นนั้นเมื่อเราส่งคืนที่อยู่ของอาร์กิวเมนต์ของฟังก์ชัน คอมไพเลอร์ Go ทำการวิเคราะห์ Escape และจัดสรรตัวแปรโลคัลบนฮีป (แทนที่จะเป็นสแต็ก) หากพวกเขาอาจหลบหนีฟังก์ชัน สำหรับรายละเอียดโปรดดูที่การส่งคืนส่วนของอาร์เรย์ในเครื่องในฟังก์ชัน Go ปลอดภัยหรือไม่

4) ด้วยฟังก์ชันไม่ระบุชื่อซับเดียว

instance4 := SomeType{
    SomeField: func() *int64 { i := int64(4); return &i }(),
}

หรือเป็นทางเลือก (สั้นกว่า):

instance4 := SomeType{
    SomeField: func(i int64) *int64 { return &i }(4),
}

5) ด้วยการแบ่งตามตัวอักษรการสร้างดัชนีและการรับที่อยู่

หากคุณต้องการที่*SomeFieldจะเป็นอื่นที่ไม่ใช่0คุณต้องมีสิ่งที่สามารถระบุได้

คุณยังทำได้ แต่น่าเกลียด:

instance5 := SomeType{
    SomeField: &[]int64{5}[0],
}
fmt.Println(*instance2.SomeField) // Prints 5

สิ่งที่เกิดขึ้นที่นี่คือส[]int64ไลซ์ถูกสร้างขึ้นด้วยตัวอักษรโดยมีองค์ประกอบหนึ่ง ( 5) และมีการจัดทำดัชนี (องค์ประกอบที่ 0) และใช้ที่อยู่ขององค์ประกอบที่ 0 ในพื้นหลังอาร์เรย์ของ[1]int64จะถูกจัดสรรและใช้เป็นอาร์เรย์สำรองสำหรับสไลซ์ ที่นี่จึงมีแผ่นสำเร็จรูปมากมาย

6) ด้วยตัวช่วยโครงสร้างตามตัวอักษร

มาตรวจสอบข้อยกเว้นของข้อกำหนดความสามารถในการระบุตำแหน่ง:

ยกเว้นข้อกำหนดความสามารถในการระบุตำแหน่งx[ในนิพจน์&x] อาจเป็นตัวอักษรผสม (อาจเป็นวงเล็บ) ก็ได้

ซึ่งหมายความว่าการใช้แอดเดรสของตัวอักษรผสมเช่นโครงสร้างลิเทอรัลก็ใช้ได้ ถ้าเราทำเช่นนั้นเราจะมีการจัดสรรค่าโครงสร้างและตัวชี้ที่ได้รับ แต่ถ้าเป็นเช่นนั้นความต้องการอื่นจะกลายเป็นมีให้เรา: "เลือกเขตของตัวถูกดำเนินการ struct แอดเดรส" ดังนั้นหากโครงสร้างลิเทอรัลมีฟิลด์ประเภทint64เราสามารถใช้แอดเดรสของฟิลด์นั้นได้ด้วย!

ลองดูตัวเลือกนี้ในการดำเนินการ เราจะใช้ประเภทโครงสร้างของ wrapper นี้:

type intwrapper struct {
    x int64
}

และตอนนี้เราสามารถทำได้:

instance6 := SomeType{
    SomeField: &(&intwrapper{6}).x,
}

โปรดทราบว่าสิ่งนี้

&(&intwrapper{6}).x

หมายถึงสิ่งต่อไปนี้:

& ( (&intwrapper{6}).x )

แต่เราสามารถเว้นวงเล็บ "ด้านนอก" เป็นตัวดำเนินการที่อยู่ได้ &ถูกนำไปใช้ผลของการแสดงออกทางเลือก

โปรดทราบว่าในเบื้องหลังสิ่งต่อไปนี้จะเกิดขึ้น (นี่เป็นไวยากรณ์ที่ถูกต้องด้วย):

&(*(&intwrapper{6})).x

7) ด้วยตัวช่วยที่ไม่ระบุชื่อโครงสร้างตามตัวอักษร

หลักการนี้เหมือนกับกรณี # 6 แต่เรายังสามารถใช้ตัวอักษรโครงสร้างแบบไม่ระบุตัวตนได้ดังนั้นจึงไม่จำเป็นต้องมีการกำหนดประเภทโครงสร้างตัวช่วย / ตัวห่อ:

instance7 := SomeType{
    SomeField: &(&struct{ x int64 }{7}).x,
}

@icza โดยทั่วไปคุณไม่ต้องการให้พวกเขาชี้ไปที่วัตถุเดียวกันฉันไม่ได้บอกว่าคุณจะทำ ฉันแค่ชี้ให้เห็นความแตกต่าง
Ryan Haining

4
@Conslo Constants จะถูกประเมินในเวลาคอมไพล์ ค่าตัวชี้ที่ถูกต้องที่อยู่หน่วยความจำที่ถูกต้องจะมีอยู่ที่รันไทม์เท่านั้นดังนั้นจึงไม่เหมือนกับค่าคงที่
icza

@icza ฉันมีคำถามเกี่ยวกับตัวเลือกที่ใช้ในวิธี # 6 ฉันมีคำถามที่กล่าวถึงในสนามเด็กเล่น URL play.golang.org/p/U9jqOYA6EP (ความคิดเห็นอนุญาตให้มีเพียงไม่กี่ตัวอักษร)
Rajkamal Subramanian

@rajkamal เพียงเพราะ spec ไม่อนุญาต คุณสามารถใช้ที่อยู่ของตัวอักษรผสม (ตัวอย่างแรกของคุณ) แต่คุณไม่สามารถใช้ที่อยู่ของฟิลด์ของลิเทอรัลแบบผสม (ไม่ได้ระบุไว้ในข้อมูลจำเพาะ) อย่างไรก็ตามคุณสามารถใช้แอดเดรสของฟิลด์ตัวแปรชนิดโครงสร้างได้ แต่ไม่ใช่ของคอมโพสิตลิเทอรัล ( "ตัวเลือกฟิลด์ของตัวถูกดำเนินการโครงสร้างแอดเดรส" )
icza

ถ้าคุณอยากขี้เกียจจริงๆนี่คือแพ็คเกจที่มีฟังก์ชั่นตัวช่วย (โซลูชัน # 3) สำหรับแต่ละประเภทดั้งเดิม: godoc.org/github.com/mwielbut/pointy
Matt Wielbut

6

ใช้ฟังก์ชันที่ส่งคืนแอดเดรสของตัวแปร int64 เพื่อแก้ปัญหา

ในโค้ดด้านล่างนี้เราใช้ฟังก์ชันfซึ่งยอมรับจำนวนเต็มและส่งกลับค่าตัวชี้ซึ่งเก็บแอดเดรสของจำนวนเต็ม โดยใช้วิธีนี้เราสามารถแก้ปัญหาข้างต้นได้อย่างง่ายดาย

type myStr struct {
    url *int64
}

func main() {
    f := func(s int64) *int64 {
        return &s
    }
    myStr{
        url: f(12345),
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.