2009-11-06 9 views
10

मैं एक सामान्य प्रकार के साथ सी stdarg.h lib का उपयोग करने की कोशिश कर रहा हूँ। टाइप int, मेरा सामान्य प्रकार है> इसे समझने के लिए, कृपया पढ़ने के लिए रखें। तो, मेरी समस्या है:सी में परिवर्तनीय तर्क, सामान्य प्रकार के साथ मूल्य कैसे प्राप्त करें?

मेरे पास एक ऐसा फ़ंक्शन है जो तर्कों की परिवर्तनीय संख्या स्वीकार करता है।

void function (int paramN, ...);

मेरे कार्यक्रम में की तरह, वहाँ कोई रास्ता नहीं है पता करने के लिए है, जो चर तर्क के प्रकार है कर रहे हैं, यह एक चार, एक सरणी, एक पूर्णांक, एक छोटा है, एक समारोह बिंदु, आदि हो सकता है ... जैसे

function (paramN, "Hey, I'm a string", 1, function_pint, array, -1); // -1 is a sentinel.

तो, मुझे लगता है कि, किसी पूर्णांक, 32bits, एक 86 (32bits) प्रणाली में है, यह स्मृति के सभी पते का आयोजन करेगा। इसलिए, यदि मुझे int के साथ सभी तर्क मिलते हैं, तो यह कोई समस्या नहीं होगी, उदाहरण के लिए, "अरे, मैं एक स्ट्रिंग हूं" इस स्ट्रिंग का पता, 32 बिट्स वैरिएबल में सामान्य रूप से फिट होता है, इसलिए, मुझे केवल बनाने की आवश्यकता है अभिनेता का चयन।

मैं सही हूँ?
क्या मैं कर सकता हूं?
नोट: मैं अपने फ़ंक्शन को printf (इस समाधान में फिट नहीं करना चाहता हूं, ठीक है?)

सभी उत्तरों के लिए धन्यवाद।
और मेरी खराब अंग्रेजी के लिए खेद है।

उत्तर

9

आप इसका वर्णन करने के तरीके को नहीं कर सकते हैं।

सी कॉलिंग सम्मेलन कॉलर के लिए स्टैक पर तर्क डालने के लिए है, लेकिन यह किसी भी प्रकार की जानकारी नहीं डालता है, इसलिए कैली के पास इसे खोजने के लिए एक तरीका होना चाहिए (कम से कम चर के आकार)।

  • प्रोटोटाइप के साथ कार्यों के लिए कोई समस्या ज्ञात नहीं है।

  • परिवर्तनीय संख्या या पैरामीटर (विविधता) वाले कार्यों के साथ यह अधिक कठिन है, आपको प्रत्येक चर के लिए प्रत्येक तर्क के लिए va_arg को कॉल करना होगा और आपको va_arg के लिए प्रकार प्रदान करना होगा। यदि आप एक ऐसा प्रकार प्रदान करते हैं जो वास्तविक नहीं है तो संकलक शिकायत नहीं करेगा (यह रनटाइम जानकारी नहीं हो सकता है) लेकिन कुछ भी हो सकता है (आमतौर पर कुछ खराब)।

इसलिए आप के लिए प्रकार पारित किया है

कुछ मामलों में आप प्रकार की भविष्यवाणी कर सकते हैं (उदाहरण: कुछ काउंटर के बाद एक काउंटर, और इसी तरह), लेकिन आमतौर पर आप पैरामीटर में एन्कोड किए जाते हैं। आप इसे printf करता है जैसे प्रारूप स्ट्रिंग में एन्कोड किया जा सकता है, jldupont द्वारा वर्णित यूनियन चाल भी काफी आम है।

लेकिन फिर भी आपको इसे पास करना होगा।

