क्या रनिंग के समय प्रोग्राम को तोड़ना किसी लापता #include के लिए संभव है?


31

क्या ऐसा कोई मामला है, जहां लापता होने #includeसे सॉफ़्टवेयर रनटाइम पर टूट जाएगा, जबकि निर्माण अभी भी हो रहा है?

दूसरे शब्दों में, क्या यह संभव है

#include "some/code.h"
complexLogic();
cleverAlgorithms();

तथा

complexLogic();
cleverAlgorithms();

दोनों सफलतापूर्वक निर्माण करेंगे, लेकिन अलग तरह से व्यवहार करेंगे?


1
संभवतः आपके शामिल किए जाने के साथ आप अपने कोड को फिर से परिभाषित संरचनाओं में ला सकते हैं जो कार्यों के कार्यान्वयन द्वारा उपयोग किए जाने वाले की तुलना में अलग हैं। इससे बाइनरी असंगतता हो सकती है। ऐसी स्थितियों को संकलक और लिंकर द्वारा नियंत्रित नहीं किया जा सकता है।
आर्मगेडेस्क्यू

11
यह निश्चित ही। एक शीर्ष लेख में परिभाषित मैक्रोज़ का होना बहुत आसान है जो उस हेडर के बाद आने वाले कोड के अर्थ को पूरी तरह से बदल देता है #include
पीटर

4
मुझे यकीन है कि कोड गोल्फ ने इस पर आधारित कम से कम एक चुनौती दी है।
मार्क

6
मैं एक विशिष्ट वास्तविक दुनिया उदाहरण को इंगित करना चाहूंगा: स्मृति रिसाव का पता लगाने के लिए वीएलडी पुस्तकालय । जब कोई प्रोग्राम VLD के साथ सक्रिय होता है, तो यह कुछ आउटपुट चैनल पर सभी ज्ञात मेमोरी लीक का प्रिंट आउट लेगा। आप इसे VLD लाइब्रेरी से जोड़कर और #include <vld.h>अपने कोड में एक रणनीतिक स्थिति में एक पंक्ति में रखकर एक कार्यक्रम में एकीकृत करते हैं । उस VLD हेडर को निकालना या जोड़ना प्रोग्राम को "ब्रेक" नहीं करता है, लेकिन यह रनटाइम व्यवहार को काफी प्रभावित करता है। मैंने देखा है कि वीएलडी ने एक कार्यक्रम को धीमा कर दिया है कि यह अनुपयोगी हो गया है।
२०:२०

जवाबों:


40

हाँ, यह पूरी तरह से संभव है। मुझे यकीन है कि बहुत सारे तरीके हैं, लेकिन मान लीजिए कि फ़ाइल में एक वैश्विक चर परिभाषा शामिल है जिसे एक निर्माता कहा जाता है। पहले मामले में कंस्ट्रक्टर निष्पादित करेगा, और दूसरे में यह नहीं होगा।

हेडर फ़ाइल में वैश्विक चर परिभाषा डालना खराब शैली है, लेकिन यह संभव है।


1
<iostream>मानक पुस्तकालय में ठीक यही करता है; यदि किसी अनुवाद इकाई में शामिल है <iostream>तो std::ios_base::Initप्रोग्राम स्टार्ट, कैरेक्टर स्ट्रीम std::coutआदि को इनिशियलाइज़ करते हुए स्टेटिक ऑब्जेक्ट का निर्माण किया जाएगा, अन्यथा यह नहीं होगा।
इकतुर

33

हाँ, यह संभव है।

#includeसंकलन के समय सब कुछ संबंधित होता है। लेकिन संकलित समय चीजें रनटाइम पर व्यवहार को बदल सकती हैं, निश्चित रूप से:

some/code.h:

#define FOO
int foo(int a) { return 1; }

फिर

#include <iostream>
int foo(float a) { return 2; }

#include "some/code.h"  // Remove that line

int main() {
  std::cout << foo(1) << std::endl;
  #ifdef FOO
    std::cout << "FOO" std::endl;
  #endif
}

#includeअधिभार के साथ , अधिभार संकल्प अधिक उपयुक्त लगता है foo(int)और इसलिए 1इसके बजाय प्रिंट करता है 2। इसके अलावा, चूंकि FOOपरिभाषित किया गया है, इसलिए यह अतिरिक्त प्रिंट करता है FOO

