Come si esegue un letterale * int64 in Go?


103

Ho un tipo di struttura con un *int64campo.

type SomeType struct {
    SomeField *int64
}

Ad un certo punto nel mio codice, voglio dichiarare un letterale di questo (ad esempio, quando so che il valore dovrebbe essere 0, o che punta a 0, sai cosa intendo)

instance := SomeType{
    SomeField: &0,
}

... tranne che questo non funziona

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

Quindi provo questo

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

... ma anche questo non funziona

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

Come faccio a fare questo? L'unica soluzione che posso trovare è usare una variabile segnaposto

var placeholder int64
placeholder = 0

instance := SomeType{
    SomeField: &placeholder,
}

Nota: la &0sintassi funziona bene quando è un * int invece di un *int64. Modifica: no non lo fa. Scusa per questo.

Modificare:

A quanto pare c'era troppa ambiguità nella mia domanda. Sto cercando un modo per affermare letteralmente a *int64. Questo potrebbe essere usato all'interno di un costruttore, o per indicare valori di struttura letterali, o anche come argomenti per altre funzioni. Ma le funzioni di supporto o l'utilizzo di un tipo diverso non sono soluzioni che sto cercando.


1
I puntatori a int sono sfortunati in quanto l'int occupa la stessa quantità di spazio di un puntatore, quindi non stai risparmiando spazio. Aggiunge solo un valore NULL che di solito è solo più complessità di quanto valga. Nella maggior parte dei casi uno 0 andrebbe bene. Se hai bisogno di un valore extra, funziona anche un bool "IsValidSomeField" e se dai a quel bool un nome migliore può dire di più sul motivo per cui hai bisogno di quel valore extra, che è buono per la leggibilità.
voutasaurus

2
È possibile utilizzare il puntatore del pacchetto , ad esempio:var _ *int64 = pointer.Int64(64)
Xor

Risposte:


212

La specifica del linguaggio Go ( operatori di indirizzo ) non consente di prendere l'indirizzo di una costante numerica (non di una costante non tipizzata né di una costante tipizzata ).

L'operando deve essere indirizzabile , ovvero un'operazione di indirizzamento di una variabile, di un puntatore indiretto o di una sezione; o un selettore di campo di uno struct operando indirizzabile; o un'operazione di indicizzazione di un array di un array indirizzabile. Come eccezione al requisito di indirizzabilità, x[nell'espressione di &x] può anche essere un letterale composto (possibilmente tra parentesi) .

Per capire perché ciò non è consentito, vedere la domanda correlata: Trova l'indirizzo della costante in go . Una domanda simile (allo stesso modo non è consentito prendere il suo indirizzo): come posso memorizzare il riferimento al risultato di un'operazione in Go?

Le tue opzioni (prova tutte su Go Playground ):

1) Con new()

Puoi semplicemente usare la new()funzione incorporata per allocare un nuovo valore zero int64e ottenere il suo indirizzo:

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

Ma si noti che questo può essere utilizzato solo per allocare e ottenere un puntatore al valore zero di qualsiasi tipo.

2) Con variabile helper

Il più semplice e consigliato per elementi diversi da zero è utilizzare una variabile di supporto il cui indirizzo può essere preso:

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

3) Con la funzione di aiuto

Nota: le funzioni di supporto per acquisire un puntatore a un valore diverso da zero sono disponibili nella mia github.com/icza/goxlibreria, ingox pacchetto, quindi non devi aggiungerle a tutti i tuoi progetti dove ne hai bisogno.

Oppure, se ne hai bisogno molte volte, puoi creare una funzione di supporto che alloca e restituisce un *int64:

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

E usandolo:

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

Nota che in realtà non abbiamo allocato nulla, il compilatore Go lo ha fatto quando abbiamo restituito l'indirizzo dell'argomento della funzione. Il compilatore Go esegue l'analisi di escape e alloca le variabili locali sull'heap (invece che sullo stack) se possono sfuggire alla funzione. Per i dettagli, vedere La restituzione di una porzione di un array locale in una funzione Go è sicura?

