source

C 디폴트 인수

goodcode 2022. 8. 31. 22:39
반응형

C 디폴트 인수

C의 함수에 디폴트 인수를 지정하는 방법이 있습니까?

와, 여기 사람들은 모두 정말 비관주의자예요.답은 '그렇다'입니다.

단순한 것이 아닙니다.마지막에는 핵심 기능, 지원 구조, 래퍼 기능, 래퍼 기능 주변 매크로가 있습니다.제 작업에는 이 모든 것을 자동화하는 일련의 매크로가 있습니다.플로우를 이해하면 같은 작업을 쉽게 할 수 있습니다.

다른 곳에서 이 내용을 작성했습니다.요약을 보충하기 위한 자세한 외부 링크는 다음과 같습니다.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);
}

이제 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)0으로 하다제 경험상, 이것은 주의해야 할 사항이지만, 필요에 따라 대처할 수 있습니다.-기본값이 0이 되는 시간의 절반입니다.

이름을 붙인 인수와 디폴트가 C의 코드화를 더 쉽고 재미있게 만들어 준다고 생각하기 때문에 이 문서를 작성하는데 어려움을 겪었습니다.그리고 C는 이 모든 것을 가능케 할 수 있을 만큼 단순하고 충분한 능력을 가지고 있기 때문에 훌륭합니다.

사실 그렇지 않아요.유일한 방법은 varargs 함수를 작성하고 발신자가 전달하지 않은 인수에 대해 수동으로 기본값을 입력하는 입니다.

네. :-) 하지만 기대했던 것과는 다릅니다.

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을 호출하게 됩니다.이는 기존 코드를 복사하거나 붙여넣지 않아도 되는 "반복하지 마십시오" 솔루션입니다.

매크로를 사용한 또 다른 방법:

#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을 합니다(이 경우는 15).

명명된 파라미터(만)를 기본값으로 사용하는 함수를 만들 수 있습니다.이것은 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

이 작업을 수행하는 가장 좋은 방법(상황에 따라서는 C++로 이동하여 '더 나은 C'로 사용하는 것이 좋습니다.클래스, 템플릿, 연산자 오버로드 또는 기타 고급 기능을 사용하지 않고 C++를 사용할 수 있습니다.

그러면 함수 오버로드와 기본 파라미터(및 사용하도록 선택한 다른 기능)를 가진 C의 배리언트가 나타납니다.C++의 제한된 서브셋만을 사용하는 것에 대해 진지하게 생각하고 있는 경우는, 조금 단련하면 됩니다.

많은 사람들이 C++를 이런 식으로 사용하는 것은 좋지 않다고 말할 것이고, 그들이 일리가 있을지도 모른다.단지 의견일 뿐이므로, 모든 것을 구입하지 않고, C++의 기능을 사용할 수 있다고 생각합니다.C++가 성공한 이유의 중요한 부분은 초기 단계에서 많은 프로그래머들이 C++를 정확히 이렇게 사용했기 때문이라고 생각합니다.

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

사용자가 무엇을 써야 할지 모르는 경우 다음 요령이 도움이 될 수 있습니다.

사용 예

나는 옌스 구스테트의 답변을 개선했다.

  1. 인라인 함수는 사용되지 않습니다.
  2. 기본값은 전처리 중에 계산됩니다.
  3. 모듈러 재사용 가능 매크로
  4. 허용된 기본값에 대해 충분한 인수가 없는 경우와 유의하게 일치하는 컴파일러 오류를 설정할 수 있습니다.
  5. 인수 유형이 모호하지 않은 상태로 유지되는 경우 매개 변수 목록의 꼬리를 구성하는 데 기본값이 필요하지 않습니다.
  6. C11과의 상호 운용 _ Generic
  7. 인수 수에 따라 함수 이름을 바꿉니다!

변종의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__))

또한 _Generic:_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__)

단답: 아니요.

조금 더 긴 답변:옵션 인수에 대해 해석하는 문자열을 전달하는 오래된 회피책이 있습니다.

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

여기서 opt에는 "name=value" 쌍 또는 다른 것이 포함될 수 있으며, 다음과 같이 부릅니다.

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

