Jak osiągnąć przeciążenie funkcji w C?


240

Czy jest jakiś sposób na osiągnięcie przeciążenia funkcji w C? Patrzę na proste funkcje do przeciążenia

foo (int a)  
foo (char b)  
foo (float c , int d)

Myślę, że nie ma prostej drogi; Szukam rozwiązań, jeśli takie istnieją.


6
Dlaczego chcesz to zrobić? C nie ma zdolności polimorficznych. Więc foo (typ losowy) jest niemożliwy. Po prostu zrób prawdziwe funki foo_i, foo_ch, foo_d itp.
jmucchiello 26.01.2009

4
Możesz pójść złą drogą, używając pustych wskaźników i identyfikatorów typów.
alk

11
Wydaje mi się, że powinienem zwrócić uwagę na fakt, że odpowiedź na to pytanie zmieniła się od czasu pierwotnego pytania , z nowym standardem C.
Leushenko

Odpowiedzi:


127

Istnieje kilka możliwości:

  1. funkcje stylu printf (wpisz jako argument)
  2. funkcje stylu opengl (wpisz nazwę funkcji)
  3. podzbiór c c ++ (jeśli możesz użyć kompilatora c ++)

1
czy możesz wyjaśnić lub podać linki do funkcji stylu Opengl?
FL4SOF,

1
@Lazer: Oto jedna prosta implementacja funkcji podobnej do printf.
Alexey Frunze,

12
Nie. Printf nie powoduje przeciążenia funkcji. używa vararg !!! A C nie obsługuje przeciążania funkcji.
hqt

52
@hqt Odpowiedź nigdy nie wspomina o przeciążeniu słowa.
Kyrias

1
@kyrias Jeśli odpowiedź nie dotyczy przeciążenia, dotyczy niewłaściwego pytania
Michael Mrozek

233

Tak!

W chwili, gdy pytanie zostało zadane, standardowy C (bez rozszerzeń) skutecznie zyskał obsługę przeciążania funkcji (nie operatorów), dzięki dodaniu _Genericsłowa kluczowego w C11. (obsługiwane w GCC od wersji 4.9)

(Przeładowanie nie jest tak naprawdę „wbudowane” w sposób pokazany w pytaniu, ale bardzo trudno jest wdrożyć coś, co działa w ten sposób.)

_Genericjest operatorem czasu kompilacji w tej samej rodzinie co sizeofi _Alignof. Jest to opisane w standardowej sekcji 6.5.1.1. Akceptuje dwa główne parametry: wyrażenie (które nie będzie oceniane w czasie wykonywania) oraz listę skojarzeń typów / wyrażeń, która wygląda trochę jak switchblok. _Genericpobiera ogólny typ wyrażenia, a następnie „przełącza” je, aby wybrać wyrażenie wyniku końcowego z listy dla swojego typu:

_Generic(1, float: 2.0,
            char *: "2",
            int: 2,
            default: get_two_object());

Powyższe wyrażenie ocenia na 2- typ wyrażenia kontrolującego jest int, więc wybiera wyrażenie powiązane z intwartością. Nic z tego nie pozostaje w czasie wykonywania. ( defaultKlauzula jest opcjonalna: jeśli ją odrzucisz, a typ nie będzie zgodny, spowoduje to błąd kompilacji).

Przydaje się to w przypadku przeciążenia funkcji, ponieważ może zostać wstawione przez preprocesor języka C i wybrać wyrażenie wynikowe na podstawie typu argumentów przekazanych do makra sterującego. Tak (przykład ze standardu C):

#define cbrt(X) _Generic((X),                \
                         long double: cbrtl, \
                         default: cbrt,      \
                         float: cbrtf        \
                         )(X)

To makro implementuje przeciążoną cbrtoperację, wysyłając typ makra do makra, wybierając odpowiednią funkcję implementacyjną, a następnie przekazując oryginalny argument makra do tej funkcji.

Aby wdrożyć oryginalny przykład, możemy to zrobić:

foo_int (int a)  
foo_char (char b)  
foo_float_int (float c , int d)

