चारों ओर तर्क की चर संख्या


333

कहो कि मेरे पास एक सी फ़ंक्शन है जो तर्कों की एक चर संख्या लेता है: मैं किसी अन्य फ़ंक्शन को कैसे कह सकता हूं जो कि इसके अंदर से तर्कों की एक चर संख्या की उम्मीद करता है, जो पहले फ़ंक्शन में आए सभी तर्कों को पारित करता है?

उदाहरण:

void format_string(char *fmt, ...);

void debug_print(int dbg_lvl, char *fmt, ...) {
    format_string(fmt, /* how do I pass all the arguments from '...'? */);
    fprintf(stdout, fmt);
 }

4
आपका उदाहरण मुझे थोड़ा अजीब लग रहा है, जिसमें आप fmt को दोनों format_string () और fprintf () में पास करते हैं। क्या format_string () को किसी तरह एक नया स्ट्रिंग वापस करना चाहिए?
क्रिस्टोफर जॉनसन

2
उदाहरण का कोई मतलब नहीं है। यह सिर्फ कोड की रूपरेखा दिखाना था।
विक्टेंट मार्टी

162
"गोगोल किया जाना चाहिए": मैं असहमत हूं। Google के पास बहुत शोर है (अस्पष्ट, अक्सर भ्रमित करने वाली जानकारी)। Stackoverflow पर एक अच्छा (मतदान, स्वीकृत जवाब) होने से वास्तव में मदद मिलती है!
अंसार

71
बस वजन करने के लिए: मैं इस सवाल से आया था, और क्योंकि यह स्टैक ओवरफ्लो था अत्यधिक आश्वस्त था कि उत्तर उपयोगी होगा। तो पूछ लेना!
दसवें

32
@ इलिया: यदि किसी ने कभी भी Google के बाहर सामान नहीं लिखा है, तो Google पर खोज करने के लिए कोई जानकारी नहीं होगी।
एरिक कपलुन

जवाबों:


211

दीर्घवृत्त पास करने के लिए, आपको उन्हें एक va_list में बदलना होगा और अपने दूसरे फ़ंक्शन में उस va_list का उपयोग करना होगा। विशेष रूप से,

void format_string(char *fmt,va_list argptr, char *formatted_string);


void debug_print(int dbg_lvl, char *fmt, ...) 
{    
 char formatted_string[MAX_FMT_SIZE];

 va_list argptr;
 va_start(argptr,fmt);
 format_string(fmt, argptr, formatted_string);
 va_end(argptr);
 fprintf(stdout, "%s",formatted_string);
}

3
कोड को प्रश्न से लिया गया है, और यह वास्तव में सिर्फ एक उदाहरण है कि कुछ कार्यात्मक के बजाय दीर्घवृत्त को कैसे परिवर्तित किया जाए। यदि आप इसे देखते हैं तो format_stringयह शायद ही उपयोगी होगा, क्योंकि इसे fmt में इन-सीटू संशोधन करना होगा, जो निश्चित रूप से या तो नहीं किया जाना चाहिए। विकल्पों में पूरी तरह से format_string से छुटकारा पाना और vfprintf का उपयोग करना शामिल होगा, लेकिन यह इस बारे में धारणा बनाता है कि वास्तव में क्या format_string करता है, या एक अलग स्ट्रिंग को format_string लौटाता है। मैं उत्तर दिखाने के लिए उत्तर संपादित करूँगा।
SmacL

1
यदि आपका प्रारूप स्ट्रिंग प्रिंटफ़ के समान प्रारूप स्ट्रिंग आदेशों का उपयोग करने के लिए होता है, तो आपको चेतावनी देने के लिए gcc और clang जैसे कुछ संकलक भी मिल सकते हैं, यदि आपका प्रारूप स्ट्रिंग पास किए गए वास्तविक तर्कों के साथ संगत नहीं है। GCC फ़ंक्शन विशेषता देखें ' अधिक विवरण के लिए प्रारूप ': gcc.gnu.org/oniltocs/gcc/Function-Attributes.html
डग रिचर्डसन

1
यदि आप एक पंक्ति में दो बार आर्गन पास कर रहे हैं तो यह काम नहीं करता है।
फॉटेनस

2
@fotanus: यदि आप किसी फ़ंक्शन को कॉल करते हैं argptrऔर कॉल किया गया फ़ंक्शन argptrबिल्कुल उपयोग करता है , तो केवल सुरक्षित कार्य कॉल करना है va_end()और फिर va_start(argptr, fmt);रीइंस्ट्रिक्ट करना है। या आप उपयोग कर सकते हैं va_copy()यदि आपका सिस्टम इसका समर्थन करता है (C99 और C11 को इसकी आवश्यकता होती है; C89 / 90 नहीं)।
जोनाथन लेफ़लर

1
कृपया ध्यान दें कि @ थॉमसपैड्रॉन-मैक्कार्थी की टिप्पणी अब पुरानी है और अंतिम फ़र्फ़रफ़ ठीक है।
फ्रेडरिक

59

