2 संपादित करें :
मैं एक अजीब परीक्षण विफलता डिबग कर रहा था जब एक फ़ंक्शन पहले C ++ स्रोत फ़ाइल में रहता था, लेकिन एक सी फ़ाइल वर्बेटिम में स्थानांतरित हो गया, गलत परिणाम वापस करना शुरू कर दिया। नीचे दिए गए MVE जीसीसी के साथ समस्या को पुन: पेश करने की अनुमति देता है। हालांकि, जब मैंने एक सनक पर, क्लैंग (और बाद में वीएस के साथ) के साथ उदाहरण संकलित किया, तो मुझे एक अलग परिणाम मिला! मैं यह पता नहीं लगा सकता कि संकलकों में से एक में इसे बग के रूप में माना जाए या सी या सी ++ मानक द्वारा अनुमत अपरिभाषित परिणाम के रूप में। अजीब तरह से, किसी भी संकलक ने मुझे अभिव्यक्ति के बारे में कोई चेतावनी नहीं दी।
अपराधी यह अभिव्यक्ति है:
ctl.b.p52 << 12;
यहाँ, के p52
रूप में टाइप किया गया है uint64_t
; यह एक संघ का हिस्सा भी है ( control_t
नीचे देखें)। शिफ्ट ऑपरेशन कोई डेटा नहीं खोता है क्योंकि परिणाम अभी भी 64 बिट्स में फिट बैठता है। हालाँकि, तब जीसीसी 52 बिट्स के लिए परिणाम को कम करने का फैसला करता है अगर मैं सी कंपाइलर का उपयोग करता हूं ! सी ++ संकलक के साथ, परिणाम के सभी 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 ++ माना जाता है।
सी part.c:
#include <stdint.h>
#include "common.h"
uint64_t c_behavior(control_t ctl) {
return ctl.b.p52 << 12;
}
सीपीपी-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 -Wpedantic main.c c-part.c cpp-part.cpp
$ ./a.exe
OMG C != C++
हालांकि, क्लैंग सी और सी ++ के साथ पहचान के अनुसार व्यवहार किया जाता है:
$ clang -Wpedantic main.c c-part.c cpp-part.cpp
$ ./a.exe
C == C++
विजुअल स्टूडियो के साथ मुझे क्लैंग के समान परिणाम प्राप्त होते हैं:
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++
मैंने विंडोज पर उदाहरणों की कोशिश की, भले ही लिनक्स पर जीसीसी के साथ मूल समस्या का पता चला था।
<<
ऑपरेटर को ट्रंकेशन की आवश्यकता के रूप में पढ़ा ।
main.c
और शायद कई तरीकों से अपरिभाषित व्यवहार का कारण बनता है। IMO यह एकल-फ़ाइल MRE पोस्ट करने के लिए स्पष्ट होगा जो प्रत्येक संकलक के साथ संकलित होने पर अलग-अलग आउटपुट का उत्पादन करती है। क्योंकि C-C ++ इंटरॉप मानक द्वारा अच्छी तरह से निर्दिष्ट नहीं है। यह भी ध्यान दें कि यूनियन एलाइज़िंग C ++ में UB का कारण बनता है।