سؤال

هل هناك طريقة لتحديد الحجج الافتراضية إلى وظيفة في ج؟

هل كانت مفيدة؟

المحلول

ليس صحيحا. الطريقة الوحيدة ستكون اكتب وظيفة varargs وملء يدويا في القيم الافتراضية للحجج التي لا يمر المتصل.

نصائح أخرى

نجاح باهر، الجميع متشائم هنا. الجواب نعم.

ليس ذلك تافهة: بحلول النهاية، سيكون لدينا الوظيفة الأساسية، وهي بنية دعم، وظيفة التفاف، وماكرو حول وظيفة التفاف. في عملي، لدي مجموعة من وحدات الماكرو لأتمتة كل هذا؛ بمجرد فهم التدفق، سيكون من السهل عليك أن تفعل الشيء نفسه.

لقد كتبت هذا الأمر في مكان آخر، لذلك إليك رابط خارجي مفصل لاستكمال الملخص هنا: http://modelingwithdata.org/arch/00000022.htm.

نود أن تتحول

double f(int i, double x)

في وظيفة تأخذ الإعدادات الافتراضية (i = 8، x = 3.14). تحديد بنية رفيق:

typedef struct {
    int i;
    double x;
} f_args;

إعادة تسمية وظيفتك f_base, وحدد وظيفة التفاف التي تحدد الإعدادات الافتراضية وتدعو القاعدة:

double var_f(f_args in){
    int i_out = in.i ? in.i : 8;
    double x_out = in.x ? in.x : 3.14;
    return f_base(i_out, x_out);
}

أضف الآن ماكرو، باستخدام وحدات ماكرو Variadic C. بهذه الطريقة لا يتعين على المستخدمين أن يعرفوا أنهم في الواقع ملء f_args بنية وتعتقد أنهم يفعلون المعتاد:

#define f(...) var_f((f_args){__VA_ARGS__});

حسنا، الآن كل ما يلي سيعمل:

f(3, 8);      //i=3, x=8
f(.i=1, 2.3); //i=1, x=2.3
f(2);         //i=2, x=3.14
f(.x=9.2);    //i=8, x=9.2

تحقق من قواعد حول كيفية تعيين المهيئين في الإعدادات الافتراضية للقواعد الدقيقة.

شيء واحد لن يعمل: f(0), ، لأننا لا نستطيع التمييز بين القيمة المفقودة والصفر. في تجربتي، هذا شيء لمشاهدةه، ولكن يمكن الاعتناء به لأن الحاجة تنشأ - - نصف الوقت الافتراضي هو الصفر حقا.

ذهبت من خلال مشكلة الكتابة هذا لأعلى لأنني أعتقد أن الحجج المسماة والإعدادات الافتراضية حقا جعل الترميز في C أسهل وحتى أكثر متعة. و C رائع أن تكون بسيطا للغاية ولا يزال لديك ما يكفي من ذلك لكسب كل هذا ممكنا.

نعم. :-) ولكن ليس بطريقة تتوقع.

int f1(int arg1, double arg2, char* name, char *opt);

int f2(int arg1, double arg2, char* name)
{
  return f1(arg1, arg2, name, "Some option");
}

لسوء الحظ، لا يسمح لك C بطرق الزائدة حتى ينتهي بك الأمر بوظائف مختلفة. لا يزال، عن طريق الاتصال ب F2، كنت في الواقع استدعاء F1 بقيمة افتراضية. هذا هو الحل "لا تكرر نفسك"، مما يساعدك على تجنب نسخ / لصق التعليمات البرمجية الموجودة.

يمكننا إنشاء وظائف تستخدم المعلمات المسماة (فقط) للقيم الافتراضية. هذا هو استمرار إجابة BK.

#include <stdio.h>                                                               

struct range { int from; int to; int step; };
#define range(...) range((struct range){.from=1,.to=10,.step=1, __VA_ARGS__})   

