চারপাশে আর্গুমেন্টের চলক সংখ্যা পাস করা


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
আপনার উদাহরণটি আমার কাছে কিছুটা অদ্ভুত লাগছে, এতে আপনি উভয় ফর্ম্যাট_স্ট্রিং () এবং fprintf () এ এফএমটি পাস করেন। Format_string () কোনওভাবে নতুন স্ট্রিং ফিরিয়ে দেওয়া উচিত?
ক্রিস্টোফার জনসন

2
উদাহরণটি বোঝায় না। এটি কেবল কোডটির বাহ্যরেখা দেখানোর জন্য ছিল।
ভিসেন্ট মার্তি

162
"গুগল করা উচিত": আমি একমত নই। গুগলের প্রচুর শব্দ আছে (অস্পষ্ট, প্রায়শই বিভ্রান্তিকর তথ্য)। স্ট্যাকওভারফ্লোতে ভাল (ভোট দেওয়া, স্বীকৃত উত্তর) পাওয়া সত্যিই সহায়তা করে!
আনসার

71
কেবলমাত্র ওজন করতে হবে: আমি গুগল থেকে এই প্রশ্নে এসেছি, এবং এটি স্ট্যাক ওভারফ্লো হওয়ায় অত্যন্ত কার্যকর ছিল যে উত্তরটি কার্যকর হবে। তাই দূরে জিজ্ঞাসা!
tenpn

32
@ ইলিয়া: যদি কেউ কখনও গুগলের বাইরে জিনিস না লিখে থাকে তবে গুগলে অনুসন্ধান করার কোনও তথ্য থাকবে না।
এরিক কাপলুন

উত্তর:


211

উপবৃত্তগুলি পাস করতে, আপনাকে সেগুলি একটি ভিএলিস্টে রূপান্তর করতে হবে এবং আপনার দ্বিতীয় ফাংশনে সেই ভিএলিস্টটি ব্যবহার করতে হবে। বিশেষ করে;

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খুব কমই কার্যকর হবে, কারণ এটি এফএমটি-তে প্রাক-পরিস্থিতি পরিবর্তন করতে হবে, যা অবশ্যই করা উচিত নয়। বিকল্পগুলির মধ্যে সম্পূর্ণরূপে ফরম্যাট_ স্ট্রিং থেকে মুক্তি পাওয়া এবং ভিএফপ্রিন্টফ ব্যবহার করা অন্তর্ভুক্ত থাকবে তবে ফরম্যাট_স্ট্রিং আসলে কী করবে বা ফর্ম্যাট_স্ট্রিংয়ের ভিন্ন স্ট্রিং রয়েছে তা সম্পর্কে অনুমান করা যায়। উত্তরটি উত্তরটি সম্পাদন করে উত্তরটি দেখাব।
SmacL

1
যদি আপনার ফর্ম্যাট স্ট্রিং প্রিন্টফের মতো একই বিন্যাসের স্ট্রিং কমান্ডগুলি ব্যবহার করতে দেখা যায় তবে আপনি যদি GC এবং ক্ল্যাংয়ের মতো কিছু সংকলকও আপনাকে সতর্কবার্তা দিতে পারেন তবে যদি আপনার ফর্ম্যাট স্ট্রিংটি প্রকৃত যুক্তিগুলির সাথে সামঞ্জস্য না হয় তবে জিসিসি ফাংশন বৈশিষ্ট্যটি দেখুন ' আরও বিশদের জন্য ফর্ম্যাট: gcc.gnu.org/onlinesocs/gcc/Function-Atributes.html
ডগ রিচার্ডসন

1
আপনি যদি পরপর দু'বার অর্গগুলি পাস করেন তবে এটি কাজ করে না বলে মনে হচ্ছে।
fotanus

2
@ ফোটানাস: আপনি যদি কোনও ফাংশন কল করেন argptrএবং ডাকা ফাংশনটি একেবারে ব্যবহার করেন argptr, কেবলমাত্র নিরাপদ কাজটি কল va_end()এবং তারপরে পুনরায় পুনরায় চালু va_start(argptr, fmt);করতে পুনরায় আরম্ভ করা উচিত। অথবা va_copy()আপনার সিস্টেম এটি সমর্থন করে যদি আপনি ব্যবহার করতে পারেন (C99 এবং C11 এটি প্রয়োজন; C89 / 90 না করে)।
জোনাথন লেফলার

1
দয়া করে নোট করুন যে @ থমাস প্যাড্রন-ম্যাকার্থির মন্তব্য এখন পুরানো এবং চূড়ান্ত এফপ্রিন্টফ ঠিক আছে।
ফ্রেডেরিক

59

আপনি দুষ্টু এবং পোর্টেবল অযোগ্য কৌশলগুলিতে যেতে না চাইলে প্রিন্টফে কল করার কোনও উপায় নেই (উদাহরণস্বরূপ) আপনি এটিতে কত যুক্তি দিয়ে যাচ্ছেন তা জেনে না।

সাধারণত ব্যবহৃত সমাধান, সবসময় vararg ফাংশন একটি বিকল্প ফর্ম প্রদান করা হয়, যাতে 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)); })
রিচার্ড জে। রস তৃতীয়

5
@ রিচার্ড জে.রোসআইআইআই আমি আশা করি আপনি আপনার মন্তব্যটি প্রসারিত করবেন, এটি এর মতো কমই পাঠযোগ্য, আমি কোডটির পিছনে ধারণাটি তৈরি করতে পারি না এবং এটি আসলে খুব আকর্ষণীয় এবং দরকারী বলে মনে হচ্ছে।
পেনেলোপ

