ポインタが初期化されない理由を誰かが説明できますかNULL
?
例:
void test(){
char *buf;
if (!buf)
// whatever
}
if buf
がnullでないため、プログラムはif内にステップインしません。
なぜゴミ箱をオンにした変数、特にメモリ上のゴミ箱を指すポインタが必要なのですか?
ポインタが初期化されない理由を誰かが説明できますかNULL
?
例:
void test(){
char *buf;
if (!buf)
// whatever
}
if buf
がnullでないため、プログラムはif内にステップインしません。
なぜゴミ箱をオンにした変数、特にメモリ上のゴミ箱を指すポインタが必要なのですか?
回答:
ポインター(および他のPODタイプ)を初期化する必要があることは誰でも知っています。
次に、問題は「誰が初期化するか」になります。
基本的に2つの方法があります。
コンパイラが開発者によって明示的に初期化されていない変数を初期化したと仮定します。次に、変数の初期化が簡単ではない状況に遭遇し、開発者が宣言ポイントでそれを行わなかった理由は、何らかの操作を実行してから割り当てる必要があったためです。
これで、変数をNULLに初期化するコードにコンパイラーが追加の命令を追加し、その後、正しい初期化を行うための開発者コードが追加されたという状況になりました。または、他の条件下では、変数は使用されない可能性があります。多くのC ++開発者は、両方の条件下でその追加の指示を犠牲にして反則を叫ぶでしょう。
ちょうど時間ではありません。しかし、スペースも。両方のリソースが非常に貴重で、開発者も諦めたくない環境がたくさんあります。
BUT:初期化を強制する効果をシミュレートできます。ほとんどのコンパイラは、初期化されていない変数について警告します。したがって、私は常に警告レベルを可能な限り最高のレベルに変えます。次に、すべての警告をエラーとして処理するようコンパイラーに指示します。これらの条件下では、ほとんどのコンパイラーは初期化されていないが使用されている変数に対してエラーを生成するため、コードが生成されなくなります。
TC ++ PLのBjarne Stroustrupの引用(特別版p.22):
機能の実装は、それを必要としないプログラムに大きなオーバーヘッドを課すべきではありません。
D
が好きですか。初期化したくない場合は、この構文float f = void;
またはを使用してくださいint* ptr = void;
。現在はデフォルトで初期化されていますが、本当に必要な場合は、コンパイラーによる実行を停止できます。
初期化には時間がかかるからです。そしてC ++では、変数で最初に行うべきことは、明示的に初期化することです。
int * p = & some_int;
または:
int * p = 0;
または:
class A {
public:
A() : p( 0 ) {} // initialise via constructor
private:
int * p;
};
その上、あなたがそれを吹くときの警告があります:あなたのコンパイラーに応じて、「値が割り当てられる前に使用される可能性があります」または同様の表現。
警告付きでコンパイルしますよね?
変数が初期化されていないことが理にかなっている状況はほとんどありませんが、デフォルトの初期化のコストは小さいので、なぜそれを行うのですか?
C ++はC89ではありません。地獄、CでさえC89ではありません。宣言とコードを混在させることができるため、初期化に適した値が得られるまで宣言を延期する必要があります。
ポインタは別のタイプです。int
、char
または他のPODタイプを作成した場合、それはゼロに初期化されないので、なぜポインターが必要なのでしょうか?これは、このようなプログラムを作成する人にとっては不要なオーバーヘッドと考えることができます。
char* pBuf;
if (condition)
{
pBuf = new char[50];
}
else
{
pBuf = m_myMember->buf();
}
初期化することがわかっている場合pBuf
、メソッドの上部で最初に作成するときにプログラムにコストがかかるのはなぜですか?これはオーバーヘッドゼロの原則です。
常にNULLに初期化されるポインターが必要な場合は、C ++テンプレートを使用してその機能をエミュレートできます。
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); }
};
Foo *a
を使用InitializedPointer<Foo> a
- Foo *a=0
タイピングが少ない純粋に学術的な演習。ただし、上記のコードは教育の観点から非常に便利です。(「プレースホルダー」のctor / dtorと代入演算に)少しの変更を加えると、スコープ付きポインター(デストラクターで解放されます)や参照カウントポインター(inc /を追加することなど)を含むさまざまなタイプのスマートポインターに簡単に拡張できます。 m_pPointerが設定またはクリアされたときのdec操作。
静的データは0に初期化されることに注意してください(別の言い方をしない限り)。
そして、はい、常に変数をできるだけ遅く、初期値で宣言する必要があります。のようなコード
int j;
char *foo;
それを読んだときに警報ベルをオフに設定する必要があります。それは100%合法なので、糸くずがそれについて鯉に説得できるかどうかはわかりません。
考えられるもう1つの理由は、リンク時にポインターにアドレスが与えられますが、ポインターの間接的なアドレス指定/逆参照はプログラマーの責任です。たいていの場合、コンパイラーはそれほど気にしませんが、ポインターを管理し、メモリーリークが発生しないようにするために、プログラマーに負担がかかります。
簡単に言えば、リンク時にポインタ変数にアドレスが与えられるという意味で、これらは初期化されます。上記のコード例では、SIGSEGVがクラッシュまたは生成することが保証されています。
健全性のために、ポインターをNULLに初期化します。これは、プログラムを誤動作させない理由をプログラマーに知らせない、malloc
または new
プログラマーの手がかりとなる場合に、ポインターを逆参照しようとする場合です。
これが役に立って意味があるといいのですが、
C ++はCのバックグラウンドから来ています-これから戻ってくるいくつかの理由があります:
Cは、C ++よりも、アセンブリ言語の置き換えです。指示されていないことは何もしません。そのため:あなたがそれを無効にしたいなら-それをしてください!
また、Cのようなベアメタル言語でnullの場合、一貫性に関する質問が自動的に表示されます。mallocを実行すると、自動的にゼロに設定されますか?スタック上に作成された構造体はどうですか?すべてのバイトをゼロにする必要がありますか?グローバル変数はどうですか?「(* 0x18);」のようなステートメントについてはどうでしょうか。これは、メモリ位置0x18をゼロにする必要があることを意味していませんか?
calloc()
。
あなたが話しているこれらの指針は何ですか?
例外安全のため、必ず使用しauto_ptr
、shared_ptr
、weak_ptr
およびその他の変異体。
優れたコードの特徴は、への1回の呼び出しを含まないコードですdelete
。
auto_ptr
して置き換えunique_ptr
ます。
ああ少年。本当の答えは、メモリをゼロにするのは簡単です。これは、たとえばポインタの基本的な初期化です。これは、オブジェクト自体の初期化とは何の関係もありません。
ほとんどのコンパイラーが最高レベルで与える警告を考えると、最高レベルのプログラミングをエラーとして扱うことは想像できません。それらを上げても、生成された大量のコードの1つでもバグを救うことができなかったので、これをお勧めすることはできません。
NULL
それを初期化することは同じくらいエラーになります。