Domyślne argumenty C.


279

Czy istnieje sposób określenia domyślnych argumentów funkcji w C?


6
chcę tylko nieco lepszego C, a nie C ++. pomyśl C +. z szeregiem drobnych ulepszeń wprowadzonych z C ++, ale nie z wielkim bałaganem. I proszę, nie ma innego programu ładującego łącza. powinien być tylko kolejnym krokiem podobnym do preprocesora. znormalizowany. wszędzie ...
ivo Welch

Podobne pytanie , którego nie widziałem na pasku bocznym.
Shelby Moore III,

Powiedziałbym, żeby przestać być barbarzyńcą i nauczyć się używać C ++ (11, ...) cóż - jk! / zgasiłem płomienie ... ale ... pokochasz to ... hahaha, nie mogę się powstrzymać, przepraszam.
moodboom 17.03.17

Odpowiedzi:


147

Nie całkiem. Jedynym sposobem byłoby napisanie funkcji varargs i ręczne wpisanie wartości domyślnych dla argumentów, których nie wywołuje program wywołujący.


22
Nienawidzę braku sprawdzania podczas korzystania z varargs.
dmckee --- były moderator kociak

16
Tak jak powinieneś; Właściwie to nie polecam; Chciałem tylko powiedzieć, że to możliwe.
Eli Courtwright

4
Jak jednak sprawdzić, czy rozmówca mija argument, czy nie? Myślę, że to zadziała, nie dzwonisz, żeby ci powiedzieć, że nie zdał? Myślę, że to sprawia, że ​​całe podejście jest nieco mniej użyteczne - dzwoniący może również wywołać funkcję o innej nazwie.
Johannes Schaub - litb

3
open(2)Połączenia system wykorzystuje to dla opcjonalnego argumentu, które mogą być obecne w zależności od wymaganych argumentów i printf(3)odczytuje ciąg formatu, który określa ile argumenty nie będzie. Oba używają varargs dość bezpiecznie i skutecznie, i chociaż z pewnością można je zepsuć, printf()szczególnie wydaje się dość popularne.
Chris Lutz

4
@Eli: Nie wszystkie kompilatory C są gcc. Trwa zaawansowana magia kompilatora, która ostrzega, gdy argumenty printf () nie pasują do ciągu formatu. I nie sądzę, że można uzyskać podobne ostrzeżenia dla własnych funkcji variadic (chyba że używają tego samego stylu łańcucha formatu).
tomlogic

280

Wow, wszyscy są tutaj takim pesymistą. Odpowiedź brzmi tak.

To nie jest trywialne: do końca będziemy mieli podstawową funkcję, strukturę wspierającą, funkcję otoki i makro wokół funkcji otoki. W mojej pracy mam zestaw makr do automatyzacji tego wszystkiego; kiedy zrozumiesz przepływ, będzie ci łatwo zrobić to samo.

Napisałem to gdzie indziej, więc tutaj jest szczegółowy zewnętrzny link do uzupełnienia podsumowania tutaj: http://modelingwithdata.org/arch/00000022.htm

Chcielibyśmy się odwrócić

double f(int i, double x)

w funkcję, która przyjmuje wartości domyślne (i = 8, x = 3,14). Zdefiniuj strukturę towarzyszącą:

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

Zmień nazwę swojej funkcji f_basei zdefiniuj funkcję otoki, która ustawia wartości domyślne i wywołuje bazę:

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

Teraz dodaj makro, używając makr rozmaitości C. W ten sposób użytkownicy nie muszą wiedzieć, że faktycznie f_argswypełniają strukturę i myślą, że robią to, co zwykle:

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

OK, teraz wszystkie następujące elementy będą działać:

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

Sprawdź reguły, w jaki sposób inicjalizatory złożone ustawiają wartości domyślne dla dokładnych reguł.

Jedna rzecz, która nie zadziała: f(0)ponieważ nie możemy odróżnić brakującej wartości od zera. Z mojego doświadczenia wynika, że ​​jest to coś, na co należy uważać, ale można sobie z tym poradzić, gdy zajdzie taka potrzeba - połowa czasu, gdy domyślnie wynosi zero.

Zadałem sobie trud napisania tego, ponieważ myślę, że nazwane argumenty i wartości domyślne naprawdę sprawiają, że kodowanie w C jest łatwiejsze i jeszcze przyjemniejsze. A C jest niesamowity, ponieważ jest tak prosty i wciąż ma wystarczająco dużo, aby wszystko to było możliwe.


16
+1 kreacja! Ma swoje ograniczenia, ale wprowadza także nazwane parametry do tabeli. Zauważ, że {}(pusty inicjator) to błąd C99.
u0b34a0f6ae