4) Con una funzione anonima di una riga

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

O come alternativa (più breve):

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

5) Con slice literal, indicizzazione e presa indirizzo

Se vuoi *SomeFieldessere diverso da0 , allora hai bisogno di qualcosa di indirizzabile.

Puoi ancora farlo, ma è brutto:

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

Quello che succede qui è che uno []int64slice viene creato con un letterale, con un elemento ( 5). Ed è indicizzato (0 ° elemento) e viene preso l'indirizzo dello 0 ° elemento. Sullo sfondo [1]int64verrà anche allocato un array di e utilizzato come array di supporto per lo slice. Quindi qui c'è un sacco di boilerplate.

6) Con un helper struct literal

Esaminiamo l'eccezione ai requisiti di indirizzabilità:

Come eccezione al requisito di indirizzabilità, x[nell'espressione di &x] può anche essere un letterale composto (possibilmente tra parentesi) .

Ciò significa che prendere l'indirizzo di un letterale composto, ad esempio uno struct literal è ok. Se lo facciamo, avremo il valore della struttura allocato e un puntatore ottenuto ad esso. Ma se è così, un altro requisito diventerà disponibile per noi: "selettore di campo di uno struct operando indirizzabile" . Quindi se la struttura letterale contiene un campo di tipo int64, possiamo anche prendere l'indirizzo di quel campo!

Vediamo questa opzione in azione. Useremo questo tipo di struttura wrapper:

type intwrapper struct {
    x int64
}

E ora possiamo fare:

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

Nota che questo

&(&intwrapper{6}).x

significa quanto segue:

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

Ma possiamo omettere la parentesi "esterna" quando l'operatore indirizzo &viene applicato al risultato dell'espressione del selettore .

Si noti inoltre che in background accadrà quanto segue (anche questa è una sintassi valida):

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

7) Con helper anonymous struct literal

Il principio è lo stesso del caso # 6, ma possiamo anche usare uno struct letterale anonimo, quindi non è necessaria la definizione del tipo di struttura helper / wrapper:

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

1
questo è funzionalmente diverso dall'ultimo esempio nella domanda con il segnaposto, poiché due instanceoggetti diversi punterebbero a due diversi int64s. Ma sembra soddisfare adeguatamente le intenzioni degli OP
Ryan Haining l'

2
questo sicuramente risponde alla mia domanda. Ma mi fa abbastanza sconvolto: &[]int64{2}[0], mi sento come in base alla descrizione delle costanti in blog.golang.org/constants questo dovrebbe proprio lavoro &0.
ThisGuy

@ RyanHaining E cosa succederebbe se funzionasse come se fosse assegnato lo stesso indirizzo? Se due instanceoggetti diversi int64puntassero allo stesso e uno modificasse l'oggetto appuntito, cambierebbero entrambi. E se ora usi lo stesso letterale per creare un terzo instance? Lo stesso indirizzo ora punta a un int64valore diverso ... quindi dovrebbe essere usato un indirizzo diverso, ma allora perché usare lo stesso nel caso dei primi 2?
icza

@icza in genere non vorresti che puntassero allo stesso oggetto, non sto dicendo che lo faresti. Sto solo sottolineando la differenza.
Ryan Haining

4
Le costanti @Conslo vengono valutate in fase di compilazione. Un valore puntatore valido, un indirizzo di memoria valido esiste solo in fase di esecuzione, quindi non è la stessa cosa delle costanti.
icza

6

Utilizzare una funzione che restituisca un indirizzo di una variabile int64 per risolvere il problema.

Nel codice seguente usiamo la funzione fche accetta un numero intero e restituisce un valore del puntatore che contiene l'indirizzo dell'intero. Utilizzando questo metodo possiamo facilmente risolvere il problema di cui sopra.

type myStr struct {
    url *int64
}

func main() {
    f := func(s int64) *int64 {
        return &s
    }
    myStr{
        url: f(12345),
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.