2009-11-06 2 views
10

제네릭 형식의 C stdarg.h lib를 사용하려고합니다. int 유형은 제 일반적인 유형입니다> 이해하려면 읽어주세요. 그래서, 내 문제는 :C의 변수 인수, 제네릭 형식의 값을 얻는 방법?

나는 변수의 인수를 허용 함수가 있습니다. 내 프로그램에서

void function (int paramN, ...);

같은 변수 인수의 유형 인 알 방법이 없습니다, 그것은 문자, 배열의 int, short 타입, 기능 점수 등이 있습니다 ... 같은

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

그래서, 내가 INT가하는 86 (32 비트) 시스템에서 32 비트이며, 생각,이 모든 메모리 주소를 개최한다. 그래서 int로 모든 인수를 얻으면 문제가되지 않을 것입니다. 예를 들어, "이봐, 나는 문자열이다."이 문자열의 주소는 일반적으로 32 비트 변수에 들어 맞습니다. 던지다.

오른쪽?
사용할 수 있습니까?
참고 : printf와 같은 기능을하고 싶지 않습니다. (이 해결책은이 경우 적합하지 않습니까?)

모든 대답에 감사드립니다.
그리고 나쁜 영어로 죄송합니다.

답변

9

설명하는대로 할 수 없습니다.

호출자는 스택에 인수를 넣을 수 있지만 형식에 대한 정보는 저장하지 않으므로 호출 수신자가 찾을 수있는 방법 (최소한 변수 크기)이 있어야합니다.

  • 모든 유형이 알려진 프로토 타입이있는 기능에는 문제가 없습니다.

  • 가변 개수 또는 매개 변수 (가변)가있는 함수의 경우 더 까다 롭습니다. 각 변수를 읽으려면 각 인수에 대해 va_arg를 호출해야하며 va_arg에 대한 유형을 제공해야합니다. 실제 타입이 아닌 타입을 제공하면 컴파일러는 불평하지 않습니다 (런타임 정보가 될 수는 없지만) 어떤 일이 일어날 수 있습니다 (일반적으로 나쁜 것).이 유형을 통과 할

은 따라서 당신은 .

어떤 경우에는 유형을 예측할 수 있습니다 (예 : 계수기 뒤에 일부 int 등이 있음). 일반적으로 매개 변수로 인코딩하여 전달합니다. 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); 
} 

이 exemple는 C99에 정의 된 새로운 stdarg 가변 헤더를 사용합니다.그것을 사용하려면 함수에 하나 이상의 고정 매개 변수가 있어야합니다 (이 예제에서는 count입니다). 좋은 점은 만약 당신이 함수 (즉, myfunc(int count1, ..., int count2, ...) 같은 것)에 여러 변수 목록을 가질 수 있다면. 나쁜 점은 순전히 가변적 인 함수 (예 : 이전의 형식과 같은 myfunc (...)과 같은 것)를 사용할 수 없다는 것입니다. varargs 호환성 헤더를 사용하여 이전 형식을 계속 사용할 수는 있지만 더 복잡하고 당신이 타입을 필요로하기 때문에 거의 필요 없다.리스트가 끝났다는 것을 알기위한 몇가지 방법이있다. (카운트를하는 유일한 방법은 아니지만, '종결 자'를 사용할 수있다.)

+0

참고 : printf와 같은 기능을하고 싶지 않습니다. (이 해결책은이 경우에 맞지 않습니까?) – drigoSkalWalker

+0

Woww, 감사합니다 kriss, 전문가입니다, 시간 내 주셔서 감사합니다. thankx 많이; D 조 – drigoSkalWalker

14

각 매개 변수 &에 void * (또는 형식화 된 구조체)를 사용하려면 "type"인수 (정수)가있는 구조를 사용하십시오. 실제 값을 포함하는 포인터/합집합입니다.

즉, 각 매개 변수는 유형이 지정된 구조에 대한 포인터와 함께 전달됩니다. 이 유형이 지정된 구조의 각 인스턴스에는 값이 들어 있습니다. 이 "값"의 유형은이 유형이 지정된 구조에 포함됩니다.

더 나은 예 :

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

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

내가 DBUS했다 우연히 같은 트릭의 최신 예.

+0

설명 할 수 있는가? 더 나은이? 내가 이해할 수 있다고 생각, 예를 들어, 가능한 형식 (INT, SHORT, CHAR, ARRAY ... ETC}, 열거 형을 만들 수 있습니다, 나는 두 필드, 하나의 필드 열거 형의 유형이 있습니까? 그리고 다른 하나는 값이 맞습니까? 올바른가? 값을 유지하는 방법은 무엇입니까? 내가 잘못하면 답장을 보내 주셔서 감사합니다. – drigoSkalWalker

+0

@drigo : 정확합니다. – jldupont

3

