편집 2 :
이전에 C ++ 소스 파일에 있던 함수가 C 파일로 그대로 이동하여 잘못된 결과를 반환하기 시작했을 때 이상한 테스트 실패를 디버깅했습니다. 아래의 MVE를 사용하면 GCC 문제를 재현 할 수 있습니다. 그러나 변덕스럽게 Clang (및 나중에 VS로)으로 예제를 컴파일하면 다른 결과를 얻었습니다! 컴파일러 중 하나에서 버그로 처리할지 또는 C 또는 C ++ 표준에서 허용되는 정의되지 않은 결과의 표현으로 처리할지 여부를 알 수 없습니다. 이상하게도, 어떤 컴파일러도 표현식에 대한 경고를주지 못했습니다.
범인은 다음과 같은 표현입니다.
ctl.b.p52 << 12;
여기서는 다음과 p52
같이 입력됩니다 uint64_t
. 또한 노동 조합의 일부이기도합니다 ( control_t
아래 참조). 결과가 여전히 64 비트에 맞기 때문에 시프트 연산은 데이터를 잃지 않습니다. 그러나 GCC는 C 컴파일러를 사용하면 결과를 52 비트로 자르기로 결정합니다 ! C ++ 컴파일러를 사용하면 모든 64 비트 결과가 유지됩니다.
이를 설명하기 위해 아래 예제 프로그램은 동일한 본문으로 두 함수를 컴파일 한 다음 결과를 비교합니다. c_behavior()
C 소스 파일과 cpp_behavior()
C ++ 파일에 있으며 main()
비교를 수행합니다.
예제 코드가있는 저장소 : https://github.com/grigory-rechistov/c-cpp-bitfields
헤더 common.h는 64 비트 와이드 비트 필드와 정수의 결합을 정의하고 다음 두 함수를 선언합니다.
#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
함수는 하나는 C로, 다른 하나는 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 ++ interop은 표준에서 제대로 지정되지 않았기 때문입니다. 또한 유니언 앨리어싱은 C ++에서 UB를 발생시킵니다.