/* use parentheses to avoid macro subst */             
void (range)(struct range r) {                                                     
    for (int i = r.from; i <= r.to; i += r.step)                                 
        printf("%d ", i);                                                        
    puts("");                                                                    
}                                                                                

int main() {                                                                     
    range();                                                                    
    range(.from=2, .to=4);                                                      
    range(.step=2);                                                             
}    

يحدد معيار C99 أن الأسماء اللاحقة في التهيئة تجاوز العناصر السابقة. يمكننا أيضا الحصول على بعض المعلمات الموضحة القياسية أيضا، فقط تغيير ماكرو وتوقيع الوظيفة وفقا لذلك. لا يمكن استخدام معلمات القيمة الافتراضية فقط في أسلوب المعلمة المسماة.

إخراج البرنامج:

1 2 3 4 5 6 7 8 9 10 
2 3 4 
1 3 5 7 9

opencv. يستخدم شيئا مثل:

/* in the header file */

#ifdef __cplusplus
    /* in case the compiler is a C++ compiler */
    #define DEFAULT_VALUE(value) = value
#else
    /* otherwise, C compiler, do nothing */
    #define DEFAULT_VALUE(value)
#endif

void window_set_size(unsigned int width  DEFAULT_VALUE(640),
                     unsigned int height DEFAULT_VALUE(400));

إذا كان المستخدم لا يعرف ما يجب عليه الكتابة، فيمكن أن تكون هذه الخدعة مفيدة:

usage example

لا.

ولا حتى آخر قياسي C99 يدعم هذا.

لا، هذه ميزة لغة C ++.

ربما أفضل طريقة للقيام بذلك (والتي قد تكون ممكنة أو غير ممكنة في حالتك حسب موقفك) هي الانتقال إلى C ++ واستخدامها ك "أفضل C". يمكنك استخدام C ++ دون استخدام الفصول أو القوالب أو التحميل الزائد للمشغل أو غيرها من الميزات المتقدمة.

سيعطيك هذا متغيرا من C مع التحميل الزائد للوظيفة والمعلمات الافتراضية (وأي كلما كنت قد اخترت استخدامها). عليك فقط أن تكون منضبطا قليلا إذا كنت جادا حقا في استخدام مجموعة فرعية مقيدة فقط من C ++.

سيقول الكثير من الناس إنها فكرة فظيعة لاستخدام C ++ بهذه الطريقة، وقد يكون لها نقطة. لكنه مجرد رأي؛ أعتقد أنه صالح لاستخدام ميزات C ++ التي تشعر بالراحة دون الحاجة إلى الشراء في كل شيء. أعتقد أن جزءا كبيرا من سبب نجاح C ++ هو أنه تم استخدامه من قبل الكثير من المبرمجين في الأيام الأولى في هذه الطريقة بالضبط.

اجابة قصيرة: لا.

إجابة أطول قليلا: هناك قديم عمر او قديم الحل البديل حيث تمر سلسلة أنت بتحليل للحجج الاختيارية:

int f(int arg1, double arg2, char* name, char *opt);

حيث قد يتضمن التقيد "الاسم = القيمة" الزوج أو أي شيء، والتي ستسمح بها

n = f(2,3.0,"foo","plot=yes save=no");

من الواضح أن هذا مفيد فقط في بعض الأحيان. بشكل عام عندما تريد واجهة واحدة لعائلة من الوظائف.


ما زلت تجد هذا النهج في رموز فيزياء الجسيمات التي كتبها البرامج المهنية في C ++ (مثل على سبيل المثال جذر). إنها ميزة رئيسية هي أنه قد يتم تمديده بشكل غير مسمى تقريبا مع الحفاظ على توافق الظهر.

يستخدم خيار آخر structس:

struct func_opts {
  int    arg1;
  char * arg2;
  int    arg3;
};