5
@ আর্টঅফ ওয়ারফেয়ার আমি নিশ্চিত নই যে আমি এটির একটি খারাপ হ্যাকের সাথে একমত, রোজের একটি দুর্দান্ত সমাধান রয়েছে তবে এতে ফানক ((টাইপ []) {ভাল1, ভাল 2, 0}) জড়িত রয়েছে; যা ক্লানকি বোধ করে, যদি আপনার # ডিফাইন ফান_শোর্ট_কুট (...) ফানক ((টাইপ []) { ভিএ_আরজিএস }) থাকে; তারপরে আপনি কেবল ফান_শার্ট_কুট (1, 2, 3, 4, 0) কল করতে পারবেন; যা আপনাকে গোলাপের ঝরঝরে কৌতুকের অতিরিক্ত বেনিফিট সহ সাধারণ বৈচিত্র্যময় ফাংশন হিসাবে একই সিনট্যাক্স দেয় ... এখানে সমস্যা কী?
chrispepper1989

9
আপনি যদি একটি যুক্তি হিসাবে 0 পাস করতে চান?
জুলিয়ান গোল্ড

1
এটি আপনার ব্যবহারকারীদের একটি টার্মিনেশন 0 দিয়ে কল করার কথা মনে রাখে it এটি কীভাবে নিরাপদ?
cp.engr

29

চমত্কার সি ++ 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
আপনি যদি ভিজ্যুয়াল স্টুডিও ব্যবহার করছেন তবে নভেম্বর ২০১২ সিটিপি ব্যবহার করে ভিজ্যুয়াল স্টুডিও ২০১২-এ ভেরিয়েডিক টেম্পলেটগুলি যুক্ত করা যেতে পারে। আপনি যদি ভিজুয়াল স্টুডিও 2013 ব্যবহার করে থাকেন তবে আপনার কাছে ভ্যারিয়েডিক টেম্পলেট থাকবে।
ব্যবহারকারী 2023370

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
কমপক্ষে এটি আসলে প্রশ্নের পুনরায় সংজ্ঞা না দিয়েই প্রশ্নের উত্তর দেয়।
lama12345

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

আপনি প্রথমে স্থানীয় বাফারে সংরক্ষণ করে ফরম্যাটারটি পাস করার সমাধান করতে পারেন, তবে এটি স্ট্যাকের প্রয়োজন এবং এটি কখনও কখনও মোকাবেলা করতে সমস্যা হতে পারে। আমি অনুসরণ করার চেষ্টা করেছি এবং মনে হচ্ছে এটি ঠিক আছে work

#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__শূন্য থাকলে (ভিজ্যুয়াল স্টুডিও সি ++ এবং জিসিসি উভয়ই)।

// 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

আসুন ধরা যাক আপনার লেখা একটি বৈকল্পিক ফাংশন রয়েছে। কারণ বৈকল্পিকের আগে কমপক্ষে একটি যুক্তি প্রয়োজন ..., আপনাকে সর্বদা ব্যবহারে একটি অতিরিক্ত যুক্তি লিখতে হবে।

অথবা আপনি কি?

আপনি যদি আপনার ভ্যারোডিক ফাংশনটি ম্যাক্রোতে আবদ্ধ করেন তবে আপনার পূর্ববর্তী আরগের দরকার নেই। এই উদাহরণ বিবেচনা করুন:

#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);
}

আপনি যদি চান ... আপনি অভ্যন্তরীণ_ফঙ্ক () এ যুক্ত করতে পারেন, তবে আপনার এটির দরকার নেই। এটি কাজ করে কারণ va_start প্রদত্ত ভেরিয়েবলের ঠিকানাটিকে সূচনা পয়েন্ট হিসাবে ব্যবহার করে। এই ক্ষেত্রে, আমরা এটিকে func () এর একটি পরিবর্তনশীলের একটি রেফারেন্স দিচ্ছি। সুতরাং এটি ঠিকানাটি ব্যবহার করে এবং স্ট্যাকের পরে ভেরিয়েবলগুলি পড়ে। অভ্যন্তরীণ_ফঙ্ক () ফাংশনটি ফানকের স্ট্যাক ঠিকানা থেকে পড়ছে ()। সুতরাং এটি কেবল তখনই কাজ করে যদি উভয় ফাংশন একই স্ট্যাক বিভাগ ব্যবহার করে।

আপনি যদি একটি সূচনা পয়েন্ট হিসাবে কোনও ভ্যারিট দেন তবে সাধারণত ভায়_স্টার্ট এবং ভ_আর্গ ম্যাক্রোগুলি কাজ করবে। আপনি যদি চান তবে আপনি অন্যান্য ফাংশনগুলিতে পয়েন্টারগুলি পাস করতে পারেন এবং সেগুলিও ব্যবহার করতে পারেন। আপনি আপনার নিজের ম্যাক্রোগুলিকে সহজেই যথেষ্ট পরিমাণে তৈরি করতে পারেন। সমস্ত ম্যাক্রোগুলি হ'ল টাইপকাস্ট মেমরি ঠিকানা। তবে তাদের সমস্ত সংকলক এবং কলিং কনভেনশনের জন্য কাজ করা বিরক্তিকর। তাই এটি সাধারণত সংকলকটির সাথে ব্যবহার করা আরও সহজ।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.