यह सिर्फ दो (असंबंधित) उदाहरण हैं जो मेरे दिमाग में तुरंत आए, और मुझे यकीन है कि बहुत अधिक हैं।


14

बस तुच्छ मामले को इंगित करने के लिए, precompiler निर्देश:

// main.cpp
#include <iostream>
#include "trouble.h" // comment this out to change behavior

bool doACheck(); // always returns true

int main()
{
    if (doACheck())
        std::cout << "Normal!" << std::endl;
    else
        std::cout << "BAD!" << std::endl;
}

और तब

// trouble.h
#define doACheck(...) false

यह पैथोलॉजिकल है, शायद, लेकिन मुझे इससे संबंधित मामला हुआ है:

#include <algorithm>
#include <windows.h> // comment this out to change behavior

using namespace std;

double doThings()
{
    return max(f(), g());
}

मासूम लगता है। बुलाने की कोशिश करता है std::max। हालाँकि, windows.h अधिकतम परिभाषित करता है

#define max(a, b)  (((a) > (b)) ? (a) : (b))

यदि यह था std::max, तो यह एक सामान्य फ़ंक्शन कॉल होगा जो एक बार f () और एक बार g () का मूल्यांकन करता है। लेकिन windows.h में वहाँ के साथ, यह अब f () या g () का दो बार मूल्यांकन करता है: एक बार तुलना के दौरान और एक बार रिटर्न वैल्यू पाने के लिए। यदि f () या g () बेकार नहीं था, तो यह समस्या पैदा कर सकता है। उदाहरण के लिए, यदि उनमें से एक काउंटर होता है जो हर बार एक अलग संख्या देता है ...।


विंडो के अधिकतम फ़ंक्शन को कॉल करने के लिए +1, क्रियान्वयन बुराई और हर जगह पोर्टेबिलिटी के लिए प्रतिबंध को शामिल करने का एक वास्तविक विश्व उदाहरण है।
स्कॉट एम

3
OTOH, यदि आप से छुटकारा using namespace std;और उपयोग करते हैं std::max(f(),g());, तो कंपाइलर समस्या को पकड़ लेगा (अस्पष्ट संदेश के साथ, लेकिन कम से कम कॉल साइट की ओर इशारा करते हुए)।
रुस्लान

@ रोलन ओह, हाँ। अगर मौका दिया जाए, तो यह सबसे अच्छी योजना है। लेकिन कभी-कभी कोई विरासत कोड के साथ काम कर रहा होता है ... (नहींं ... कड़वा नहीं। कड़वा बिल्कुल नहीं!)
Cort Ammon

4

टेम्प्लेट विशेषज्ञता गायब होना संभव है।

// header1.h:

template<class T>
void algorithm(std::vector<T> &ts) {
    // clever algorithm (sorting, for example)
}

class thingy {
    // stuff
};

// header2.h

template<>
void algorithm(std::vector<thingy> &ts) {
    // different clever algorithm
}

// main.cpp

#include <vector>
#include "header1.h"
//#include "header2.h"

int main() {
    std::vector<thingy> thingies;
    algorithm(thingies);
}

4

बाइनरी असंगतता, एक सदस्य तक पहुंचना या इससे भी बदतर, गलत वर्ग के एक फ़ंक्शन को कॉल करना:

#pragma once

//include1.h:
#ifndef classw
#define classw

class class_w
{
    public: int a, b;
};

#endif

एक फ़ंक्शन इसका उपयोग करता है, और यह ठीक है:

//functions.cpp
#include <include1.h>
void smartFunction(class_w& x){x.b = 2;}

कक्षा के दूसरे संस्करण में लाना:

#pragma once

//include2.h:
#ifndef classw
#define classw

class class_w
{
public: int a;
};

#endif

मुख्य में कार्यों का उपयोग करना, दूसरी परिभाषा वर्ग की परिभाषा को बदल देती है। यह बाइनरी असंगतता की ओर जाता है और बस रनटाइम पर क्रैश होता है। और main.cpp में पहले शामिल को हटाकर समस्या को ठीक करें:

//main.cpp

#include <include2.h> //<-- Remove this to fix the crash
#include <include1.h>

void smartFunction(class_w& x);
int main()
{
    class_w w;
    smartFunction(w);
    return 0;
}

