[解決済み] NULLインスタンスに対してメンバ関数を呼び出すと、どのような場合に未定義の動作となるのですか?
質問
以下のコードを考えてみましょう。
#include <iostream>
struct foo
{
// (a):
void bar() { std::cout << "gman was here" << std::endl; }
// (b):
void baz() { x = 5; }
int x;
};
int main()
{
foo* f = 0;
f->bar(); // (a)
f->baz(); // (b)
}
私たちは
(b)
がクラッシュすることを期待します。なぜなら、対応するメンバ
x
に対応するメンバがないからです。実際には
(a)
はクラッシュしないので
this
のポインタは決して使われないので、クラッシュしません。
なぜなら
(b)
は
this
ポインタ (
(*this).x = 5;
)、および
this
が null の場合、null を参照することは常に未定義の動作であると言われているため、 プログラムは未定義の動作に入ります。
は
(a)
は未定義の動作になるのでしょうか?両方の関数(と
x
が静的である場合はどうでしょうか?
どのように解決するのですか?
どちらも
(a)
と
(b)
は未定義の動作になります。nullポインタを介してメンバ関数を呼び出すのは常に未定義の動作です。関数が静的な場合、技術的には同様に未定義ですが、いくつかの論争があります。
最初に理解すべきことは、NULLポインタを参照解除することがなぜ未定義の動作であるのかということです。C++03 では、実際にはここに少し曖昧さがあります。
とはいえ "ヌルポインターを再参照すると、未定義の動作になります" は、§1.9/4 と§8.3.2/4 の両方のノートで言及されていますが、決して明示的に述べられていません。(注釈は非規範的なものです)。
しかし、§3.10/2から推論してみることは可能です。
lvalueはオブジェクトや関数を指します。
デリファレンスする場合、結果はlvalueになります。nullポインタ ではありません。 はオブジェクトを参照しないので、lvalueを使用すると未定義の動作になります。問題は、前の文が述べられていないことです。では、lvalue を "use" するとはどういうことでしょうか?それとも、lvalue から rvalue への変換を実行するという、より正式な意味で使用するのでしょうか?
とにかく、それは間違いなくrvalueに変換することはできません(§4.1/1)。
lvalueが参照するオブジェクトがT型のオブジェクトでなく、Tから派生した型のオブジェクトでもない場合、またはオブジェクトが初期化されていない場合、この変換を必要とするプログラムは未定義の動作をします。
ここでは間違いなく未定義の動作です。
曖昧なのは、deferenceに対するundefined behaviorかどうかという点です。
を使用せず
を使用しない(つまり、l 値を取得するが r 値に変換しない)ことが、未定義の動作であるかどうかに起因しています。もしそうでなければ
int *i = 0; *i; &(*i);
はうまく定義されています。これは
アクティブイシュー
.
つまり、strict "dereference a null pointer, get undefined behavior" ビューと weak "use a dereferenced null pointer, get undefined behavior" ビューを持っているということです。
さて、質問を考えてみましょう。
はい。
(a)
は未定義の動作をもたらします。実際、もし
this
がNULLの場合
の内容に関わらず、関数
の場合、結果は未定義です。
これは§5.2.5/3から続いています。
もし
E1
が "pointer to class X" という型を持っているならば、この式はE1->E2
は等価な形式に変換されます。(*(E1)).E2;
*(E1)
は厳密な解釈では未定義の動作となり
.E2
はrvalueに変換するため、弱解釈では未定義の動作になります。
また、(§9.3.1/1)から直接未定義な動作であることがわかります。
クラスXの非静的メンバ関数が、X型でもXから派生した型でもないオブジェクトに対して呼び出された場合、その動作は未定義である。
静的関数では、厳密な解釈と弱い解釈が違いを生みます。厳密には未定義です。
静的メンバはクラスメンバアクセス構文を用いて参照することができ、その場合、オブジェクト式が評価されます。
つまり、非静的であるかのように評価され、再びNULLポインタの参照を解除して
(*(E1)).E2
.
しかし
E1
は静的メンバ関数呼び出しでは使われないので、弱解釈を使えば呼び出しはうまく定義されます。
*(E1)
が lvalue を生成した場合、静的関数は解決されます。
*(E1)
は破棄され、関数が呼び出されます。lvalueからrvalueへの変換がないため、未定義の動作はありません。
C++0x では、n3126 の時点で、曖昧さが残っています。今のところ、安全であること:厳密な解釈を使用してください。
関連
-
[解決済み】cc1plus:エラー:g++で認識されないコマンドラインオプション"-std=c++11"
-
[解決済み】浮動小数点例外エラーが発生する: 8
-
[解決済み】fpermissiveフラグは何をするのですか?
-
[解決済み】「Expected '(' for function-style cast or type construction」エラーの意味とは?
-
[解決済み】エラー。switchステートメントでcaseラベルにジャンプする
-
[解決済み】エラー:不完全な型へのメンバーアクセス:前方宣言の
-
[解決済み】警告 - 符号付き整数式と符号なし整数式の比較
-
[解決済み】f(i = -1, i = -1)の挙動が未定義なのはなぜ?
-
[解決済み] 強化されたGCC 6オプティマイザは、なぜ実用的なC++コードを壊すのですか?
-
[解決済み] 非アクティブな組合員へのアクセスと未定義の動作?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】構造体のベクター初期化について
-
[解決済み】エラー:strcpyがこのスコープで宣言されていない
-
[解決済み】エラー:不完全な型へのメンバーアクセス:前方宣言の
-
[解決済み】システムが指定されたファイルを見つけられませんでした。
-
[解決済み】指定範囲内の乱数で配列を埋める(C++)
-
[解決済み】C++ - 適切なデフォルトコンストラクタがない [重複]。
-
[解決済み】 while(cin) と while(cin >> num) の違いは何ですか?)
-
[解決済み】変数やフィールドがvoid宣言されている
-
[解決済み] スタックアロケーションにより初期化されていない値が作成された
-
[解決済み】c++で.txtファイルから2次元の配列に読み込む