जब तक आप शरारती और गैर-पोर्टेबल चाल में नहीं आना चाहते हैं, तब तक यह जानने के बिना कि आपको कितने तर्क दिए जा रहे हैं, यह जानने के बिना कॉल करने का कोई तरीका नहीं है।

आम तौर पर उपयोग किया जाने वाला समाधान हमेशा वैरग कार्यों का एक वैकल्पिक रूप प्रदान करना है, इसलिए printfइसमें vprintfएक va_listजगह होती है ......संस्करणों के आसपास बस रैपर हैंva_list संस्करणों।


ध्यान दें कि vsyslogहै नहीं POSIX अनुपालन करता है।
patryk.beza

53

वेरिएडिक फंक्शंस खतरनाक हो सकते हैं । यहाँ एक सुरक्षित चाल है:

   void func(type* values) {
        while(*values) {
            x = *values++;
            /* do whatever with x */
        }
    }

func((type[]){val1,val2,val3,val4,0});

11
इससे भी बेहतर चाल है: #define callVardicMethodSafely(values...) ({ values *v = { values }; _actualFunction(values, sizeof(v) / sizeof(*v)); })
रिचर्ड जे। रॉस III

5
@ RichardJ.RossIII मेरी इच्छा है कि आप अपनी टिप्पणी पर विस्तार करेंगे, यह शायद ही इस तरह से पठनीय है, मैं कोड के पीछे का विचार नहीं बना सकता हूं और यह वास्तव में बहुत दिलचस्प और उपयोगी लगता है।
पेनेलोप

5
@ArtOfWarfare मुझे यकीन नहीं है कि मैं इस बात से सहमत हूं कि इसकी खराब हैक, रोज का एक शानदार समाधान है, लेकिन इसमें टाइपिंग फंक (प्रकार []] {val1, val2, 0}); अगर आपको #define func_short_cut (...) func ((प्रकार []) { VA_ARGS }) मिला हुआ है, जो भद्दा लगता है ; तो आप बस func_short_cut (1, 2, 3, 4, 0) को कॉल कर सकते हैं; जो आपको गुलाब के स्वच्छ चाल के अतिरिक्त लाभ के साथ एक सामान्य वैरिएड फ़ंक्शन के समान सिंटैक्स देता है ... यहां क्या मुद्दा है?
क्रिसपाइपर १

9
यदि आप एक तर्क के रूप में 0 पास करना चाहते हैं तो क्या होगा?
जूलियन गोल्ड

1
यह आपके उपयोगकर्ताओं को एक समाप्ति के साथ कॉल करने के लिए याद रखने की आवश्यकता है। 0. यह कैसे सुरक्षित है?
cp.engr

29

शानदार C ++ 0x में आप वैरेडिक टेम्प्लेट का उपयोग कर सकते हैं:

template <typename ... Ts>
void format_string(char *fmt, Ts ... ts) {}

template <typename ... Ts>
void debug_print(int dbg_lvl, char *fmt, Ts ... ts)
{
  format_string(fmt, ts...);
}

यह मत भूलो कि दृश्य स्टूडियो में अभी भी वैरेडिक टेम्प्लेट उपलब्ध नहीं हैं ... यह आपके लिए निश्चित रूप से चिंता का विषय हो सकता है!
टॉम स्विरली

1
यदि आप Visual Studio का उपयोग कर रहे हैं, तो varadic टेम्प्लेट्स को विज़ुअल स्टूडियो 2012 में नवंबर 2012 CTP का उपयोग करके जोड़ा जा सकता है। यदि आप Visual Studio 2013 का उपयोग कर रहे हैं, तो आपके पास वैरेडिक टेम्प्लेट होंगे।
user2023370 12

7

आप फ़ंक्शन कॉल के लिए इनलाइन असेंबली का उपयोग कर सकते हैं। (इस कोड में मुझे लगता है कि तर्क अक्षर हैं)।

void format_string(char *fmt, ...);
void debug_print(int dbg_level, int numOfArgs, char *fmt, ...)
    {
        va_list argumentsToPass;
        va_start(argumentsToPass, fmt);
        char *list = new char[numOfArgs];
        for(int n = 0; n < numOfArgs; n++)
            list[n] = va_arg(argumentsToPass, char);
        va_end(argumentsToPass);
        for(int n = numOfArgs - 1; n >= 0; n--)
        {
            char next;
            next = list[n];
            __asm push next;
        }
        __asm push fmt;
        __asm call format_string;
        fprintf(stdout, fmt);
    }

4
पोर्टेबल नहीं, कंपाइलर पर निर्भर करता है, और कंपाइलर ऑप्टिमाइज़ेशन को रोकता है। बहुत बुरा समाधान।
जिओप्रो

4
बिना डिलीट के भी नया।
user7116

8
कम से कम यह वास्तव में इस सवाल का जवाब देता है, सवाल को फिर से परिभाषित किए बिना।
लामा ४२४५

6

आप मैक्रो को भी आज़मा सकते हैं।

#define NONE    0x00
#define DBG     0x1F
#define INFO    0x0F
#define ERR     0x07
#define EMR     0x03
#define CRIT    0x01