कोई भी संस्करण एक संकलन या लिंक समय त्रुटि उत्पन्न नहीं करता है।

इसके विपरीत स्थिति, एक शामिल दुर्घटना को हल करता है:

//main.cpp
//#include <include1.h>  //<-- Add this include to fix the crash
#include <include2.h>
...

प्रोग्राम के पुराने संस्करण में बग को ठीक करते समय या बाहरी लाइब्रेरी / dll / साझा ऑब्जेक्ट का उपयोग करते समय ये स्थितियां और भी अधिक कठिन होती हैं। इसलिए कभी-कभी द्विआधारी पिछड़े संगतता के नियमों का पालन करना चाहिए।


दूसरा हेडर ifndef के कारण शामिल नहीं किया जाएगा। अन्यथा यह संकलन नहीं करेगा (वर्ग पुनर्निर्धारण की अनुमति नहीं है)।
इगोर आर।

@IgorR। जागरुक रहें। दूसरा हेडर (शामिल1.h) केवल पहले स्रोत कोड में शामिल है। यह बाइनरी असंगतता की ओर जाता है। यह वास्तव में कोड का उद्देश्य है, ilustrate करने के लिए कि कैसे कोई रनटाइम पर दुर्घटना का कारण बन सकता है।
आर्मडेडेस्क्यू

1
@IgorR। यह बहुत ही सरल कोड है, जो इस तरह की स्थिति को दर्शाता है। लेकिन वास्तविक जीवन में स्थिति बहुत अधिक जटिल हो सकती है। पूरे पैकेज को पुनर्स्थापित किए बिना कुछ प्रोग्राम को पैच करने का प्रयास करें। यह विशिष्ट स्थिति है, जहां कड़ाई से पिछड़े द्विआधारी संगतता नियमों का पालन किया जाना चाहिए। अन्यथा पैचिंग एक असंभव कार्य है।
आर्मडेडेस्क्यू

मुझे यकीन नहीं है कि "पहला स्रोत कोड" क्या है, लेकिन अगर आपका मतलब है कि 2 अनुवाद इकाइयों में एक वर्ग की 2 अलग-अलग परिभाषाएं हैं, तो यह ओडीआर उल्लंघन है, अर्थात अपरिभाषित व्यवहार।
इगोर आर।

1
यह अपरिभाषित व्यवहार है , जैसा कि C ++ मानक द्वारा वर्णित है। एफडब्ल्यूआईडब्ल्यू, निश्चित रूप से, इस तरह से एक यूबी पैदा करना संभव है ...
इगोर आर

3

मैं बताना चाहता हूं कि समस्या सी में भी मौजूद है।

आप संकलक को बता सकते हैं कि एक फ़ंक्शन कुछ कॉलिंग कन्वेंशन का उपयोग करता है। यदि आप नहीं करते हैं, तो कंपाइलर को यह अनुमान लगाना होगा कि यह C ++ के विपरीत डिफ़ॉल्ट एक का उपयोग करता है, जहां कंपाइलर इसे कंपाइल करने से मना कर सकता है।

उदाहरण के लिए,

main.c

int main(void) {
  foo(1.0f);
  return 1;
}

foo.c

#include <stdio.h>

void foo(float x) {
  printf("%g\n", x);
}

लिनक्स पर x86-64 पर, मेरा आउटपुट है

0

यदि आप यहां प्रोटोटाइप को छोड़ते हैं, तो संकलक आपके पास है

int foo(); // Has different meaning in C++

और अनिर्दिष्ट तर्क सूचियों के लिए कन्वेंशन की आवश्यकता है जिसे floatपरिवर्तित किया doubleजाना चाहिए। इसलिए यद्यपि मैंने दिया 1.0f, संकलक इसे 1.0dपास करने के लिए इसे परिवर्तित करता है foo। और सिस्टम वी एप्लीकेशन बाइनरी इंटरफेस एएमडी 64 आर्किटेक्चर प्रोसेसर सप्लीमेंट के अनुसार, double64 कम से कम महत्वपूर्ण बिट्स में पास हो जाता है xmm0। लेकिन fooएक फ्लोट की उम्मीद है, और यह इसे 32 सबसे महत्वपूर्ण बिट्स से पढ़ता है xmm0, और 0 प्राप्त करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.