Edit 2 :
Saya sedang men-debug kegagalan pengujian aneh ketika fungsi yang sebelumnya berada di file sumber C ++ tetapi pindah ke file C kata demi kata, mulai mengembalikan hasil yang salah. MVE di bawah ini memungkinkan untuk mereproduksi masalah dengan GCC. Namun, ketika saya, dengan hati-hati, mengkompilasi contoh dengan Dentang (dan kemudian dengan VS), saya mendapat hasil yang berbeda! Saya tidak tahu apakah akan memperlakukan ini sebagai bug di salah satu kompiler, atau sebagai manifestasi dari hasil yang tidak ditentukan yang diizinkan oleh standar C atau C ++. Anehnya, tidak ada kompiler yang memberi saya peringatan tentang ekspresi.
Pelakunya adalah ungkapan ini:
ctl.b.p52 << 12;
Di sini, p52
diketik sebagai uint64_t
; itu juga merupakan bagian dari persatuan (lihat di control_t
bawah). Operasi shift tidak kehilangan data apa pun karena hasilnya masih sesuai dengan 64 bit. Namun, kemudian GCC memutuskan untuk memotong hasilnya menjadi 52 bit jika saya menggunakan kompiler C ! Dengan kompiler C ++, semua 64 bit hasil dipertahankan.
Untuk menggambarkan ini, contoh program di bawah ini mengkompilasi dua fungsi dengan tubuh yang identik, dan kemudian membandingkan hasilnya. c_behavior()
ditempatkan dalam file sumber C dan cpp_behavior()
dalam file C ++, dan main()
melakukan perbandingan.
Repositori dengan kode contoh: https://github.com/grigory-rechistov/c-cpp-bitfields
Header common.h mendefinisikan gabungan bitfield lebar dan integer 64-bit dan mendeklarasikan dua fungsi:
#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
Fungsi-fungsinya memiliki tubuh yang identik, kecuali bahwa satu diperlakukan sebagai C dan yang lainnya sebagai 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 menunjukkan perbedaan antara hasil yang mereka kembalikan:
$ gcc -Wpedantic main.c c-part.c cpp-part.cpp
$ ./a.exe
OMG C != C++
Namun, dengan Dentang C dan C ++ berperilaku identik dan seperti yang diharapkan:
$ clang -Wpedantic main.c c-part.c cpp-part.cpp
$ ./a.exe
C == C++
Dengan Visual Studio saya mendapatkan hasil yang sama dengan Dentang:
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++
Saya mencoba contoh-contoh di Windows, meskipun masalah asli dengan GCC ditemukan di Linux.
<<
Operator sebagai membutuhkan pemotongan.
main.c
dan mungkin menyebabkan perilaku tidak terdefinisi dalam beberapa cara. IMO akan lebih jelas untuk memposting satu file MRE yang menghasilkan output berbeda ketika dikompilasi dengan masing-masing kompiler. Karena C-C ++ interop tidak ditentukan dengan baik oleh standar. Juga perhatikan bahwa aliasing serikat menyebabkan UB dalam C ++.