問題タブ [undefined-behavior]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票する
8 に答える
20653 参照

c++ - 左側のオペランドが負の値の場合、左シフト操作が未定義動作を呼び出すのはなぜですか?

Cでは、左側のオペランドが負の値の場合、ビット単位の左シフト演算によって未定義動作が呼び出されます。

ISO C99(6.5.7 / 4)からの関連する引用

E1 << E2の結果は、E1の左シフトE2ビット位置です。空になったビットはゼロで埋められます。E1に符号なしタイプがある場合、結果の値はE1×2 E2であり、結果タイプで表現可能な最大値より1を法として減少します。E1に符号付きタイプと非負の値があり、E1×2 E2が結果タイプで表現可能である場合、それが結果の値です。それ以外の場合、動作は定義されていません。

しかし、C ++では、動作は明確に定義されています。

ISO C ++-03(5.8 / 2)

E1 << E2の値は、E1(ビットパターンとして解釈される)の左シフトされたE2ビット位置です。空のビットはゼロで埋められます。E1がunsigned型の場合、結果の値はE1に数量2を掛けてE2の累乗になり、E1の型がunsignedlongの場合はULONG_MAX+1を法として、それ以外の場合はUINT_MAX+1になります。[注:定数ULONG_MAXおよびUINT_MAXはヘッダーで定義されています)。]

つまり、

Cでは未定義動作を呼び出しますが、動作はC++で明確に定義されています。

ISO C ++委員会が、Cでの動作とは対照的に、その動作が明確に定義されていると見なすようになったのはなぜですか?

一方、implementation defined左オペランドが負の場合の動作はビット単位の右シフト演算ですよね?

私の質問は、なぜ左シフト操作がCで未定義動作を呼び出すのか、そしてなぜ右シフト演算子が実装定義の動作だけを呼び出すのかということです。

PS:「標準がそう言っているので、それは未定義の振る舞いです」のような答えを与えないでください。:P

0 投票する
11 に答える
26583 参照

c - constで定義されたオブジェクトの値をポインターで変更できますか?

それは機能しますか?

0 投票する
2 に答える
3280 参照

java - このステートメントがJavaで機能しないのはなぜですかx^= y ^ = x ^ = y;

値が入れ替わると思いますが、x=0とy=1になります。C言語で試してみると、正しい結果が得られます。

0 投票する
4 に答える
8733 参照

java - Java と C の ++i + ++i + ++i

どちらがより正しいですか? Java の結果は 12 または C = 13 です。または、正確さの問題ではない場合は、詳しく説明してください。

0 投票する
1 に答える
736 参照

c++ - i += ++i は C++0x で未定義の動作ですか?

C++0x に関する限り i = ++i 未定義ではないという説明に非常に納得していますが、 の動作がi += ++i明確に定義されているかどうかを判断することはできません。テイカーはいますか?

0 投票する
13 に答える
34555 参照

c++ - C/C++ での符号付きオーバーフローの検出

一見すると、この質問は整数オーバーフローを検出する方法の重複のように思えるかもしれません。ですが、実際には大きく異なります。

符号なし整数のオーバーフローを検出することは非常に簡単ですが、C/C++ で符号付きオーバーフローを検出すること、実際にはほとんどの人が考えるよりも難しいことがわかりました。

それを行うための最も明白でありながら単純な方法は、次のようなものです。

これに関する問題は、C 標準によると、符号付き整数のオーバーフローが未定義の動作であることです。 言い換えれば、標準によれば、符号付きオーバーフローを引き起こすとすぐに、プログラムは null ポインターを逆参照した場合と同じように無効になります。したがって、未定義の動作を引き起こすことはできず、上記の事後条件チェックの例のように、事後にオーバーフローを検出しようとします。

上記のチェックは多くのコンパイラで機能する可能性がありますが、期待することはできません。実際、C 標準では符号付き整数のオーバーフローは未定義であると述べられているため、最適化フラグが設定されている場合、一部のコンパイラ (GCC など) は上記のチェックを最適化して除外します。これにより、オーバーフローをチェックする試みが完全に中断されます。

したがって、オーバーフローをチェックする別の方法は次のとおりです。

このような加算を実行してもオーバーフローが発生しないことを事前に確認するまで、2 つの整数を実際に加算しないため、これはより有望に思えます。したがって、未定義の動作は発生しません。

ただし、残念ながら、このソリューションは、加算演算が機能するかどうかをテストするためだけに減算演算を実行する必要があるため、最初のソリューションよりも効率が大幅に低下します。この (小さな) パフォーマンス ヒットを気にしないとしても、このソリューションが適切であると完全に確信しているわけではありません。この式lhs <= INT_MIN - rhsは、符号付きオーバーフローが不可能であると考えて、コンパイラが最適化して取り除く可能性のある式とまったく同じように見えます。

ここでより良い解決策はありますか?1) 未定義の動作を引き起こさないこと、および 2) オーバーフロー チェックを最適化する機会をコンパイラに提供しないことが保証されているものはありますか? 両方のオペランドを符号なしにキャストし、独自の 2 の補数演算をローリングしてチェックを実行する方法があるのではないかと考えていましたが、その方法がよくわかりません。

