編集2:
以前にC ++ソースファイルに存在していた関数がそのままCファイルに移動され、誤った結果が返され始めたときに、奇妙なテストエラーをデバッグしていました。以下のMVEにより、GCCでの問題を再現できます。しかし、気まぐれでClangを使用して(そして後でVSを使用して)例をコンパイルすると、異なる結果が得られました!これをコンパイラの1つのバグとして扱うのか、CまたはC ++標準で許可されている未定義の結果の明示として扱うのか、私にはわかりません。奇妙なことに、どのコンパイラーも式に関する警告を出しませんでした。
犯人はこの表現です:
ctl.b.p52 << 12;
ここでp52
は、と入力されuint64_t
ます。組合の一部でもありcontrol_t
ます(下記参照)。結果は64ビットに収まるため、シフト操作はデータを失わない。ただし、Cコンパイラを使用している場合、GCCは結果を52ビットに切り捨てることを決定します。C ++コンパイラでは、結果の64ビットがすべて保持されます。
これを説明するために、以下のサンプルプログラムは、同一の本体を持つ2つの関数をコンパイルし、それらの結果を比較します。c_behavior()
Cソースファイルとcpp_behavior()
C ++ファイルに配置されmain()
、比較を行います。
サンプルコードを含むリポジトリ:https : //github.com/grigory-rechistov/c-cpp-bitfields
ヘッダーcommon.hは、64ビット幅のビットフィールドと整数の和集合を定義し、2つの関数を宣言します。
#ifndef COMMON_H
#define COMMON_H
#include <stdint.h>
typedef union control {
uint64_t q;
struct {
uint64_t a: 1;
uint64_t b: 1;
uint64_t c: 1;
uint64_t d: 1;
uint64_t e: 1;
uint64_t f: 1;
uint64_t g: 4;
uint64_t h: 1;
uint64_t i: 1;
uint64_t p52: 52;
} b;
} control_t;
#ifdef __cplusplus
extern "C" {
#endif
uint64_t cpp_behavior(control_t ctl);
uint64_t c_behavior(control_t ctl);
#ifdef __cplusplus
}
#endif
#endif // COMMON_H
関数の本体は同じですが、1つはCとして扱われ、もう1つはC ++として扱われます。
c-part.c:
#include <stdint.h>
#include "common.h"
uint64_t c_behavior(control_t ctl) {
return ctl.b.p52 << 12;
}
cpp-part.cpp:
#include <stdint.h>
#include "common.h"
uint64_t cpp_behavior(control_t ctl) {
return ctl.b.p52 << 12;
}
main.c:
#include <stdio.h>
#include "common.h"
int main() {
control_t ctl;
ctl.q = 0xfffffffd80236000ull;
uint64_t c_res = c_behavior(ctl);
uint64_t cpp_res = cpp_behavior(ctl);
const char *announce = c_res == cpp_res? "C == C++" : "OMG C != C++";
printf("%s\n", announce);
return c_res == cpp_res? 0: 1;
}
GCCは、それらが返す結果の違いを示しています。
$ gcc -Wpedantic main.c c-part.c cpp-part.cpp
$ ./a.exe
OMG C != C++
ただし、Clangでは、CとC ++は同じように動作し、期待どおりに動作します。
$ clang -Wpedantic main.c c-part.c cpp-part.cpp
$ ./a.exe
C == C++
Visual Studioでは、Clangと同じ結果が得られます。
C:\Users\user\Documents>cl main.c c-part.c cpp-part.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24234.1 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
main.c
c-part.c
Generating Code...
Compiling...
cpp-part.cpp
Generating Code...
Microsoft (R) Incremental Linker Version 14.00.24234.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:main.exe
main.obj
c-part.obj
cpp-part.obj
C:\Users\user\Documents>main.exe
C == C++
LinuxでGCCの元の問題が発見されたにもかかわらず、Windowsで例を試しました。
main.c
、いくつかの点で未定義の動作があり、未定義の可能性があります。IMO各コンパイラでコンパイルしたときに異なる出力を生成する単一ファイルのMREをポストする方が明確です。C-C ++の相互運用性が標準で適切に指定されていないためです。また、ユニオンエイリアスはC ++でUBを引き起こすことに注意してください。