void func(int arg, struct func_opts *opts)
{
    int arg1 = 0, arg3 = 0;
    char *arg2 = "Default";
    if(opts)
      {
        if(opts->arg1)
            arg1 = opts->arg1;
        if(opts->arg2)
            arg2 = opts->arg2;
        if(opts->arg3)
            arg3 = opts->arg3;
      }
    // do stuff
}

// call with defaults
func(3, NULL);

// also call with defaults
struct func_opts opts = {0};
func(3, &opts);

// set some arguments
opts.arg3 = 3;
opts.arg2 = "Yes";
func(3, &opts);

لا.

لا، ولكن قد تفكر في استخدام جلس من الوظائف (أو وحدات الماكرو) تقريبية باستخدام Args الافتراضي:

// No default args
int foo3(int a, int b, int c)
{
    return ...;
}

// Default 3rd arg
int foo2(int a, int b)
{
    return foo3(a, b, 0);  // default c
}

// Default 2nd and 3rd args
int foo1(int a)
{
    return foo3(a, 1, 0);  // default b and c
}

نعم، مع ميزات C99 يمكنك القيام بذلك. يعمل هذا دون تحديد هياكل بيانات جديدة أو نحو ذلك وبدون الوظيفة قد اتخاذ قرار بشأن وقت التشغيل، كيف تم استدعاؤه، ودون أي علامات عامة حسابية.

للحصول على شرح مفصل انظر مشاركتي في

http://gustedt.wordpress.com/2010/03/03/Default-argums-For-c99/

جينز

عموما لا، ولكن في دول مجلس التعاون الخليجي، قد تجعل المعلمة الأخيرة من function () اختياري مع ماكرو.

في FUNCB ()، استخدم قيمة خاصة (-1) للإشارة إلى أن أحتاج إلى القيمة الافتراضية لمعلمة 'B'.

#include <stdio.h> 

int funcA( int a, int b, ... ){ return a+b; }
#define funcA( a, ... ) funcA( a, ##__VA_ARGS__, 8 ) 


int funcB( int a, int b ){
  if( b == -1 ) b = 8;
  return a+b;
}

int main(void){
  printf("funcA(1,2): %i\n", funcA(1,2) );
  printf("funcA(1):   %i\n", funcA(1)   );

  printf("funcB(1, 2): %i\n", funcB(1, 2) );
  printf("funcB(1,-1): %i\n", funcB(1,-1) );
}

أنا تحسين جينز غوستيدت إجابه لهذا السبب:

  1. الوظائف المضمنة غير مستخدمة
  2. يتم حساب الإعدادات الافتراضية أثناء التعامل معها
  3. وحدات وحدات قابلة لإعادة الاستخدام
  4. من الممكن تعيين خطأ التحويل البرمجي الذي يتطابق بشكل مفيد في حالة عدم كفاية الوسائط غير الكافية للإعدادات الافتراضية المسموح بها
  5. البيانات الافتراضية غير مطلوبة لتشكيل ذيل قائمة المعلمات إذا كانت أنواع الوسيطة ستبقى لا لبس فيها
  6. interopts مع C11 _generic
  7. تختلف اسم الوظيفة حسب عدد الحجج!

variadic.h:

#ifndef VARIADIC

#define _NARG2(_0, _1, _2, ...) _2
#define NUMARG2(...) _NARG2(__VA_ARGS__, 2, 1, 0)
#define _NARG3(_0, _1, _2, _3, ...) _3
#define NUMARG3(...) _NARG3(__VA_ARGS__, 3, 2, 1, 0)
#define _NARG4(_0, _1, _2, _3, _4, ...) _4
#define NUMARG4(...) _NARG4(__VA_ARGS__, 4, 3, 2, 1, 0)
#define _NARG5(_0, _1, _2, _3, _4, _5, ...) _5
#define NUMARG5(...) _NARG5(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define _NARG6(_0, _1, _2, _3, _4, _5, _6, ...) _6
#define NUMARG6(...) _NARG6(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
#define _NARG7(_0, _1, _2, _3, _4, _5, _6, _7, ...) _7
#define NUMARG7(...) _NARG7(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define _NARG8(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) _8
#define NUMARG8(...) _NARG8(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define _NARG9(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _9
#define NUMARG9(...) _NARG9(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define __VARIADIC(name, num_args, ...) name ## _ ## num_args (__VA_ARGS__)
#define _VARIADIC(name, num_args, ...) name (__VARIADIC(name, num_args, __VA_ARGS__))
#define VARIADIC(name, num_args, ...) _VARIADIC(name, num_args, __VA_ARGS__)
#define VARIADIC2(name, num_args, ...) __VARIADIC(name, num_args, __VA_ARGS__)

