Come definire un oggetto il cui indirizzo è nullo?
-
19-08-2019 - |
Domanda
Mi chiedo come posso definire un oggetto in C il cui riferimento sarà nullo?
// definition of foo
...
void * bar = &foo; // bar must be null
Ci sono alcuni modi che potrei trovare per farlo, ma nessuno soddisfa le mie esigenze.
__attribute__((weak)) extern int foo; //not working with cygwin/gcc 3.4
__attribute__((at(0))) int foo; //only with rvds
#define foo (*(int*) 0) //cannot be embedded in a macro
In realtà, preferirei una soluzione conforme allo standard (c99), ma tutto ciò che funziona sarà ok.
Modificato:Il motivo per farlo è che la barra non sarà sempre nulla.Ecco un esempio più rilevante:
// macro that will define foo to a real object or to *null
DECL(foo);
int * bar = &foo;
if(bar) {
// we can call func
func(bar);
} else {
// bar undefined
exit(-1);
}
Naturalmente questo non è ancora molto rilevante, perché posso usare #if nelle mie condizioni.Il progetto coinvolge infatti grandi strutture, moltissimi file, pochi compilatori, qualche CPU target e molti programmatori che generano bug con una probabilità esponenziale alla complessità della sintassi che utilizzano.È per questo che vorrei una semplice macro per dichiarare il mio oggetto foo.
Soluzione
Stai provando a creare un simbolo con un indirizzo pari a zero. Il tuo ultimo esempio è probabilmente l'unico modo per farlo all'interno del compilatore / linguaggio C.
L'approccio che molto probabilmente risolverà il tuo problema è quello di guardare il file di input al programma linker. La maggior parte dei linker ti consente di definire l'etichetta foo come zero.
In uno script unix ld questo è solo: foo = 0;
Altri suggerimenti
Mi manca qualcosa, ma cosa non funziona su void * bar = NULL
?
Nella tua classe, puoi ignorare l'operatore &
:
class MyClass
{
public:
MyClass() :
m_isNull(true)
{
}
MyClass(int value) :
m_isNull(),
m_value(value)
{
}
int value() const
{
/* If null, throw exception, maybe? */
return m_value;
}
bool isNull() const
{
return m_isNull;
}
/////////////////////////
// Here's the "magic". //
/////////////////////////
MyClass *operator&()
{
if(m_isNull)
return 0;
return this;
}
private:
bool m_isNull;
int m_value;
};
Questo produce un comportamento che un utente di MyClass
probabilmente non si aspetterebbe. Non sono sicuro di dove & Quot; caratteristica & Quot; sarebbe richiesto o addirittura voluto.
Se vuoi prendere l'indirizzo reale di un'istanza boost
, puoi usare MyClass::operator&()
(come suggerito nei commenti a questa risposta):
MyClass foo;
MyClass *fooptr = &foo; // fooptr == NULL
fooptr = boost::addressof(foo); // fooptr = &foo (the real address)
Oppure puoi usare il casting, quindi <=> non viene chiamato:
struct DummyStruct {};
MyClass foo;
MyClass *fooptr = &foo; // fooptr == NULL
fooptr = &reinterpret_cast<DummyStruct>(foo); // fooptr = &foo (the real address)
Quello che vuoi è un riferimento che contiene un indirizzo null. Questa è una pratica estremamente negativa, ma qui va:
#ifdef NON_NULL
Foo realFoo;
Foo &foo = realFoo;
#else
Foo &foo = *(Foo*)NULL;
#endif
Per favore, non farlo in questo modo. auto_ptr potrebbe essere un'opzione migliore.
Ok, deve essere la domanda, ma vuoi avere un puntatore con un valore pari a 0?
che ne dite di
void * bar = (void*) 0;
Non devi fare tutto questo casino riguardo a:un puntatore è semplicemente un numero a cui il compilatore associa un tipo;se vuoi che il numero sia 0, assegna 0.
Oh, e rispondendo ad un'altra domanda, non è che la lingua abbia qualcosa a che fare con questo, è solo che non sai necessariamente cosa si trova nella posizione 0.Abbiamo avuto molti problemi con il codice BSD in passato perché sui primi unix BSD, *0 == 0 era affidabile in modo affidabile.Quindi le persone scriverebbero cose come
while(*c) doSomething();
perché quando hanno dereferenziato 0x00 alla fine di una stringa, hanno guardato LOCATION 0 che aveva VALUE 0.Sfortunatamente, ciò non era necessariamente vero su altre piattaforme.
Dubito davvero che ci sia un modo per farlo nello standard C99. Nel C ++ standard, sarebbe difficile fare riferimento all'oggetto una volta creato, se fosse possibile crearlo, poiché la dereferenziazione di una costante puntatore null non è definita e uno 0 costante integrale nella costante puntatore è una costante puntatore null. (Sì, potresti provare int i; i = 0; foo * p = reinterpret_cast<foo *>i; p->doSomething();
ma lo standard non specifica cosa fa reinterpret_cast & Lt; & Gt; se non in modo vago e dipendente dall'implementazione.)
Quindi, la mia prossima domanda è cosa stai cercando di realizzare qui. Se stai cercando di distorcere la lingua in modi strani e interessanti, ciò non cambia in base allo standard. Se hai un uso legittimo per questo, quale sarebbe?
Penso che tu sia vicino, solo un passo del puntatore indiretto lontano.
A giudicare dall'esempio di codice, non è necessario prendere l'indirizzo di foo
.
Puoi realizzare ciò che desideri creando una funzione che alloca e crea un'istanza bar
.
Quindi invece di:
DECL(foo);
int * bar = &foo;
potresti avere:
#define FOO_IS_NULL 0
int * getFoo()
{
if( FOO_IS_NULL )
{
return 0;
}
int *tmp = malloc(sizeof(int));
*tmp = 1234;
return tmp;
}
int * bar = getFoo();
Nota che questo elimina completamente la variabile free(bar)
.
L'unica avvertenza è che ora è necessario <=>.
Bene, ecco cosa abbiamo:
- C++ consente l'overload
operator&
e dispone di modelli che fanno il lavoro per te, ma non consente di dereferenziare il puntatore nullo. - C consente di dereferenziare il puntatore nullo, a condizione che l'indirizzo venga preso successivamente.Permette anche l'assegnazione
void*
a qualsiasi puntatore a un oggetto.
Beh, è l'ideale.Innanzitutto la parte C, che è molto semplice.Non capisco il tuo punto secondo cui non puoi incorporarlo nelle macro.Funziona bene per me.
#define foo_ (*(void*) 0)
Ora, la parte C++.Inserisci il materiale in nullp.hpp:
struct nullp {
struct proxy {
template<typename T> operator T*() {
return 0;
}
};
proxy operator&() {
return omg();
}
} foo;
Ora, tutto ciò che dobbiamo fare è incollare insieme le cose:
#ifdef __cplusplus
#include "nullp.hpp"
#else
#define foo (*(void*) 0)
#endif
Ora puoi usare &foo
e assegnarlo a qualche puntatore a qualche tipo di oggetto.
Senti, mi dispiace, ma stai rendendo tutto questo troppo difficile e troppo complicato.
Se vuoi chiamare la cosa in C, hai bisogno di un puntatore per funzionare. Quindi, ad esempio, hai
/* ptr to 0-ary function returning int */
int (*func)() = NULL ; /* That's all you need. */
// elsewhere ....
int myfunc() { /* do a good thing */ }
func = myfunc ; /* function name *is* its address */
Ora, nel codice successivo puoi farlo
if(!func)
(*func)();
else
/* function not defined */
Non devi fare confusione con il caricatore e non hai bisogno di --- e non dovresti usare --- eventuali macro zippy a meno che tu davvero davvero hanno una forte ragione per farlo. Questo è tutto standard C.
Non penso che ci sia un modo standard per definire qualcosa che abbia l'indirizzo 0. O piuttosto, non è definito poiché sarebbe quindi possibile dereference 0, che è indefinito (o è specifico della piattaforma?) nello standard .
Cosa stai cercando di fare?
Se stai usando C ++, puoi usare riferimenti:
int *foo = 0;
int &bar = *foo;
int *foo_addr = &bar; // gives NULL