Quelqu'un peut-il s'il vous plaît expliquer pourquoi les pointeurs ne sont pas initialisés à NULL
?
Exemple:
void test(){
char *buf;
if (!buf)
// whatever
}
Le programme n'interviendrait pas dans le if car buf
n'est pas nul.
J'aimerais savoir pourquoi, dans quel cas avons-nous besoin d'une variable avec une corbeille active, spécialement des pointeurs adressant la corbeille en mémoire?
Nous réalisons tous que le pointeur (et d'autres types de POD) doit être initialisé.
La question devient alors "qui doit les initialiser".
Eh bien, il existe essentiellement deux méthodes:
Supposons que le compilateur initialise toute variable non explicitement initialisée par le développeur. Nous rencontrons ensuite des situations dans lesquelles l’initialisation de la variable n’était pas triviale et où le développeur ne l’a pas fait au point de déclaration parce qu’il/elle avait besoin d’effectuer une opération puis d’affecter.
Nous avons donc maintenant le cas où le compilateur a ajouté une instruction supplémentaire au code qui initialise la variable sur NULL, puis que le code du développeur est ajouté pour effectuer la bonne initialisation. Ou dans d'autres conditions, la variable est potentiellement jamais utilisée. Beaucoup de développeurs C++ crieraient dans les deux cas au prix de cette instruction supplémentaire.
Ce n'est pas qu'une question de temps. Mais aussi de l'espace. Il existe de nombreux environnements dans lesquels les deux ressources sont limitées et les développeurs ne veulent pas abandonner non plus.
MAIS : Vous pouvez simuler l'effet de l'initialisation forcée. La plupart des compilateurs vous avertiront des variables non initialisées. Donc, je tourne toujours mon niveau d'avertissement au plus haut niveau possible. Ensuite, demandez au compilateur de traiter tous les avertissements comme des erreurs. Dans ces conditions, la plupart des compilateurs généreront alors une erreur pour les variables non initialisées mais utilisées, empêchant ainsi la génération de code.
Citant Bjarne Stroustrup dans TC++ PL (Édition spéciale p.22):
L'implémentation d'une fonctionnalité ne doit pas imposer de frais généraux importants aux programmes qui n'en ont pas besoin.
Parce que l'initialisation prend du temps. Et en C++, la première chose à faire avec une variable est de l'initialiser explicitement:
int * p = & some_int;
ou:
int * p = 0;
ou:
class A {
public:
A() : p( 0 ) {} // initialise via constructor
private:
int * p;
};
Parce qu'une des devises du C++ est:
Vous ne payez pas pour ce que vous n'utilisez pas
Pour cette raison même, le operator[]
de la classe vector
ne vérifie pas par exemple si l'index est hors limites.
Pour des raisons historiques, principalement parce que c’est comme cela que c’est fait dans C. C’est une autre question, mais je pense que le principe de la surcharge zéro était en quelque sorte impliqué dans cette décision de conception. .
En outre, nous avons un avertissement pour le moment où vous exploser: "est éventuellement utilisé avant une valeur assignée" ou un verbage similaire selon votre compilateur.
Vous compilez avec des avertissements, non?
Il existe très peu de situations dans lesquelles il est logique qu'une variable soit non initialisée, et l'initialisation par défaut a un faible coût, alors pourquoi le faire?
C++ n'est pas C89. Bon Dieu, même C n'est pas C89. Vous pouvez mélanger les déclarations et le code. Vous devez donc différer la déclaration jusqu'à ce que vous disposiez d'une valeur appropriée pour l'initialisation.
Un pointeur est juste un autre type. Si vous créez un int
, char
ou tout autre type de POD, il n'est pas initialisé à zéro, alors pourquoi un pointeur? Cela pourrait être considéré comme une surcharge inutile pour quelqu'un qui écrit un programme comme celui-ci.
char* pBuf;
if (condition)
{
pBuf = new char[50];
}
else
{
pBuf = m_myMember->buf();
}
Si vous savez que vous allez l'initialiser, pourquoi le programme devrait-il engendrer des coûts lorsque vous créez pour la première fois pBuf
en haut de la méthode? C’est le principe zéro frais généraux.
Si vous souhaitez un pointeur toujours initialisé sur NULL, vous pouvez utiliser un modèle C++ pour émuler cette fonctionnalité:
template<typename T> class InitializedPointer
{
public:
typedef T TObj;
typedef TObj *PObj;
protected:
PObj m_pPointer;
public:
// Constructors / Destructor
inline InitializedPointer() { m_pPointer=0; }
inline InitializedPointer(PObj InPointer) { m_pPointer = InPointer; }
inline InitializedPointer(const InitializedPointer& oCopy)
{ m_pPointer = oCopy.m_pPointer; }
inline ~InitializedPointer() { m_pPointer=0; }
inline PObj GetPointer() const { return (m_pPointer); }
inline void SetPointer(PObj InPtr) { m_pPointer = InPtr; }
// Operator Overloads
inline InitializedPointer& operator = (PObj InPtr)
{ SetPointer(InPtr); return(*this); }
inline InitializedPointer& operator = (const InitializedPointer& InPtr)
{ SetPointer(InPtr.m_pPointer); return(*this); }
inline PObj operator ->() const { return (m_pPointer); }
inline TObj &operator *() const { return (*m_pPointer); }
inline bool operator!=(PObj pOther) const
{ return(m_pPointer!=pOther); }
inline bool operator==(PObj pOther) const
{ return(m_pPointer==pOther); }
inline bool operator!=(const InitializedPointer& InPtr) const
{ return(m_pPointer!=InPtr.m_pPointer); }
inline bool operator==(const InitializedPointer& InPtr) const
{ return(m_pPointer==InPtr.m_pPointer); }
inline bool operator<=(PObj pOther) const
{ return(m_pPointer<=pOther); }
inline bool operator>=(PObj pOther) const
{ return(m_pPointer>=pOther); }
inline bool operator<=(const InitializedPointer& InPtr) const
{ return(m_pPointer<=InPtr.m_pPointer); }
inline bool operator>=(const InitializedPointer& InPtr) const
{ return(m_pPointer>=InPtr.m_pPointer); }
inline bool operator<(PObj pOther) const
{ return(m_pPointer<pOther); }
inline bool operator>(PObj pOther) const
{ return(m_pPointer>pOther); }
inline bool operator<(const InitializedPointer& InPtr) const
{ return(m_pPointer<InPtr.m_pPointer); }
inline bool operator>(const InitializedPointer& InPtr) const
{ return(m_pPointer>InPtr.m_pPointer); }
};
Notez que les données statiques sont initialisées à 0 (sauf indication contraire de votre part).
Et oui, vous devez toujours déclarer vos variables le plus tard possible et avec une valeur initiale. Code comme
int j;
char *foo;
devrait déclencher des sonneries d'alarme lorsque vous le lisez. Je ne sais pas si les charpie s peuvent être persuadés d'en parler car c'est 100% légal.
Une autre raison possible est qu’au moment de la connexion, les pointeurs se voient attribuer une adresse, mais l’adressage indirect/le dé-référencement d’un pointeur incombe au programmeur. D'ordinaire, le compilateur s'en fiche, mais le programmeur a la charge de gérer les pointeurs et de s'assurer qu'aucune fuite de mémoire ne se produit.
En réalité, ils sont initialisés en ce sens qu’au moment du lien, la variable du pointeur reçoit une adresse. Dans votre exemple de code ci-dessus, il est garanti que cela plantera ou générera un SIGSEGV.
Pour des raisons de sécurité, initialisez toujours les pointeurs sur NULL. Ainsi, toute tentative de le déréférencer sans malloc
ou new
indiquera au programmeur la raison pour laquelle le programme s'est mal comporté.
J'espère que cela aide et a du sens,
Eh bien, si C++ initialisait les pointeurs, les personnes du C qui se plaignaient du fait que "C++ est plus lent que C" auraient quelque chose de réel sur lequel se raccrocher;)
C++ vient de C - et quelques raisons en découlent:
C, encore plus que C++, est un remplacement du langage Assembly. Il ne fait rien que vous ne lui dites pas de faire. Par conséquent, si vous voulez annuler, faites-le!
En outre, si vous supprimez des éléments dans un langage brut comme C, des questions de cohérence surgissent automatiquement: si vous malloc quelque chose, doit-il être automatiquement mis à zéro? Qu'en est-il d'une structure créée sur la pile? tous les octets doivent-ils être mis à zéro? Qu'en est-il des variables globales? Qu'en est-il d'une déclaration du type "(* 0x18);" cela ne signifie-t-il pas alors que la position de mémoire 0x18 doit être mise à zéro?
Quels sont ces indicateurs dont vous parlez?
Pour la sécurité des exceptions, utilisez toujours auto_ptr
, shared_ptr
, weak_ptr
et leurs autres variantes.
Une caractéristique d'un bon code est celle qui n'inclut pas un seul appel à delete
.