#define foo(_1, ...) _Generic((_1),                                  \
                              int: foo_int,                          \
                              char: foo_char,                        \
                              float: _Generic((FIRST(__VA_ARGS__,)), \
                                     int: foo_float_int))(_1, __VA_ARGS__)
#define FIRST(A, ...) A

W tym przypadku moglibyśmy zastosować default:skojarzenie dla trzeciego przypadku, ale to nie pokazuje, jak rozszerzyć zasadę na wiele argumentów. Efektem końcowym jest to, że możesz używać foo(...)w swoim kodzie bez obawy (wiele [1]) o rodzaj jego argumentów.


W przypadku bardziej skomplikowanych sytuacji, np. Funkcji przeciążających większą liczbę argumentów lub zmieniających się liczb, można użyć makr narzędziowych do automatycznego generowania statycznych struktur wysyłki:

void print_ii(int a, int b) { printf("int, int\n"); }
void print_di(double a, int b) { printf("double, int\n"); }
void print_iii(int a, int b, int c) { printf("int, int, int\n"); }
void print_default(void) { printf("unknown arguments\n"); }

#define print(...) OVERLOAD(print, (__VA_ARGS__), \
    (print_ii, (int, int)), \
    (print_di, (double, int)), \
    (print_iii, (int, int, int)) \
)

#define OVERLOAD_ARG_TYPES (int, double)
#define OVERLOAD_FUNCTIONS (print)
#include "activate-overloads.h"

int main(void) {
    print(44, 47);   // prints "int, int"
    print(4.4, 47);  // prints "double, int"
    print(1, 2, 3);  // prints "int, int, int"
    print("");       // prints "unknown arguments"
}

( tutaj implementacja ) Przy odrobinie wysiłku możesz zredukować liczbę podstawek do wyglądu przypominającego język z natywną obsługą przeciążania.

Nawiasem mówiąc , w C99 było już możliwe przeciążenie liczbą argumentów (nie typu).


[1] zauważ, że sposób, w jaki C ocenia typy, może cię jednak potknąć. Spowoduje to wybranie, foo_intjeśli na przykład spróbujesz przekazać literał znakowy, i musisz trochę zepsuć, jeśli chcesz, aby przeciążenia obsługiwały literały łańcuchowe. Mimo to ogólnie całkiem fajnie.


Na podstawie twojego przykładu wygląda na to, że jedyną przeciążoną funkcją jest funkcja makr. Zobaczę, czy rozumiem poprawnie: jeśli chcesz przeciążyć funkcje, użyjesz preprocesora do przekierowania wywołania funkcji na podstawie przekazanych typów danych, prawda?
Nick

Niestety, za każdym razem, gdy C11 zacznie się chwytać, zakładam, że MISRA nie zastosuje tej funkcji z tych samych powodów, dla których zabraniają listy zmiennych argumentów. Staram się trzymać MISRA całkiem blisko w moim świecie.
Nick

9
@Nick to wszystko przeciążenie. Jest to po prostu niejawnie obsługiwane w innych językach (np. Tak naprawdę nie można uzyskać „wskaźnika do przeciążonej funkcji” w żadnym języku, ponieważ przeciążenie oznacza wiele ciał). Zauważ, że nie może tego zrobić sam preprocesor, wymaga to jakiejś wysyłki typu; preprocesor po prostu zmienia wygląd.
Leushenko

1
Jak ktoś, kto dość dobrze zna C99 i chce się tego nauczyć, wydaje się to zbyt skomplikowane, nawet dla C.
Tyler Crompton

5
@TylerCrompton Jest oceniany podczas kompilacji.
JAB

75

Jak już wspomniano, przeciążenie w tym sensie, że masz na myśli, że nie jest obsługiwane przez C. Częstym idiomem do rozwiązania problemu jest spowodowanie, aby funkcja zaakceptowała oznaczony związek . Jest to realizowane przez structparametr, w którym structsam składa się z pewnego rodzaju wskaźnika typu, takiego jak an enumi unionróżnego rodzaju wartości. Przykład:

#include <stdio.h>

typedef enum {
    T_INT,
    T_FLOAT,
    T_CHAR,
} my_type;

typedef struct {
    my_type type;
    union {
        int a; 
        float b; 
        char c;
    } my_union;
} my_struct;

void set_overload (my_struct *whatever) 
{
    switch (whatever->type) 
    {
        case T_INT:
            whatever->my_union.a = 1;
            break;
        case T_FLOAT:
            whatever->my_union.b = 2.0;
            break;
        case T_CHAR:
            whatever->my_union.c = '3';
    }
}

void printf_overload (my_struct *whatever) {
    switch (whatever->type) 
    {
        case T_INT:
            printf("%d\n", whatever->my_union.a);
            break;
        case T_FLOAT:
            printf("%f\n", whatever->my_union.b);
            break;
        case T_CHAR:
            printf("%c\n", whatever->my_union.c);
            break;
    }

}

int main (int argc, char* argv[])
{
    my_struct s;

    s.type=T_INT;
    set_overload(&s);
    printf_overload(&s);

    s.type=T_FLOAT;
    set_overload(&s);
    printf_overload(&s);

    s.type=T_CHAR;
    set_overload(&s);
    printf_overload(&s); 
}

22
Dlaczego nie po prostu uczynić wszystkie whatevers do oddzielnych funkcji ( set_int, set_floatitp). Następnie „oznaczanie typem” staje się „dodaj nazwę typu do nazwy funkcji”. Wersja w tej odpowiedzi wymaga więcej pisania, większego kosztu działania, większej szansy na błędy, które nie zostaną wykryte podczas kompilacji ... Nie widzę żadnej korzyści z robienia tego w ten sposób! 16 głosów pozytywnych ?!
Ben

20
Ben, ta odpowiedź jest pozytywnie oceniona, ponieważ odpowiada na pytanie, zamiast po prostu powiedzieć „nie rób tego”. Masz rację, że bardziej idiomatyczne jest używanie w C osobnych funkcji, ale jeśli ktoś chce polimorfizmu w C, jest to dobry sposób na zrobienie tego. Ponadto ta odpowiedź pokazuje, w jaki sposób zaimplementowałbyś polimorfizm w czasie wykonywania w kompilatorze lub maszynie wirtualnej: otaguj wartość typem, a następnie wyślij na jej podstawie. Jest to zatem doskonała odpowiedź na pierwotne pytanie.
Nils von Barth,

20

Oto najjaśniejszy i najbardziej zwięzły przykład, w którym wykazałem przeciążenie funkcji w C:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

char *adds(char *a, char *b) {
    char *res = malloc(strlen(a) + strlen(b) + 1);
    strcpy(res, a);
    strcat(res, b);
    return res;
}

#define add(a, b) _Generic(a, int: addi, char*: adds)(a, b)

int main(void) {
    int a = 1, b = 2;
    printf("%d\n", add(a, b)); // 3

    char *c = "hello ", *d = "world";
    printf("%s\n", add(c, d)); // hello world

    return 0;
}

https://gist.github.com/barosl/e0af4a92b2b8cabd05a7


1
Myślę, że jest to duplikat stackoverflow.com/a/25026358/1240268 w duchu (ale z mniejszym wyjaśnieniem).
Andy Hayden

1
Zdecydowanie wolę 1 pojedynczy ciągły blok pełnego i uruchamialnego kodu niż krojenie i siekanie w kostkę, czyli # 1240268. Do każdego własnego.
Jay Taylor

1
Wolę odpowiedzi wyjaśniające, co robią i dlaczego działają. To nie robi ani jednego. „Najlepsze, co widziałem:” nie jest ekspozycją.
underscore_d

19

Jeśli twoim kompilatorem jest gcc i nie masz nic przeciwko robieniu aktualizacji ręcznie za każdym razem, gdy dodajesz nowe przeciążenie, możesz wykonać magię makr i uzyskać pożądany efekt pod względem rozmówców, nie jest tak przyjemnie pisać ... ale jest to możliwe

