何をすべてに共通の未定義の挙動とC++プログラマにとって知っておくべきこ?[定休日]
-
21-08-2019 - |
質問
何をすべてに共通の未定義の挙動とC++プログラマにとって知っておくべきこ?
言う:
a[i] = i++;
解決
ポインタ
- Dereferencing、
NULL
ポインタ - Dereferencingのポインタによって返される"新しい"配分のサイズはゼロに
- 利用のポインタをオブジェの寿命が終了した場合(例えば、スタックメモリ確保されるオブジェクトの削除オブジェクト)
- Dereferencingのポインタがいても初期化され
- 演算のポインタ利結果、外部境界(上または下)の配列になります。
- Dereferencingのポインタ位置の配列。
- 変換ポインタをオブジェを接種類
- を使用
memcpy
コピー重複するバッファ.
バッファがオーバーフローする
- 読み取りまたは書き込み、オブジェクト配列のオフセットが負、または超えるサイズのオブジェクト(スタック/ヒープオーバーフロー)
整数であふれ
- 符号付き整数オーバーフロー
- 式を評価する際には数学的に定義され
- 左シフト値が負の金額(右シフトによる負の金額は実装で定義される)
- 移転価値額以上のビット数の番号(例:
int64_t i = 1; i <<= 72
定義されません)
種キャストのConst
- 鋳造数値に値することにより表される対象種は、直接またはstatic_cast)
- 自動変数として絶対に割り当て(例えば、
int i; i++; cout << i;
) - の値を使用して他の型のオブジェクト以外の
volatile
またはsig_atomic_t
の受信号 - る修正の文字列リテラルはその他の定数オブジェクトの有効期間は一
- 結狭い幅広い文字列リテラル中の前処理
機能テンプレート
- 返さない値からの価値を還元機能を直接流れるからtryブロック)
- 複数の異なる定義と同じエンティティ(クラス、テンプレートの列挙で、インライン機能、静的加機能等)
- 無限再帰をインスタンス生成のテンプレート
- 呼び出し機能異なるパラメータを使ってや連携のパラメータと連携する機能を定義します。
OOP
- カスケード破壊のオブジェクトの静的保存期間
- 結果の提供の一部と重なるオブジェ
- 再帰的に再入力機能を初期化の際に、その静物
- 決仮想関数呼び出しは、純粋な仮想関数のオブジェクトからそのコンストラクタまたはデストラクタ
- を参照しnonstatic員のオブジェクトの施工されていないまたは既に破壊
ソースファイルおよび前処理
- 空のソースファイルが終わらないと改行が終わるか、又はバックスラッシュ(以前のC++11)
- バックスラッシュの後にある文字は、指定された逃コードの文字または文字列定数(これは実装で定義されたC++11).
- を超える実装の制限(numberの入れ子のブロック関数の数のプログラムのご利用スタックスペース---)
- プリプロセッサの数値とはできないかで表される
long int
- 前処理のディレクティブの左側の機能のマクロ定義
- 動的に生成定義されたトークンに
#if
表現
分類
- 呼び出し口の破壊プログラムの静的ストレージ期間
他のヒント
関数のパラメータが評価される順序は、の の未指定の行動のです。 (これは、あなたのプログラムをクラッシュさせ、爆発、または注文ピザ...とは違っの の未定義の行動のではないでしょう。)
唯一の要件は、関数が呼び出される前に、すべてのパラメータが完全に評価されなければならないということです。
<時間>この:
// The simple obvious one.
callFunc(getA(),getB());
これに相当することができます:
int a = getA();
int b = getB();
callFunc(a,b);
またはこの:
int b = getB();
int a = getA();
callFunc(a,b);
これは、いずれかになります。それはコンパイラ次第です。結果は、副作用に応じて、重要でできます。
コンパイラは、再オーダー式の評価部分を(意味が変わらないと仮定して)。
自由に元の質問から:
a[i] = i++;
// This expression has three parts:
(a) a[i]
(b) i++
(c) Assign (b) to (a)
// (c) is guaranteed to happen after (a) and (b)
// But (a) and (b) can be done in either order.
// See n2521 Section 5.17
// (b) increments i but returns the original value.
// See n2521 Section 5.2.6
// Thus this expression can be written as:
int rhs = i++;
int lhs& = a[i];
lhs = rhs;
// or
int lhs& = a[i];
int rhs = i++;
lhs = rhs;
ダブルロックをチェック。 そして作るために1つの簡単な間違います。
A* a = new A("plop");
// Looks simple enough.
// But this can be split into three parts.
(a) allocate Memory
(b) Call constructor
(c) Assign value to 'a'
// No problem here:
// The compiler is allowed to do this:
(a) allocate Memory
(c) Assign value to 'a'
(b) Call constructor.
// This is because the whole thing is between two sequence points.
// So what is the big deal.
// Simple Double checked lock. (I know there are many other problems with this).
if (a == null) // (Point B)
{
Lock lock(mutex);
if (a == null)
{
a = new A("Plop"); // (Point A).
}
}
a->doStuff();
// Think of this situation.
// Thread 1: Reaches point A. Executes (a)(c)
// Thread 1: Is about to do (b) and gets unscheduled.
// Thread 2: Reaches point B. It can now skip the if block
// Remember (c) has been done thus 'a' is not NULL.
// But the memory has not been initialized.
// Thread 2 now executes doStuff() on an uninitialized variable.
// The solution to this problem is to move the assignment of 'a'
// To the other side of the sequence point.
if (a == null) // (Point B)
{
Lock lock(mutex);
if (a == null)
{
A* tmp = new A("Plop"); // (Point A).
a = tmp;
}
}
a->doStuff();
// Of course there are still other problems because of C++ support for
// threads. But hopefully these are addresses in the next standard.
私のお気に入りは、私はそれが未定義の動作は、コンパイル時にのみ発生一つだと考えているので、「テンプレートのインスタンス化で無限再帰」です。
const
を用いconst_cast<>
nessストリッピングした後に一定に割り当て
const int i = 10;
int *p = const_cast<int*>( &i );
*p = 1234; //Undefined
の未定義の動作はの、また均等厄介あるほかの実装定義の動作の
プログラムは、結果が規格で規定されていない何かをするとき、未定義の動作が発生します。
実装定義の動作は、標準によって定義されていない結果れたプログラムによって動作が、実装は文書化する必要とされます。例では、スタックオーバーフロー質問のから「マルチバイト文字リテラル」であり、これをコンパイルに失敗したCコンパイラはの<?あります/ em>の
あなたが移植起動時に処理系定義の動作だけであなたを噛む(ただし、コンパイラにも移植されたの新しいバージョンにアップグレード!)
変数は、式(技術的に一度配列点の間)に一度更新されてもよい。
int i =1;
i = ++i;
// Undefined. Assignment to 'i' twice in the same expression.
基本的な理解する様々な環境です。の完全なリストは部5.2.4.1の仕様となります。ここにいくつかの例があります;
- 127パラメータの一つの機能の定義
- 127引数を一度にひとつの
- 127パラメータを一つのマクロ定義
- 127引数を一つのマクロ呼び出し
- 4095文字の論理的なソースライン
- 4095文字を文字列 リテラルは全文字列リテラル(後 連結)
- 65535バイトをする オブジェクト(主催の環境のみ)
- 15nestingレベル#includedfiles
- 1023場合はラベルスイッチ 声明を除く anynested換算)
たんに驚いてい限1023場合はラベルスイッチを書きforseeる超過のために発生するコードlex/パーサはかなりeasially.
これらの限界を超えていません(クラッシュ、安全上の欠陥など)です。
右、ごきげんよう、トメ子ですから、C仕様ですが、C++株式これらの基本対応しています。
の重複メモリ領域間でコピーするmemcpy
を使用しました。たとえばます:
char a[256] = {};
memcpy(a, a, sizeof(a));
動作はC ++ 03標準により包含されるC規格に従って定義されていない。
7.21.2.1 memcpy関数
概要
1 /の#includeのvoid *のmemcpyを(void *型S1を制限し、CONST void *型S2、size_tのn)を制限します。
説明
2 / memcpy関数 オブジェクトのコピーn個の文字がオブジェクトにS2で指さ s1で指さ。コピーが重複するオブジェクト間で行われる場合には、 動作は未定義です。戻り値3 memcpy関数を返します S1の値ます。
7.21.2.2 MEMMOVE機能
概要
1の#include void *型MEMMOVE(ボイド* s1を、CONST void *型S2、size_tの N)
説明
オブジェクトから2つのMEMMOVE関数コピーn文字は指さ s2がオブジェクトにs1で指さ。コピーがあるかのように行われます オブジェクトからn文字が最初にコピーされ、S2で指さ オブジェクトが重なっていないn文字の一時的な配列 s1とs2で指さ、一時から、その後のn文字 アレイは、オブジェクトにコピーされ、S1で指さ。戻り値
3 MEMMOVE関数はS1の値を返します。
サイズを保証するC ++のための唯一のタイプはchar
です。そして、大きさは、他のすべてのタイプの1サイズは、プラットフォームに依存している。
ネームスペースレベルのオブジェクトは、初期化のために互いに依存することはありません。