분명히 이것은 가끔씩만 유용하다.일반적으로 기능 패밀리에 대한 단일 인터페이스를 필요로 하는 경우.


이 접근방식은 프로페셔널 프로그램이 c++(를 들어 ROOT)로 작성한 입자물리학 코드에서도 볼 수 있습니다.백호환성을 유지하면서 거의 무기한으로 확장할 수 있는 것이 큰 장점입니다.

또 다른 옵션은structs:

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

네.

매크로 경유

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

float 변수에는 매크로 인수 내에서 허용되지 않는 마침표(.')가 필요하기 때문에 float 변수에는 기본값을 지정할 수 있습니다., 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  

아니요.

네, C99의 기능을 사용하면 이 작업을 수행할 수 있습니다.이는 새로운 데이터 구조 등을 정의하지 않고, 함수가 호출 방법을 런타임에 결정할 필요도 없고, 계산 오버헤드도 없습니다.

상세한 것에 대하여는, 다음의 URL 에 있는 제 투고를 참조해 주세요.

http://gustedt.wordpress.com/2010/06/03/default-arguments-for-c99/

젠스

아니요, 그건 C++ 언어 기능입니다.

아니요. 단, 함수(또는 매크로) 세트를 사용하여 기본 arg를 사용하여 근사치를 산출하는 것을 고려할 수 있습니다.

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

일반적으로는 없습니다만, gcc에서는 funcA()의 마지막 파라미터를 매크로에서 옵션으로 설정할 수 있습니다.

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

왜 안 되지?

옵션 인수에 기본값을 지정합니다.이와 같이 함수의 호출자가 반드시 인수 값을 전달할 필요는 없습니다.인수는 기본값을 사용합니다.이 인수는 클라이언트의 옵션입니다.

예를 들어,

void foo(int a, int b = 0);

여기서 b는 옵션 인수입니다.

아니요.

최신 C99 규격도 이를 지원하지 않습니다.

네, 어떤 작업을 동시에 수행할 수 있습니다.여기에서는 얻을 수 있는 다양한 인수 목록을 알아야 하지만 모든 작업을 처리할 수 있는 기능은 동일합니다.

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

C에만 VARGS를 사용할 필요는 없습니다.여기 예가 있습니다.

int funcA_12(int a1, int a2) { ... }

#define funcA(a1) funcA_12(a1, 0)

이 답변은 위의 두 함수 방식과 매우 유사하지만, 이 경우 인수를 정의하는 함수 이름에 매크로를 사용합니다.

https://github.com/cindRoberta/C/blob/master/structure/function/default_parameter.c

#include<stdio.h>

void f_impl(int a, float b) {
  printf("%d %g\n", a, b);
}

#define f_impl(...) f_macro(__VA_ARGS__, 3.7)
#define f_macro(a, b, ...) f_impl(a, b)

int main(void) {
  f_impl(1);
  f_impl(1, 2, 3, 4);

  return 0;
}

나는 이것을 더 나은 방법으로 하는 방법을 알고 있다.【NULL】【NULL】【NULL】【NULL】【NULL】【NULL】【NULL】【NULL】【NULL】★ 다음 값이 '있다'인지 합니다.NULL디폴트값으로 변경합니다.

void func(int x){
if(x == NULL)
  x = 2;
....
}

단, 경고가 발생합니다.파라미터 값이 다음과 같으면 아무것도 하지 않는 값을 할당하는 것이 좋습니다.

void func(int x){
if(x == 1)
  x = 2;
....
}

위의 예에서는 다음과 같습니다.x1 함수는 을 '이러다'로 .2;

@user904963 덕분에 편집: 모든 범위의 숫자를 포함해야 하는 경우 파라미터를 기본값으로 설정할지 여부를 함수에 알리기 위해 다른 인수를 추가하는 것은 어렵지 않습니다.

void func(int x, bool useDefault){
if(useDefault) //useDefault == true
  x = 2;
....
}

ᄋ, ᄋ, ᄋ, ᄋ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ.stdbool.h

언급URL : https://stackoverflow.com/questions/1472138/c-default-arguments

반응형