spójrz na __builtin_types_compatible_p, a następnie użyj go do zdefiniowania makra, które działa podobnie

#define foo(a) \
((__builtin_types_compatible_p(int, a)?foo(a):(__builtin_types_compatible_p(float, a)?foo(a):)

ale tak, paskudne, po prostu nie

EDYCJA: C1X otrzyma wsparcie dla ogólnych wyrażeń typu, które wyglądają tak:

#define cbrt(X) _Generic((X), long double: cbrtl, \
                              default: cbrt, \
                              float: cbrtf)(X)

13

Tak, w pewnym sensie.

Oto przykład:

void printA(int a){
printf("Hello world from printA : %d\n",a);
}

void printB(const char *buff){
printf("Hello world from printB : %s\n",buff);
}

#define Max_ITEMS() 6, 5, 4, 3, 2, 1, 0 
#define __VA_ARG_N(_1, _2, _3, _4, _5, _6, N, ...) N
#define _Num_ARGS_(...) __VA_ARG_N(__VA_ARGS__) 
#define NUM_ARGS(...) (_Num_ARGS_(_0, ## __VA_ARGS__, Max_ITEMS()) - 1) 
#define CHECK_ARGS_MAX_LIMIT(t) if(NUM_ARGS(args)>t)
#define CHECK_ARGS_MIN_LIMIT(t) if(NUM_ARGS(args) 
#define print(x , args ...) \
CHECK_ARGS_MIN_LIMIT(1) printf("error");fflush(stdout); \
CHECK_ARGS_MAX_LIMIT(4) printf("error");fflush(stdout); \
({ \
if (__builtin_types_compatible_p (typeof (x), int)) \
printA(x, ##args); \
else \
printB (x,##args); \
})

int main(int argc, char** argv) {
    int a=0;
    print(a);
    print("hello");
    return (EXIT_SUCCESS);
}

Wyjdzie 0 i witam .. z printA i printB.


2
int main (int argc, char ** argv) {int a = 0; drukuj (a); print („cześć”); return (EXIT_SUCCESS); } wyświetli 0 i witaj .. z printA i printB ...
Kapitan Barbossa,

1
__builtin_types_compatible_p, czy nie jest to specyficzny dla kompilatora GCC?
Sogartar

11

Poniższe podejście jest podobne do modelu a2800276 , ale z dodaną odrobiną magii makro C99:

// we need `size_t`
#include <stddef.h>

// argument types to accept
enum sum_arg_types { SUM_LONG, SUM_ULONG, SUM_DOUBLE };

// a structure to hold an argument
struct sum_arg
{
    enum sum_arg_types type;
    union
    {
        long as_long;
        unsigned long as_ulong;
        double as_double;
    } value;
};

// determine an array's size
#define count(ARRAY) ((sizeof (ARRAY))/(sizeof *(ARRAY)))

// this is how our function will be called
#define sum(...) _sum(count(sum_args(__VA_ARGS__)), sum_args(__VA_ARGS__))

// create an array of `struct sum_arg`
#define sum_args(...) ((struct sum_arg []){ __VA_ARGS__ })

// create initializers for the arguments
#define sum_long(VALUE) { SUM_LONG, { .as_long = (VALUE) } }
#define sum_ulong(VALUE) { SUM_ULONG, { .as_ulong = (VALUE) } }
#define sum_double(VALUE) { SUM_DOUBLE, { .as_double = (VALUE) } }

// our polymorphic function
long double _sum(size_t count, struct sum_arg * args)
{
    long double value = 0;

    for(size_t i = 0; i < count; ++i)
    {
        switch(args[i].type)
        {
            case SUM_LONG:
            value += args[i].value.as_long;
            break;

            case SUM_ULONG:
            value += args[i].value.as_ulong;
            break;

            case SUM_DOUBLE:
            value += args[i].value.as_double;
            break;
        }
    }

    return value;
}

// let's see if it works

#include <stdio.h>

int main()
{
    unsigned long foo = -1;
    long double value = sum(sum_long(42), sum_ulong(foo), sum_double(1e10));
    printf("%Le\n", value);
    return 0;
}

11

To może wcale nie pomóc, ale jeśli używasz clang, możesz użyć atrybutu przeciążalnego - Działa to nawet podczas kompilacji jako C

http://clang.llvm.org/docs/AttributeReference.html#overloadable

nagłówek

extern void DecodeImageNow(CGImageRef image, CGContextRef usingContext) __attribute__((overloadable));
extern void DecodeImageNow(CGImageRef image) __attribute__((overloadable));

Realizacja

void __attribute__((overloadable)) DecodeImageNow(CGImageRef image, CGContextRef usingContext { ... }
void __attribute__((overloadable)) DecodeImageNow(CGImageRef image) { ... }

10

W tym sensie, że masz na myśli - nie, nie możesz.

Możesz zadeklarować va_argfunkcję podobną do

void my_func(char* format, ...);

, ale musisz podać jakieś informacje o liczbie zmiennych i ich typach w pierwszym argumencie - podobnie jak printf()robi.


6

Zwykle brodawka wskazująca typ jest dołączana lub dodawana do nazwy. Możesz uniknąć makr w niektórych przypadkach, ale raczej zależy to od tego, co próbujesz zrobić. W C nie ma polimorfizmu, tylko przymus.

Za pomocą makr można wykonywać proste operacje ogólne:

#define max(x,y) ((x)>(y)?(x):(y))

Jeśli twój kompilator obsługuje typeof , w makrze można umieścić bardziej skomplikowane operacje. Możesz wtedy mieć symbol foo (x), aby obsługiwać tę samą operację dla różnych typów, ale nie możesz zmieniać zachowania między różnymi przeciążeniami. Jeśli chcesz rzeczywistych funkcji zamiast makr, możesz być w stanie wkleić typ do nazwy i użyć drugiego wklejenia, aby uzyskać do niego dostęp (nie próbowałem).


możesz wyjaśnić trochę więcej na temat podejścia opartego na makrach.
FL4SOF,

4

Odpowiedź Leushenko jest naprawdę fajna - tylko: fooprzykład nie kompiluje się z GCC, który się nie udaje foo(7), potykając się o FIRSTmakro i faktyczne wywołanie funkcji ( (_1, __VA_ARGS__)pozostając z nadwyżką przecinka. Dodatkowo mamy kłopoty, jeśli chcemy zapewnić dodatkowe przeciążenia , takie jak foo(double).

Postanowiłem więc rozwinąć odpowiedź nieco dalej, w tym pozwolić na przeciążenie pustki ( foo(void)- co spowodowało sporo problemów ...).

Pomysł jest teraz: Zdefiniuj więcej niż jeden rodzajowy w różnych makrach i pozwól wybrać poprawny zgodnie z liczbą argumentów!

Liczba argumentów jest dość łatwa, na podstawie tej odpowiedzi :

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)

#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y

To miłe, rozwiązujemy jeden SELECT_1lub SELECT_2(lub więcej argumentów, jeśli ich potrzebujesz / potrzebujesz), więc po prostu potrzebujemy odpowiednich definicji:

#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1),    \
        int: foo_int,                   \
        char: foo_char,                 \
        double: foo_double              \
)
#define SELECT_2(_1, _2) _Generic((_1), \
        double: _Generic((_2),          \
                int: foo_double_int     \
        )                               \
)

OK, dodałem już void overload - jednak ten faktycznie nie jest objęty standardem C, który nie dopuszcza pustych argumentów variadic, tzn. Polegamy na rozszerzeniach kompilatora !

Na początku puste wywołanie makra ( foo()) nadal generuje token, ale puste. Makro liczące faktycznie zwraca 1 zamiast 0, nawet przy pustym wywołaniu makra. Możemy „łatwo” wyeliminować ten problem, jeśli __VA_ARGS__ warunkowo wstawimy przecinek , w zależności od pustej listy:

#define NARG(...) ARG4_(__VA_ARGS__ COMMA(__VA_ARGS__) 4, 3, 2, 1, 0)

To wydawało się łatwe, ale COMMAmakro jest dość ciężkie; na szczęście temat jest już omawiany na blogu Jensa Gustedta (dzięki, Jens). Podstawowa sztuczka polega na tym, że makra funkcyjne nie są rozwijane, jeśli nie są nawiasami, w celu uzyskania dalszych wyjaśnień zajrzyj na blog Jensa ... Musimy tylko trochę zmodyfikować makra do naszych potrzeb (zamierzam użyć krótszych nazw i mniej argumentów za zwięzłością).

#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, _3, N, ...) N
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 1, 0)

#define SET_COMMA(...) ,

#define COMMA(...) SELECT_COMMA             \
(                                           \
        HAS_COMMA(__VA_ARGS__),             \
        HAS_COMMA(__VA_ARGS__ ()),          \
        HAS_COMMA(SET_COMMA __VA_ARGS__),   \
        HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)

#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3

#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
// ... (all others with comma)
#define COMMA_1111 ,

A teraz mamy się dobrze ...

Pełny kod w jednym bloku:

/*
 * demo.c
 *
 *  Created on: 2017-09-14
 *      Author: sboehler
 */

#include <stdio.h>

void foo_void(void)
{
    puts("void");
}
void foo_int(int c)
{
    printf("int: %d\n", c);
}
void foo_char(char c)
{
    printf("char: %c\n", c);
}
void foo_double(double c)
{
    printf("double: %.2f\n", c);
}
void foo_double_int(double c, int d)
{
    printf("double: %.2f, int: %d\n", c, d);
}

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)