당신이 명시 적으로하지 않는 한 당신이 전달한 인수의 유형에 대한 정보가 컴파일 후에 유지되지 않기 때문에 설명하는 방식으로 가변 인수를 사용하여이를 수행 할 수 없습니다. 문자열을 나타내는 메모리의 비트가 숫자 또는 다른 것을 나타낼 수 있기 때문에 인수 변수의 주소를 전달한다고해도이 사실을 알 수 없습니다.

varargs를 변수 유형과 함께 사용하려면 형식 정보를 인수 자체에 저장하거나 (예 : 답변에서 jldupont에 설명 된대로) 정보를 변수가 아닌 인수에 저장할 수 있습니다 (예 : 형식 문자열은 printf과 같습니다.

+0

참고 : printf와 같은 기능을하고 싶지 않습니다. (이 해결책은이 경우 적합하지 않습니다.) – drigoSkalWalker

+1

나는 이해합니다. 나는 왜 다른 사람들이 당신이 거부하고있는이 해결책을 선택하는지 분명히하려고 노력하고 있습니다. –

+0

Ok 네이선, 답변 해 주셔서 감사합니다. 다른 방법으로이 작업을 수행하고 싶습니다. 어쨌든, 당신의 대답 녀석을위한 감사합니다! ; D – drigoSkalWalker

1

http://en.wikipedia.org/wiki/Stdarg.h에서 대처 :

함수에 전달 된 이름 인수 [...] 유형을 결정하는 정의 된 메커니즘이 없다. 이 함수는 어떻게 든이를 알거나 결정해야하며, 그 수단은 다양합니다.

사용자의 기능입니다. 은 인수가 문자열 인 인수로부터 알 수 없습니다. 이 있어야 함수에 대해 어떻게 생각하는지 알 수 있습니다. 그렇기 때문에 printf 대회가 ​​매우 일반적입니다.

+0

참고 : printf와 같은 기능을하고 싶지 않습니다. (이 해결책은이 경우 적합하지 않습니까?) – drigoSkalWalker

+2

우리는 그것을 이해했습니다. 당신이 요구 한 것을 성취 할 수있는 쉬운 방법이 없습니다. varags 함수에 인수에 어떤 데이터가 있는지 알려주는 방법을 찾아야합니다. 'printf' 규칙 만이 유일한 선택은 아니지만 (링크 또는 jldupont의 답변 참조) 항상 말해야합니다. – dmckee

+0

Ok dmckee, 답장을 보내 주셔서 감사합니다. 다른 방법으로이 작업을 수행하고 싶습니다. 어쨌든, 당신의 대답 녀석을위한 감사합니다! ; D – drigoSkalWalker

0

stdint.h에 정의 된 u? intptr_t를 사용해보십시오. 이것은 포인터를 담을만큼 충분히 큰 정수입니다.

부동 소수점 숫자를 전달할 때 어떤 일이 일어나는지 생각해 볼 수도 있습니다. 그것은 변환되어 double로 전달됩니다. 프로그램이 int를 기대하고 있다면 스택의 뷰를 파괴 할 것이다. 아야. 그리고 컴파일러는 이것을 잡을 수 없습니다.

함수가 정의 된대로 paramN에 비트 인코딩이없는 경우 (enam 또는 bitfield이거나 적어도 서명이 있어야 함) 수신하는 매개 변수의 유형을 알 수있는 방법이 없습니다. 그렇기 때문에 그 (것)들과 가진 유용한 아무거나를 할 수있을 것 같지 않다. C에는 해당 객체에 대한 런타임 유형 정보가 없습니다.

정확히 무엇을하려고합니까?

+0

안녕하세요 남자, 귀하의 대답은 위대한, 나는 가변 인수 내 함수에 몇 가지 함수 지점을 전달하려고하지만, 사람 사이에, 나는 문자열이나 int도 전달할 것입니다, 나는 내가 사용하는 데 아무 문제가 없다고 생각합니다 int를 수행하거나, intptr_t를 사용하려면, 그것에 대해 무엇을 말해 주어야합니까? 귀하의 대답을 주셔서 감사합니다 o/ – drigoSkalWalker

+0

귀하의 프로그램은 각 인수의 유형을 알고 있어야하며 귀하의 함수에서 이것을 알 수있는 유일한 방법은 인수를 통해 그 정보를 어떻게 든 전달하는 것입니다. 만약 printf를 printf ("% s : % d", "string", i)와 같이 호출한다면 은 첫 번째 인수를 문자열로 사용하고 두 번째를 int로 사용합니다. 사용자가 printf ("% s : % d", 0, "x") printf와 같은 다른 내용을 전달하면 프로그램에 오류가 발생합니다. 인수를 사용하는 방법을 함수에 어떻게 말하고 있습니까? 어느 것이 int인지, 문자열인지, 포인터인지 & c인지 어떻게 알 수 있습니까? –