0 投票する
3 に答える
1526 参照

c - C99 では、f()+g() は未定義ですか、それとも単に未指定ですか?

fC99 では、関数とg干渉の副作用があったとしても、式f() + g()にシーケンス ポイントが含まれておらず、シーケンス ポイントが含まれている場合でも、その動作は未規定であるfg考えていました: f() が前に呼び出されるg()、または f() の前に g()。

私はもはや確信が持てません。コンパイラが関数をインライン化し (関数が宣言されていなくてもコンパイラが実行することを決定する場合がありますinline)、命令を並べ替えたらどうなるでしょうか? 上記の2つとは異なる結果が得られるでしょうか? つまり、これは未定義の動作ですか?

これは、私がこのようなことを書こうとしているからではなく、静的アナライザーでそのようなステートメントに最適なラベルを選択するためです。

0 投票する
1 に答える
86 参照

html - HTML コメントの奇妙な動作

私のコードには、ラジオ ボタンのあるフォームがあります。最初のラジオ ボタンは表示されますが、2 番目 (Repeat Every Week) は表示されませんか? コメント マーカーを削除すると、すべて正常に動作します。しかし、なぜそれが起こっているのですか?

0 投票する
5 に答える
8259 参照

c - 中間コピーなしで標準 C で memmove を実装する方法は?

私のシステムのmanページから:

void *memmove(void *dst, const void *src, size_t len);

説明
memmove() 関数は、文字列 src から文字列 dst に len バイトをコピーします。
2 つの文字列が重複する場合があります。コピーは常に非破壊的な方法で行われ
ます。

C99 標準から:

6.5.8.5 2 つのポインターを比較すると、結果は、指しているオブジェクトのアドレス空間内の相対位置によって異なります。オブジェクト型または不完全型への 2 つのポインターが両方とも同じオブジェクトを指している場合、または両方が同じ配列オブジェクトの最後の要素の 1 つ後ろを指している場合、それらは等しいと見なされます。指しているオブジェクトが同じ集合体オブジェクトのメンバーである場合、後で宣言された構造体メンバーへのポインターは、構造体で以前に宣言されたメンバーへのポインターよりも大きく、添字値が大きい配列要素へのポインターは、同じ配列の要素へのポインターよりも大きくなります。より低い添字値で。同じ共用体オブジェクトのメンバーへのすべてのポインターは等しいと比較されます。式の場合P が配列オブジェクトの要素を指し、式 Q が同じ配列オブジェクトの最後の要素を指している場合、ポインター式はQ+1 より大きいと比較 されますP。それ以外の場合、動作は undefinedです。

強調は私のものです。

厳密なエイリアシングの問題を軽減するために、引数dstsrcをポインターに変換できますがchar、同じブロック内を指している場合に正しい順序でコピーを行うために、異なるブロック内を指している可能性のある 2 つのポインターを比較することは可能ですか? ?

明らかな解決策は ですが、 と が異なるブロックを指しているif (src < dst)場合は未定義です。「未定義」とは、条件が 0 または 1 を返すと想定してはならないことを意味します (これは、標準の語彙では「未指定」と呼ばれていました)。srcdst

代替手段はif ((uintptr_t)src < (uintptr_t)dst)であり、これは少なくとも指定されていませんが、src < dstが定義されたときに と同等であることを標準が保証しているかどうかはわかりません(uintptr_t)src < (uintptr_t)dst)。ポインター比較は、ポインター演算から定義されます。たとえば、加算に関するセクション 6.5.6 を読んだとき、ポインター演算はuintptr_t演算と逆の方向に進む可能性がpあるように思えchar*ます。

これはほんの一例です。一般的に言えば、ポインターを整数に変換するときに保証されることはほとんどないようです。

これはmemmove、コンパイラと共に提供されているため、純粋に学術的な質問です。実際には、コンパイラの作成者は、未定義のポインタ比較を未指定の動作に単純に促進するか、関連するプラグマを使用してコンパイラにそれらをmemmove正しくコンパイルさせることができます。たとえば、この実装には次のスニペットがあります。

memmove標準 C で効率的に実装できないことが本当である場合、標準が未定義の動作で行き過ぎていることの証明として、この例を引き続き使用したいと思います

0 投票する
4 に答える
970 参照

c++ - const_cast と UB

$5.2.11/7 - 「[注: オブジェクトの型に応じて、const-qualifier68 をキャストする const_cast から生じるデータ メンバーへのポインター、左辺値、またはポインターを介した書き込み操作は、未定義の動作を引き起こす可能性があります (7.1. 5.1).]"

このセクション (C++03) の文言は、私にとって驚くべきものです。驚くべきことは2つあります。

a) まず、'may' の使い方。なぜ「かもしれない」なのですか?標準の他の場所は、未定義の動作について非常に明確です

b) もともと const オブジェクトの constness をキャストしても、すぐに「未定義の動作」にならないのはなぜですか。UB をトリガーするために書き込みが必要なのはなぜですか?