आप वास्तव में अंतर्निहित डेटा बाइनरी प्रतिनिधित्व पर भरोसा नहीं कर सकते हैं, यहां तक ​​कि डेटा आकार भी नहीं। यहां तक ​​कि यदि प्रोग्राम लिखते समय काम करना प्रतीत होता है, तो इसकी कोई संगतता नहीं है और सिस्टम के किसी भी बदलाव, कंपाइलर, या यहां तक ​​कि संकलन विकल्पों को बदलने के दौरान भी तोड़ सकता है।

चलिए एक उदाहरण के साथ जाते हैं जहां आप तर्क के बाद टाइप पास करते हैं (इस प्रकार न तो यूनियन चाल और न ही प्रारूप स्ट्रिंग जैसे printf)। क्या यह होता है दोगुना और उन्हें जोड़ने के लिए सभी इसे पारित है मूल्य, कोई वास्तव में उपयोगी परिवर्तित ना:

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

enum mytypes {LONG, INT, FLOAT, DOUBLE }; 

double myfunc(int count, ...){ 
    long tmp_l; 
    int tmp_i; 
    double tmp_d; 
    double res = 0; 
    int i; 

    va_list ap; 
    va_start(ap, count); 
    for(i=0 ; i < count; i++){ 
     int type = va_arg(ap, enum mytypes); 
     switch (type){ 
      case LONG: 
      tmp_l = va_arg(ap, long); 
      res += tmp_l; 
      break; 
      case INT: 
      tmp_i = va_arg(ap, int); 
      res += tmp_i; 
      break; 
      case FLOAT: 
      /* float is automatically promoted to double when passed to va_arg */ 
      case DOUBLE: 
      tmp_d = va_arg(ap, double); 
      res += tmp_d; 
      break; 
      default: /* unknown type */ 
      break; 
     } 
    } 
    va_end(ap); 
    return res; 
} 

int main(){ 
    double res; 
    res = myfunc(5, 
     LONG, (long)1, 
     INT, (int)10, 
     DOUBLE, (double)2.5, 
     DOUBLE, (double)0.1, 
     FLOAT, (float)0.3); 
    printf("res = %f\n", res); 
} 