// Vary function name by number of arguments supplied
#define VARIADIC_NAME(name, num_args) name ## _ ## num_args ## _name ()
#define NVARIADIC(name, num_args, ...) _VARIADIC(VARIADIC_NAME(name, num_args), num_args, __VA_ARGS__)

#endif

سيناريو الاستخدام المبسط:

const uint32*
uint32_frombytes(uint32* out, const uint8* in, size_t bytes);

/*
The output buffer defaults to NULL if not provided.
*/

#include "variadic.h"

#define uint32_frombytes_2(   b, c) NULL, b, c
#define uint32_frombytes_3(a, b, c)    a, b, c
#define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

ومع _generic:

const uint8*
uint16_tobytes(const uint16* in, uint8* out, size_t bytes);

const uint16*
uint16_frombytes(uint16* out, const uint8* in, size_t bytes);

const uint8*
uint32_tobytes(const uint32* in, uint8* out, size_t bytes);

const uint32*
uint32_frombytes(uint32* out, const uint8* in, size_t bytes);

/*
The output buffer defaults to NULL if not provided.
Generic function name supported on the non-uint8 type, except where said type
is unavailable because the argument for output buffer was not provided.
*/

#include "variadic.h"

#define   uint16_tobytes_2(a,    c) a, NULL, c
#define   uint16_tobytes_3(a, b, c) a,    b, c
#define   uint16_tobytes(...) VARIADIC(  uint16_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define uint16_frombytes_2(   b, c) NULL, b, c
#define uint16_frombytes_3(a, b, c)    a, b, c
#define uint16_frombytes(...) VARIADIC(uint16_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define   uint32_tobytes_2(a,    c) a, NULL, c
#define   uint32_tobytes_3(a, b, c) a,    b, c
#define   uint32_tobytes(...) VARIADIC(  uint32_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define uint32_frombytes_2(   b, c) NULL, b, c
#define uint32_frombytes_3(a, b, c)    a, b, c
#define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define   tobytes(a, ...) _Generic((a),                                                                                                 \
                                   const uint16*: uint16_tobytes,                                                                       \
                                   const uint32*: uint32_tobytes)  (VARIADIC2(  uint32_tobytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__))

#define frombytes(a, ...) _Generic((a),                                                                                                 \
                                         uint16*: uint16_frombytes,                                                                     \
                                         uint32*: uint32_frombytes)(VARIADIC2(uint32_frombytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__))

ومع اختيار اسم وظيفة VARIADIC، والتي لا يمكن دمجها مع _generic:

// winternitz() with 5 arguments is replaced with merkle_lamport() on those 5 arguments.

#define   merkle_lamport_5(a, b, c, d, e) a, b, c, d, e
#define   winternitz_7(a, b, c, d, e, f, g) a, b, c, d, e, f, g
#define   winternitz_5_name() merkle_lamport
#define   winternitz_7_name() winternitz
#define   winternitz(...) NVARIADIC(winternitz, NUMARG7(__VA_ARGS__), __VA_ARGS__)

خدعة أخرى باستخدام وحدات الماكرو:

#include <stdio.h>

#define func(...) FUNC(__VA_ARGS__, 15, 0)
#define FUNC(a, b, ...) func(a, b)

int (func)(int a, int b)
{
    return a + b;
}

