bool
変数を取得し、その2番目のビットを1に設定すると、変数は同時にtrueとfalseに評価されます。次のコードを-g
オプション(gcc-v6.3.0/Linux/RHEL6.0-2016-x86_64/bin/g++ -g main.cpp -o mytest_d
)を指定してgcc6.3でコンパイルし、実行可能ファイルを実行します。あなたは以下を得ます。
Tを同時にtrueとfalseに等しくするにはどうすればよいですか?
value bits
----- ----
T: 1 0001
after bit change
T: 3 0011
T is true
T is false
これは、trueおよびfalseの定義がC++とは異なる別の言語(fortranなど)で関数を呼び出すときに発生する可能性があります。 Fortranでは、いずれかのビットが0でない場合、値はtrueです。すべてのビットが0の場合、値はfalseです。
#include <iostream>
#include <bitset>
using namespace std;
void set_bits_to_1(void* val){
char *x = static_cast<char *>(val);
for (int i = 0; i<2; i++ ){
*x |= (1UL << i);
}
}
int main(int argc,char *argv[])
{
bool T = 3;
cout <<" value bits " <<endl;
cout <<" ----- ---- " <<endl;
cout <<" T: "<< T <<" "<< bitset<4>(T)<<endl;
set_bits_to_1(&T);
bitset<4> bit_T = bitset<4>(T);
cout <<"after bit change"<<endl;
cout <<" T: "<< T <<" "<< bit_T<<endl;
if (T ){
cout <<"T is true" <<endl;
}
if ( T == false){
cout <<"T is false" <<endl;
}
}
/////////////////////////////////// // ifortでコンパイルするとC++と互換性がないFortran関数。
logical*1 function return_true()
implicit none
return_true = 1;
end function return_true
C++では、bool
のビット表現(およびサイズ)は実装定義です。一般に、1または0を可能な値として取るchar
- size型として実装されます。
その値を許可されたものとは異なる値に設定すると(この特定のケースでは、bool
にchar
を介してエイリアスを設定し、そのビット表現を変更することにより)、言語の規則に違反しているため、何かが起こる可能性があります。特に、「壊れた」bool
は、true
とfalse
の両方として(またはtrue
とfalse
の両方として)同時に動作する可能性があることが、標準で明示的に指定されています。
初期化されていない自動オブジェクトの値を調べるなど、この国際標準で「未定義」として記述されている方法で
bool
値を使用すると、true
でもfalse
でもないかのように動作する場合があります。
(C++ 11、[basic.fundamental]、注記47)
この特定のケースでは、 この奇妙な状況でどのように終わったかを見ることができます :最初のif
がコンパイルされます
_ movzx eax, BYTE PTR [rbp-33]
test al, al
je .L22
_
T
をeax
にロードし(拡張子はゼロ)、すべてゼロの場合は印刷をスキップします。代わりに次の場合
_ movzx eax, BYTE PTR [rbp-33]
xor eax, 1
test al, al
je .L23
_
テストif(T == false)
はif(T^1)
に変換され、下位ビットだけが反転します。これは、有効なbool
の場合は問題ありませんが、「壊れた」場合は切り捨てられません。
この奇妙なシーケンスは、最適化レベルが低い場合にのみ生成されることに注意してください。より高いレベルでは、これは一般にゼロ/非ゼロのチェックに要約され、あなたのようなシーケンスは 単一のテスト/条件付きブランチ になる可能性があります。とにかく、他のコンテキストでは奇妙な動作が発生します。 bool
の値を他の整数に合計する場合:
_int foo(bool b, int i) {
return i + b;
}
_
_foo(bool, int):
movzx edi, dil
lea eax, [rdi+rsi]
ret
_
ここで、dil
は0/1であると「信頼されています」。
プログラムがすべてC++の場合、解決策は簡単です。このようにbool
の値を壊さないでください。ビット表現をいじるのを避ければ、すべてうまくいきます。特に、整数からbool
に割り当てた場合でも、コンパイラーは必要なコードを発行して、結果の値が有効なbool
であることを確認します。そのため、_bool T = 3
_は確かに安全であり、T
はtrue
は根性に欠けています。
代わりに、bool
と同じ概念を共有しない他の言語で記述されたコードと相互運用する必要がある場合は、「境界」コードのbool
を避け、適切なサイズの整数としてマーシャリングしてください。それは条件付き&coで動作します。まったく同じです。
免責事項Fortranについて私が知っているのは、今朝私が標準文書で読んだものであり、ブックマークとして使用するFortranリストを含むパンチカードがいくつかあるので、気楽にやってください。
まず、この種の言語の相互運用性は、言語標準の一部ではなく、プラットフォームABIの一部です。 Linux x86-64について話しているので、関連ドキュメントは System V x86-64 ABI です。
まず、C __Bool
_型(3.1.2注†でC++ bool
と同じであると定義されている)がFortran LOGICAL
との互換性を持っていることはどこにも明記されていません。特に、9.2.2では、表9.2は、「プレーン」LOGICAL
が_signed int
_にマップされることを指定しています。 _TYPE*N
_タイプについて
「_
TYPE*N
_」表記は、TYPE
型の変数または集約メンバーがN
バイトのストレージを占有することを指定します。
(同上)
_LOGICAL*1
_に明示的に指定された同等のタイプはなく、それは理解できます。それは標準でさえありません。実際、_LOGICAL*1
_を含むFortranプログラムをFortran 95準拠モードでコンパイルしようとすると、両方についてifortによって警告が表示されます。
_./example.f90(2): warning #6916: Fortran 95 does not allow this length specification. [1]
logical*1, intent(in) :: x
------------^
_
そして努力によって
_./example.f90:2:13:
logical*1, intent(in) :: x
1
Error: GNU Extension: Nonstandard type declaration LOGICAL*1 at (1)
_
そのため、水はすでに混濁しています。したがって、上記の2つのルールを組み合わせて、安全のために_signed char
_を使用します。
ただし:ABIは以下も指定します:
タイプ
LOGICAL
の値は、1として実装された_.TRUE.
_および0として実装された_.FALSE.
_です。
したがって、LOGICAL
値に1と0以外のものを格納するプログラムがある場合、Fortran側の仕様はすでに外れています!あなたは言う:
Fortran _
logical*1
_の表記はbool
と同じですが、fortranではビットが00000011の場合はtrue
であり、C++では未定義です。
この最後のステートメントは真実ではありません。Fortran標準は表現にとらわれず、ABIは反対を明確に述べています。実際、これは実際に LOGICAL
比較のためにgfortの出力をチェックする によって簡単に確認できます。
_integer function logical_compare(x, y)
logical, intent(in) :: x
logical, intent(in) :: y
if (x .eqv. y) then
logical_compare = 12
else
logical_compare = 24
end if
end function logical_compare
_
なる
_logical_compare_:
mov eax, DWORD PTR [rsi]
mov edx, 24
cmp DWORD PTR [rdi], eax
mov eax, 12
cmovne eax, edx
ret
_
最初に正規化せずに、2つの値の間にまっすぐなcmp
があることに気づくでしょう(ifort
とは異なり、この点でより保守的です)。
さらに興味深い:ABIの内容に関係なく、ifortはデフォルトでLOGICAL
に非標準の表現を使用します。これは _-fpscomp logicals
_ スイッチのドキュメントで説明されており、LOGICAL
および言語間の互換性に関する興味深い詳細も指定されています。
ゼロ以外の値の整数はtrueとして扱われ、ゼロの値の整数はfalseとして扱われることを指定します。リテラル定数.TRUE。整数値は1、リテラル定数は.FALSEです。整数値は0です。この表現は、バージョン8.0より前のインテルFortranリリースおよびFortran PowerStationで使用されます。
デフォルトは_
fpscomp nologicals
_で、奇数の整数値(下位ビット1)がtrueとして扱われ、偶数の整数値(下位ビット0)がfalseとして扱われることを指定します。リテラル定数.TRUE。整数値は-1、リテラル定数は.FALSEです。整数値は0です。この表現は、Compaq Visual Fortranで使用されます。 LOGICAL値の内部表現は、Fortran規格では指定されていません。 LOGICALコンテキストで整数値を使用するプログラム、または他の言語で記述されたプロシージャにLOGICAL値を渡すプログラムは、移植性がなく、正しく実行されない可能性があります。インテルは、LOGICAL値の内部表現に依存するコーディング方法を避けることをお勧めします。
(強調を追加)
ここで、LOGICAL
の内部表現は通常問題になりません。私が収集したものから、 "ルールに従って"プレイし、言語の境界を越えない場合は、気づかないでしょう。標準に準拠したプログラムの場合、INTEGER
とLOGICAL
の間に「直接変換」はありません。 INTEGER
をLOGICAL
に押し込める唯一の方法は、TRANSFER
です。これは本質的に移植不可能であり、実際の保証はありません。または、割り当て時の非標準のINTEGER
<-> LOGICAL
変換です。
後者は 文書化されています 常にゼロ以外の結果になるように努力します-> _.TRUE.
_、ゼロ-> _.FALSE.
_、そして あなたは見ることができます これを実現するためにコードが生成されます(レガシー表現を使用した場合の複雑なコードですが)。この方法で任意の整数をLOGICAL
に押し込むようには見えません。
_logical*1 function integer_to_logical(x)
integer, intent(in) :: x
integer_to_logical = x
return
end function integer_to_logical
_
_integer_to_logical_:
mov eax, DWORD PTR [rdi]
test eax, eax
setne al
ret
_
_LOGICAL*1
_の逆変換は、ゼロ拡張(gfort)の整数整数であるため、上記のドキュメントで契約を遵守するには、LOGICAL
の値が0または1であることを明確に想定しています。
しかし、一般的に、これらの変換の状況は ビット混乱 なので、私はそれらから離れます。
つまり、簡単に言えば、INTEGER
データをLOGICAL
の値に入れないでください。これは、Fortranでも悪いので、正しいコンパイラフラグを使用してブール値のABI準拠の表現を取得してください。C/ C++との相互運用性は問題ありません。 。ただし、安全性を高めるために、C++側では単純なchar
を使用します。
最後に、私が収集したもの ドキュメントから には、ブール値を含め、Cとの相互運用性の組み込みサポートがあります。あなたはそれを活用しようとするかもしれません。
言語とコンパイラの両方との契約に違反すると、これが起こります。
「ゼロは偽である」、「ゼロ以外は真である」とどこかで聞いたことがあるでしょう。言語のパラメーターにこだわって、int
をbool
に静的に、またはその逆に静的に変換する場合も同様です。
あなたがビット表現をいじり始めるときそれは保持されません。その場合、契約を破って、(少なくとも)実装定義の動作の領域に入ります。
単にそれをしないでください。
bool
をメモリに格納する方法はあなた次第ではありません。それはコンパイラ次第です。 bool
の値を変更する場合は、true
/false
を割り当てるか、整数を割り当てて、C++が提供する適切な変換メカニズムを使用します。
この方法でbool
を使用する方法に実際に特定のコールアウトを与えるために使用されるC++標準は、いたずらで悪、悪( "bool
値の使用初期化されていない自動オブジェクトの値を調べるなど、このドキュメントで「未定義」と説明されている方法では、true
でもfalse
でもないかのように動作する可能性があります。 ")、ただし 編集上の理由によりC++ 20で削除されました 。