#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y

#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1), \
        int: foo_int,                \
        char: foo_char,              \
        double: foo_double           \
)
#define SELECT_2(_1, _2) _Generic((_1), \
        double: _Generic((_2),          \
                int: foo_double_int     \
        )                               \
)

#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, N, ...) N

#define NARG(...) ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0)
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 0)

#define SET_COMMA(...) ,

#define COMMA(...) SELECT_COMMA             \
(                                           \
        HAS_COMMA(__VA_ARGS__),             \
        HAS_COMMA(__VA_ARGS__ ()),          \
        HAS_COMMA(SET_COMMA __VA_ARGS__),   \
        HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)

#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3

#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
#define COMMA_0011 ,
#define COMMA_0100 ,
#define COMMA_0101 ,
#define COMMA_0110 ,
#define COMMA_0111 ,
#define COMMA_1000 ,
#define COMMA_1001 ,
#define COMMA_1010 ,
#define COMMA_1011 ,
#define COMMA_1100 ,
#define COMMA_1101 ,
#define COMMA_1110 ,
#define COMMA_1111 ,

int main(int argc, char** argv)
{
    foo();
    foo(7);
    foo(10.12);
    foo(12.10, 7);
    foo((char)'s');

    return 0;
}

1

Czy nie możesz po prostu używać C ++ i nie używać wszystkich innych funkcji C ++ oprócz tej?

Jeśli nadal nie jest to tylko ścisłe C, poleciłbym zamiast tego funkcje variadic .


3
Nie, jeśli kompilator C ++ nie jest dostępny dla systemu operacyjnego, dla którego koduje.
Brian

2
nie tylko to, ale może chcieć C ABI, które nie ma w nim nazwy.
Spudd86,


-4

Mam nadzieję, że poniższy kod pomoże ci zrozumieć przeciążenie funkcji

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

int fun(int a, ...);
int main(int argc, char *argv[]){
   fun(1,10);
   fun(2,"cquestionbank");
   return 0;
}
int fun(int a, ...){
  va_list vl;
  va_start(vl,a);

  if(a==1)
      printf("%d",va_arg(vl,int));
   else
      printf("\n%s",va_arg(vl,char *));
}

2
Odpowiedź powinna wyjaśniać, co robi i dlaczego działa. Jeśli nie, to w jaki sposób ktoś może coś zrozumieć?
underscore_d

Tutaj nie ma przeciążenia.
melpomene

va_end nigdy nie został wywołany
user2262111
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.