इस उदाहरण नई stdarg variadic हैडर C99 में परिभाषित का उपयोग करें।इसका उपयोग करने के लिए आपको अपने फ़ंक्शन में कम-से-कम एक निश्चित पैरामीटर होना चाहिए (इस उदाहरण में यह count है)। अच्छी बात यह है कि यदि आपके पास आपके फ़ंक्शन में कई भिन्नताएं हो सकती हैं (यानी myfunc(int count1, ..., int count2, ...) जैसी कुछ)। बुरी बात यह है कि आपके पास पूरी तरह से भिन्न कार्य नहीं हो सकता है (यानी पुराना प्रारूप के साथ myfunc (...) जैसे कुछ। आप अभी भी varargs संगतता शीर्षलेख का उपयोग कर पुराने प्रारूप का उपयोग कर सकते हैं। लेकिन यह अधिक जटिल है और शायद ही कभी जरूरी है, क्योंकि आपको प्रकारों की आवश्यकता है लेकिन सूची जानने के लिए कुछ तरीका भी समाप्त हो गया है और गिनती की तरह कुछ आसान है (लेकिन ऐसा करने का एकमात्र तरीका नहीं है, उदाहरण के लिए 'टर्मिनेटर' का उपयोग किया जा सकता है)

+0

नोट: मैं अपने फ़ंक्शन को printf (यह समाधान, इस मामले में फिट नहीं करना चाहता हूं ठीक है?) – drigoSkalWalker

+0

वाह, धन्यवाद क्रिस, आप एक विशेषज्ञ हैं, आपके समय के लिए धन्यवाद, जवाब बहुत अच्छा है! बहुत बहुत धन्यवाद; डी – drigoSkalWalker

14

प्रत्येक पैरामीटर & के लिए एक शून्य * (या एक टाइप की गई संरचना) का उपयोग करें "प्रकार" तर्क (एक पूर्णांक) के साथ संरचना का उपयोग करें। वास्तविक मूल्य रखने के लिए एक सूचक/संघ।

दूसरे शब्दों में, प्रत्येक पैरामीटर एक टाइपर संरचना के लिए एक सूचक के साथ पारित किया जाता है। इस टाइप की गई संरचना के प्रत्येक उदाहरण में एक मान होता है। इस "मूल्य" का प्रकार इस टाइप की गई संरचना में निहित है।

बेहतर उदाहरण:

typedef struct { 
    int type; 
    union { 
    int int_value; 
    double double_value; 
    ... 
    }; 
} Param; 

void function(Param *p1, Param *p2, ...)

ऐसी चाल का नवीनतम उदाहरण मैं DBus था में भाग गया।

+0

क्या आप समझा सकते हैं यह बेहतर है? मुझे लगता है कि मैं समझ गया, उदाहरण के लिए, मैं संभव प्रकार {INT, लघु, चार्ज, ARRAY ... ईटीसी} के साथ एक enum बना सकते हैं, मैं दो क्षेत्रों के साथ एक संरचना, एक क्षेत्र एनम का सही प्रकार है? और दूसरे के पास मूल्य ठीक है? यह सही है? मूल्य कैसे पकड़ें? क्षमा करें अगर मैं गलत हूं और आपके उत्तर के लिए धन्यवाद। – drigoSkalWalker

+0

@drigo: आप सही हैं। – jldupont

3

आप इस तरह के तर्कसंगत तर्कों के साथ ऐसा नहीं कर सकते हैं जिस तरह से आप वर्णन कर रहे हैं क्योंकि आपके द्वारा पारित तर्कों के प्रकार के बारे में कोई जानकारी तब तक संकलित नहीं की जाती है जब तक आप इसे स्पष्ट रूप से नहीं करते। तर्क चर के पते को पार करने से आपको यह भी नहीं बताया जाएगा, क्योंकि स्ट्रिंग का प्रतिनिधित्व करने वाली स्मृति में बिट्स किसी संख्या या कुछ और का प्रतिनिधित्व कर सकती हैं।

वैरगास वैरिएबल प्रकारों के साथ काम करने के लिए, आप तर्कों में स्वयं की जानकारी को संग्रहीत कर सकते हैं (उदाहरण के लिए, जैसा कि उनके उत्तर में jldupont द्वारा वर्णित है), या आप जानकारी को गैर-चरणीय तर्क में संग्रहीत कर सकते हैं (उदाहरण के लिए, प्रारूप printf की तरह स्ट्रिंग)।

+0

नोट: मैं अपने काम को printf (इस समाधान में नहीं करना चाहता, इस मामले में ठीक नहीं है?) – drigoSkalWalker

+1

मैं समझता हूं। मैं सिर्फ यह स्पष्ट करने की कोशिश कर रहा हूं कि अन्य लोग इस समाधान को क्यों चुनते हैं जिसे आप अस्वीकार कर रहे हैं। –

+0

ठीक नाथन, आपके उत्तर के लिए धन्यवाद, मैं केवल यह ठीक करने के लिए अन्य तरीका प्राप्त करना चाहता हूं? वैसे भी, आपके उत्तर लड़के के लिए धन्यवाद! ; डी – drigoSkalWalker

1

http://en.wikipedia.org/wiki/Stdarg.h से परछती:

कोई तंत्र [...] कार्य करने के लिए पारित कर दिया अनाम तर्क के प्रकार का निर्धारण करने के लिए परिभाषित नहीं है। फ़ंक्शन को किसी भी तरह से जानना या निर्धारित करना आवश्यक है, जिसका अर्थ भिन्न होता है।

है यही कारण है कि अपने कार्य सिर्फ तर्क जो तर्क तार कर रहे हैं से पता नहीं कर सकते हैं। आप अपने काम को बताने के लिए की आवश्यकता होगी। यही कारण है कि printf सम्मेलन इतना आम है।

+0

नोट: मैं अपने काम को printf (इस समाधान में नहीं करना चाहता, इस मामले में ठीक नहीं है?) – drigoSkalWalker

+2

हम समझ गए। आपने जो पूछा है उसे पूरा करने के लिए * कोई * आसान तरीका नहीं है। आपको * एक varags फ़ंक्शन को बताने का तरीका ढूंढना चाहिए कि तर्कों में किस प्रकार का डेटा है। 'Printf' सम्मेलन एकमात्र विकल्प नहीं है (लिंक या jldupont का जवाब देखें), लेकिन आपको हमेशा कहना है। – dmckee

+0

ठीक है dmckee, आपके उत्तर के लिए धन्यवाद, मैं केवल यह ठीक करने के लिए अन्य तरीका प्राप्त करना चाहता हूं? वैसे भी, आपके उत्तर लड़के के लिए धन्यवाद! ; डी – drigoSkalWalker

0

आपको कोशिश करें? Intptr_t, जिसे stdint.h में परिभाषित किया गया है। यह एक पूर्णांक है जो एक सूचक को पकड़ने के लिए पर्याप्त होने की गारंटी है।

आप यह भी सोचना चाहेंगे कि जब आप फ़्लोटिंग पॉइंट नंबर पास करते हैं तो क्या होता है; इसे एक डबल के रूप में परिवर्तित और पारित किया जाता है। यदि आपका कार्यक्रम एक इंट की उम्मीद कर रहा है तो यह ढेर के अपने दृष्टिकोण को नष्ट कर देगा। आउच। और संकलक इसे पकड़ नहीं सकता है।

और परिभाषित अनुसार आपके कार्य में (पैरामीटर में किसी भी बिट-एन्कोडिंग को छोड़कर, इस मामले में यह एक enum या bitfield होना चाहिए, या, कम से कम, हस्ताक्षरित) यह जानने का कोई तरीका नहीं है कि यह किस प्रकार के पैरामीटर प्राप्त करता है हैं, और इस तरह उनके साथ कुछ भी उपयोगी करने में सक्षम होने की संभावना नहीं है। सी के पास अपनी वस्तुओं पर कोई रन-टाइम प्रकार की जानकारी नहीं है।

क्या, आप वास्तव में क्या करने की कोशिश कर रहे हैं?

+0

हाय मैन, आपका जवाब बहुत अच्छा है, मैं चरम तर्कों के अपने फ़ंक्शन में कुछ फ़ंक्शन पॉइंट पास करने का प्रयास कर रहा हूं, लेकिन बीच में, मैं एक स्ट्रिंग या int भी पास करूंगा, मुझे लगता है कि मुझे उपयोग करने में कोई समस्या नहीं है इसे पूरा करने के लिए int, या, intptr_t भी उपयोग करने के लिए, आप इसके बारे में मुझे क्या कहते हैं? आपके उत्तर के लिए धन्यवाद o/ – drigoSkalWalker

+0

आपके प्रोग्राम को यह जानना है कि प्रत्येक तर्क का प्रकार क्या है, और आपके फ़ंक्शन में इसे जानने का एकमात्र तरीका किसी भी तरह से आपके तर्कों के माध्यम से उस जानकारी को पास करना है। यदि आप printf ("% s:% d", "string", i) जैसे प्रिंटफ को कॉल करते हैं तो यह स्ट्रिंग के रूप में अपना पहला तर्क और दूसरा int के रूप में उपयोग करता है क्योंकि स्वरूप स्ट्रिंग इसे करने के लिए कहती है। यदि उपयोगकर्ता कुछ और पास करता है, जैसे printf ("% s:% d", 0, "x") printf उसे नहीं जानता है, और आपका प्रोग्राम गलती में है। आप अपने कार्यों को कैसे प्रयोग कर रहे हैं अपने तर्कों का उपयोग कैसे करें? यह कैसे पता चलता है कि कौन से चींटियां हैं, जो तार हैं, जो पॉइंटर्स हैं, और सी? –

संबंधित मुद्दे

 संबंधित मुद्दे