28
Oto jednak coś dla Ciebie: standard pozwala wielokrotnie określać nazwanych członków, późniejsze zastąpienie. Tak więc tylko dla nazwanych parametrów możesz rozwiązać problem domyślny i pozwolić na puste wywołanie. #define vrange(...) CALL(range,(param){.from=1, .to=100, .step=1, __VA_ARGS__})
u0b34a0f6ae

3
Mam nadzieję, że błędy kompilatora są czytelne, ale jest to świetna technika! Prawie wygląda jak kwargs python.
totowtwo

6
@RunHolt Chociaż z pewnością prostsze, nie jest obiektywnie lepsze; nazwane parametry mają takie zalety, jak łatwość odczytu wywołań (kosztem czytelności kodu źródłowego). Jedna jest lepsza dla programistów źródła, druga jest lepsza dla użytkowników funkcji. Trochę pochopnie jest po prostu wyrzucić „ten jest lepszy!”
Alice,

3
@DawidPi: C11 6.7.9 (19), przy inicjowaniu agregatów: „wszystkie podobiekty, które nie zostały jawnie zainicjowane, powinny być inicjowane domyślnie tak samo jak obiekty o statycznym czasie przechowywania”. Jak wiadomo, elementy o czasie statycznym są inicjowane na zero | NULL | \ 0. [To też było w C99.]
.

161

Tak. :-) Ale nie w sposób, którego można by się spodziewać.

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

Niestety, C nie pozwala na przeciążanie metod, więc otrzymujesz dwie różne funkcje. Mimo to, wywołując f2, faktycznie dzwoniłbyś do f1 z wartością domyślną. Jest to rozwiązanie „Don't Repeat Yourself”, które pomaga uniknąć kopiowania / wklejania istniejącego kodu.


24
FWIW, wolałbym użyć liczby na końcu funkcji, aby wskazać liczbę argumentów, które potrzebuje. Ułatwia to korzystanie z dowolnej dowolnej liczby. :)
Macke

4
To zdecydowanie najlepsza odpowiedź, ponieważ pokazuje prosty sposób na osiągnięcie tego samego celu. Mam funkcję, która jest częścią stałego interfejsu API, którego nie chcę zmieniać, ale potrzebuję go, aby przyjąć nowy parametr. Oczywiście, jest to tak oślepiająco oczywiste, że mi tego brakowało (utknąłem na myśleniu o domyślnym parametrze!)
RunHolt,

4
f2 może być również makrem preprocesora
osvein

41

Możemy tworzyć funkcje, które używają nazwanych parametrów (tylko) dla wartości domyślnych. To kontynuacja odpowiedzi 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);                                                             
}    

Standard C99 określa, że ​​późniejsze nazwy w inicjalizacji zastępują poprzednie elementy. Możemy również mieć pewne standardowe parametry pozycyjne, wystarczy odpowiednio zmienić makro i podpis funkcji. Domyślnych parametrów wartości można używać tylko w nazwanym stylu parametrów.

Wyjście programu:

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

2
Wydaje się to łatwiejszą i prostszą implementacją niż rozwiązanie Wim ten Brink lub BK. Czy są jakieś wady tej implementacji, których inni też nie mają?
stephenmm

24

OpenCV używa czegoś takiego:

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

Jeśli użytkownik nie wie, co powinien napisać, ta sztuczka może być pomocna:

przykład użycia


19

Nie.

Nie obsługuje tego nawet najnowszy standard C99.


proste nie byłoby jeszcze lepsze;)
KevinDTimm

21
@kevindtimm: To nie jest możliwe, SO wymaga minimalnej długości odpowiedzi. Próbowałem. :)
zrelaksuj się

2
Proszę odnieść się do mojej odpowiedzi. :)
chaos


14

Krótka odpowiedź: Nie.

Nieco dłuższa odpowiedź: istnieje stare, stare obejście, w którym przekazujesz ciąg, który analizujesz pod kątem opcjonalnych argumentów:

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

gdzie opt może zawierać parę „nazwa = wartość” lub coś, i jak byś nazwał

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

Oczywiście jest to przydatne tylko czasami. Zasadniczo, gdy chcesz mieć pojedynczy interfejs z rodziną funkcji.


Wciąż znajdziesz to podejście w kodach fizyki cząstek, które są pisane przez profesjonalne programy w c ++ (jak na przykład ROOT ). Jego główną zaletą jest to, że można go rozszerzać prawie bez końca, zachowując kompatybilność wsteczną.


2
Połącz to z varargs i masz mnóstwo zabawy!
David Thornley,

5
Użyłbym niestandardowego structi kazałbym wywołującemu go utworzyć, wypełnić pola dla różnych opcji, a następnie przekazać je przez adres lub przekazać NULLdomyślne opcje.
Chris Lutz