int main(void)
{
    printf("%d\n", func(1));
    printf("%d\n", func(1, 2));
    return 0;
}

إذا تم تمرير حجة واحدة فقط، b يتلقى القيمة الافتراضية (في هذه الحالة 15)

نعم، يمكنك القيام ب Somehing Simulair، هنا عليك أن تعرف قوائم الوسيطة المختلفة التي يمكنك الحصول عليها ولكن لديك نفس الوظيفة للتعامل معها ثم كل شيء.

typedef enum { my_input_set1 = 0, my_input_set2, my_input_set3} INPUT_SET;

typedef struct{
    INPUT_SET type;
    char* text;
} input_set1;

typedef struct{
    INPUT_SET type;
    char* text;
    int var;
} input_set2;

typedef struct{
    INPUT_SET type;
    int text;
} input_set3;

typedef union
{
    INPUT_SET type;
    input_set1 set1;
    input_set2 set2;
    input_set3 set3;
} MY_INPUT;

void my_func(MY_INPUT input)
{
    switch(input.type)
    {
        case my_input_set1:
        break;
        case my_input_set2:
        break;
        case my_input_set3:
        break;
        default:
        // unknown input
        break;
    }
}

نعم

من خلال وحدات الماكرو

3 المعلمات:

#define my_func2(...) my_func3(__VA_ARGS__, 0.5)
#define my_func1(...) my_func2(__VA_ARGS__, 10)
#define VAR_FUNC(_1, _2, _3, NAME, ...) NAME
#define my_func(...) VAR_FUNC(__VA_ARGS__, my_func3, my_func2, my_func1)(__VA_ARGS__)

void my_func3(char a, int b, float c) // b=10, c=0.5
{
    printf("a=%c; b=%d; c=%f\n", a, b, c);
}

إذا كنت تريد الوسيطة الرابعة، فاحدد إضافة my_func3 إضافية. لاحظ التغييرات في var_func، my_func2 و my_func

4 المعلمات:

#define my_func3(...) my_func4(__VA_ARGS__, "default") // <== New function added
#define my_func2(...) my_func3(__VA_ARGS__, (float)1/2)
#define my_func1(...) my_func2(__VA_ARGS__, 10)
#define VAR_FUNC(_1, _2, _3, _4, NAME, ...) NAME
#define my_func(...) VAR_FUNC(__VA_ARGS__, my_func4, my_func3, my_func2, my_func1)(__VA_ARGS__)

void my_func4(char a, int b, float c, const char* d) // b=10, c=0.5, d="default"
{
    printf("a=%c; b=%d; c=%f; d=%s\n", a, b, c, d);
}

استثناء فقط على ذلك يطفو لا يمكن إعطاء المتغيرات القيم الافتراضية (ما لم تكن هذه هي الحجة الأخيرة في حالة 3 معلمات)، لأنهم يحتاجون إلى فترة (".")، والتي غير مقبولة داخل حجج ماكرو. ولكن يمكن معرفة العمل كما هو موضح في my_func2 ماكرو (من 4 حالة المعلمات)

برنامج

int main(void)
{
    my_func('a');
    my_func('b', 20);
    my_func('c', 200, 10.5);
    my_func('d', 2000, 100.5, "hello");

    return 0;
}

انتاج:

a=a; b=10; c=0.500000; d=default                                                                                                                                                  
a=b; b=20; c=0.500000; d=default                                                                                                                                                  
a=c; b=200; c=10.500000; d=default                                                                                                                                                
a=d; b=2000; c=100.500000; d=hello  

لماذا لا يمكننا القيام بذلك.

إعطاء الوسيطة الاختيارية القيمة الافتراضية. وبهذه الطريقة، لا يحتاج المتصل الوظيفة بالضرورة إلى تمرير قيمة الوسيطة. الحجة تأخذ القيمة الافتراضية. وسهولة هذه الحجة تصبح اختيارية للعميل.

ل

void foo (int a، int b = 0)؛

هنا هي حجة اختيارية.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top