#define DEBUG_LEVEL ERR

#define WHERESTR "[FILE : %s, FUNC : %s, LINE : %d]: "
#define WHEREARG __FILE__,__func__,__LINE__
#define DEBUG(...)  fprintf(stderr, __VA_ARGS__)
#define DEBUG_PRINT(X, _fmt, ...)  if((DEBUG_LEVEL & X) == X) \
                                      DEBUG(WHERESTR _fmt, WHEREARG,__VA_ARGS__)

int main()
{
    int x=10;
    DEBUG_PRINT(DBG, "i am x %d\n", x);
    return 0;
}

6

यद्यपि आप पहले स्थानीय बफर में इसे संग्रहीत करके फॉर्मेटर को पारित करने का समाधान कर सकते हैं, लेकिन इसके लिए स्टैक की आवश्यकता होती है और इससे निपटने के लिए कुछ समय जारी किया जा सकता है। मैंने निम्नलिखित की कोशिश की और यह ठीक काम करने लगता है।

#include <stdarg.h>
#include <stdio.h>

void print(char const* fmt, ...)
{
    va_list arg;
    va_start(arg, fmt);
    vprintf(fmt, arg);
    va_end(arg);
}

void printFormatted(char const* fmt, va_list arg)
{
    vprintf(fmt, arg);
}

void showLog(int mdl, char const* type, ...)
{
    print("\nMDL: %d, TYPE: %s", mdl, type);

    va_list arg;
    va_start(arg, type);
    char const* fmt = va_arg(arg, char const*);
    printFormatted(fmt, arg);
    va_end(arg);
}

int main() 
{
    int x = 3, y = 6;
    showLog(1, "INF, ", "Value = %d, %d Looks Good! %s", x, y, "Infact Awesome!!");
    showLog(1, "ERR");
}

उम्मीद है की यह मदद करेगा।


2

रॉस का समाधान थोड़ा साफ हो गया। केवल तभी काम करता है जब सभी आर्गन पॉइंटर्स हों। यदि __VA_ARGS__खाली है (दोनों विजुअल स्टूडियो C ++ और GCC do) तो भाषा कार्यान्वयन को पिछले अल्पविराम के समर्थन का समर्थन करना चाहिए ।

// pass number of arguments version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__}; _actualFunction(args+1,sizeof(args) / sizeof(*args) - 1);}


// NULL terminated array version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__, NULL}; _actualFunction(args+1);}

0

मान लीजिए कि आपके पास एक विशिष्ट वैरेडिक फ़ंक्शन है जो आपने लिखा है। क्योंकि वैराडिक से पहले कम से कम एक तर्क की आवश्यकता होती है ..., आपको हमेशा उपयोग में एक अतिरिक्त तर्क लिखना होगा।

या तुम करते हो?

यदि आप अपने वैरिएबल फ़ंक्शन को मैक्रो में लपेटते हैं, तो आपको कोई पूर्ववर्ती arg नहीं चाहिए। इस उदाहरण पर विचार करें:

#define LOGI(...)
    ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))

यह स्पष्ट रूप से कहीं अधिक सुविधाजनक है, क्योंकि आपको हर बार प्रारंभिक तर्क निर्दिष्ट करने की आवश्यकता नहीं है।


-5

मैं अनिश्चित हूँ अगर यह सभी संकलक के लिए काम करता है, लेकिन यह मेरे लिए अब तक काम किया है।

void inner_func(int &i)
{
  va_list vars;
  va_start(vars, i);
  int j = va_arg(vars);
  va_end(vars); // Generally useless, but should be included.
}

void func(int i, ...)
{
  inner_func(i);
}

यदि आप चाहें तो ... को inner_func () में जोड़ सकते हैं, लेकिन आपको इसकी आवश्यकता नहीं है। यह काम करता है क्योंकि va_start प्रारंभ बिंदु के रूप में दिए गए चर के पते का उपयोग करता है। इस मामले में, हम इसे func () में एक चर के संदर्भ में दे रहे हैं। तो यह उस पते का उपयोग करता है और स्टैक पर उसके बाद चर पढ़ता है। Inner_func () फ़ंक्शन func () के स्टैक पते से पढ़ रहा है। तो यह केवल तभी काम करता है जब दोनों फ़ंक्शन समान स्टैक सेगमेंट का उपयोग करते हैं।

Va_start और va_arg मैक्रोज़ आम तौर पर काम करेंगे यदि आप उन्हें शुरुआती बिंदु के रूप में कोई भी संस्करण देते हैं। तो अगर आप चाहते हैं कि आप अन्य कार्यों के लिए संकेत पारित कर सकते हैं और उन का उपयोग भी कर सकते हैं। आप अपना मैक्रोज़ आसानी से बना सकते हैं। सभी मैक्रोज़ करते हैं टाइपकास्ट मेमोरी एड्रेस। हालांकि उन्हें सभी कंपाइलरों के लिए काम करना और सम्मेलनों को कॉल करना कष्टप्रद है। इसलिए आमतौर पर कंपाइलर के साथ आने वालों का उपयोग करना आसान होता है।

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