Kopiowanie wzorców kodu z ROOT to okropny pomysł!
innisfree

13

Prawdopodobnie najlepszym sposobem na zrobienie tego (co może, ale nie musi być możliwe w twoim przypadku, w zależności od twojej sytuacji) jest przejście do C ++ i użycie go jako „lepszego C”. Możesz używać C ++ bez użycia klas, szablonów, przeciążania operatora lub innych zaawansowanych funkcji.

To da ci wariant C z przeciążeniem funkcji i parametrami domyślnymi (i wszystkimi innymi funkcjami, które wybierzesz). Musisz być trochę zdyscyplinowany, jeśli naprawdę poważnie podchodzisz do używania tylko ograniczonego podzbioru C ++.

Wiele osób powie, że używanie C ++ w ten sposób jest okropnym pomysłem i mogą mieć rację. Ale to tylko opinia; Myślę, że warto korzystać z funkcji C ++, z którymi czujesz się komfortowo, bez konieczności kupowania całości. Myślę, że istotną przyczyną sukcesu C ++ jest to, że okropnie wielu programistów wykorzystało go we wczesnych dniach właśnie w ten sposób.


13

Jeszcze inna opcja wykorzystuje 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);

9

Nie.


2
jakie jest obejście? Widzę, że jest 20202020w kodzie szesnastkowym, ale jak go wpisać?
Lazer

5

Kolejna sztuczka z użyciem makr:

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

Jeśli przekazany zostanie tylko jeden argument, botrzymuje wartość domyślną (w tym przypadku 15)


4

Nie, ale możesz rozważyć użycie zestawu funkcji (lub makr) w celu przybliżenia przy użyciu domyślnych argumentów:

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

4

Tak, dzięki funkcjom C99 możesz to zrobić. Działa to bez definiowania nowych struktur danych lub mniej i bez konieczności decydowania przez funkcję w czasie wykonywania, jak zostanie ona wywołana, i bez narzutu obliczeniowego.

Szczegółowe wyjaśnienie znajduje się w moim poście pod adresem

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

Jens


Zobacz także moją odpowiedź, która pochodzi od twojej.
Shelby Moore III,

1
Podaj przykład.
user877329,

3

Generalnie nie, ale w gcc Możesz ustawić ostatni parametr funcA () jako opcjonalny w makrze.

W funcB () używam specjalnej wartości (-1), aby zasygnalizować, że potrzebuję wartości domyślnej parametru „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

Poprawiłem Jens Gustedt za odpowiedź , tak aby:

  1. funkcje inline nie są używane
  2. wartości domyślne są obliczane podczas przetwarzania wstępnego
  3. modułowe makra wielokrotnego użytku
  4. możliwe jest ustawienie błędu kompilatora, który w sposób znaczący pasuje do przypadku niewystarczających argumentów dla dozwolonych wartości domyślnych
  5. wartości domyślne nie są wymagane do utworzenia ogona listy parametrów, jeśli typy argumentów pozostaną jednoznaczne
  6. współdziała z C11 _ Generic
  7. różnicuj nazwę funkcji o liczbę argumentów!

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

Uproszczony scenariusz użytkowania:

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

I z _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__))

I z wyborem nazwy funkcji variadic, której nie można łączyć z _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__)

1

TAK

Poprzez makra

3 parametry:

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

Jeśli chcesz czwartego argumentu, musisz dodać dodatkowy my_func3. Zwróć uwagę na zmiany w VAR_FUNC, my_func2 i my_func

4 parametry:

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

Jedynym wyjątkiem jest to, że zmienne zmiennoprzecinkowe nie mogą mieć wartości domyślnych ( chyba że jest to ostatni argument, jak w przypadku 3 parametrów ), ponieważ potrzebują kropki („.”), Co nie jest akceptowane w argumentach makr. Ale może wymyślić obejście, jak widać w makrze my_func2 ( przypadek 4 parametrów) )

Program

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

Wynik:

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  

0

Tak, możesz zrobić coś simulair, tutaj musisz znać różne listy argumentów, które możesz uzyskać, ale masz tę samą funkcję do obsługi, to wszystko.

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

-6

Dlaczego nie możemy tego zrobić?

Podaj opcjonalny argument wartość domyślną. W ten sposób obiekt wywołujący funkcję niekoniecznie musi przekazywać wartość argumentu. Argument przyjmuje wartość domyślną. I łatwo ten argument staje się opcjonalny dla klienta.

Na przykład

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

Tutaj b jest argumentem opcjonalnym.


10
Oszałamiający wgląd, problem polega na tym, że C nie obsługuje opcjonalnych argumentów ani przeciążonych funkcji, więc bezpośrednie rozwiązanie się nie kompiluje.
Thomas
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.