Czy lepiej używać argumentów C void " void foo (void)" czy nie "void foo ()"? [duplikat]

To pytanie ma już odpowiedź tutaj:

Co jest lepsze: void foo() Czy void foo(void)? Z void wygląda brzydko i niespójnie, ale powiedziano mi, że jest dobry. Czy to prawda?

Edit: wiem, że niektóre stare Kompilatory robią dziwne rzeczy, ale jeśli używam tylko GCC, to void foo() Ok? Czy foo(bar); zostanie zaakceptowana?

 187

6 answers

void foo(void);

To jest poprawny sposób, aby powiedzieć "brak parametrów" w C, i działa również w C++.

Ale:

void foo();

Oznacza różne rzeczy w C i C++! W C oznacza "może przyjmować dowolną liczbę parametrów nieznanych typów", a w C++ oznacza to samo co foo(void).

Funkcje listy argumentów zmiennych są z natury nie-typesafe i należy ich unikać tam, gdzie to możliwe.

 219
Author: Daniel Earwicker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-28 23:48:30

Istnieją dwa sposoby określania parametrów w C. Jeden używa listy identyfikatorów, a drugi listy typów parametrów. Lista identyfikatorów może być pominięta, ale lista typów nie może. Tak więc, aby powiedzieć, że jedna funkcja nie pobiera argumentów w definicji funkcji, robisz to za pomocą (pominiętej) listy identyfikatorów

void f() {
    /* do something ... */
}

I to z listą typu parametru:

void f(void) {
    /* do something ... */
}

Jeśli na liście typu parametru jedynym typem parametru jest void (musi mieć wtedy nazwę), to oznacza to, że funkcja nie pobiera argumentów. Ale te dwa sposoby definiowania funkcji mają różnicę w odniesieniu do tego, co deklarują.

Listy identyfikacyjne

Pierwsza definiuje, że funkcja przyjmuje określoną liczbę argumentów, ale ani Liczba nie jest przekazywana, ani typy tego, co jest potrzebne - jak w przypadku wszystkich deklaracji funkcji, które używają list identyfikatorów. Więc rozmówca musi znać typy i liczyć dokładnie przed ręką. Więc jeśli dzwoniący wywoła funkcję argumentując to, zachowanie jest nieokreślone. Stos może zostać uszkodzony na przykład, ponieważ wywołana funkcja oczekuje innego układu, gdy uzyska kontrolę.

Używanie list identyfikatorów w parametrach funkcji jest przestarzałe. Był używany w dawnych czasach i jest nadal obecny w wielu kodach produkcyjnych. Mogą one powodować poważne niebezpieczeństwo z powodu tych promocji argumentów (jeśli promowany typ argumentu nie pasuje do typu parametru definicji funkcji, zachowanie jest / align = "left" / ) i są oczywiście znacznie mniej bezpieczne. Więc zawsze używaj void thingy dla funkcji bez parametrów, zarówno w only-deklaracjach, jak i definicjach funkcji.

Lista typów parametrów

Drugi definiuje, że funkcja pobiera zero argumentów i komunikuje to - podobnie jak we wszystkich przypadkach, w których funkcja jest deklarowana za pomocą listy typów parametrów, która jest nazywana prototype. Jeśli wywołujący wywołuje funkcję i podaje jej jakiś argument, to jest to błąd a kompilator wypluwa odpowiedni błąd.

Drugi sposób deklarowania funkcji ma wiele zalet. Jednym z nich jest oczywiście sprawdzanie ilości i typów parametrów. Inną różnicą jest to, że ponieważ kompilator zna typy parametrów, może zastosować domyślne konwersje argumentów do typu parametrów. Jeśli nie ma listy typów parametrów, nie można tego zrobić, a argumenty są konwertowane na typy promowane (nazywane domyślnym argumentem promocja). char stanie się int, na przykład, podczas gdy float stanie się double.

Typ złożony dla funkcji

Nawiasem mówiąc, jeśli plik zawiera zarówno pominiętą listę identyfikatorów, jak i listę typu parametru, lista typu parametru "wygrywa". Typ funkcji na końcu zawiera prototyp:

void f();
void f(int a) {
    printf("%d", a);
}

// f has now a prototype. 

Dzieje się tak dlatego, że obie deklaracje nie mówią nic sprzecznego. Drugi miał jednak coś do powiedzenia. Czyli jednym z argumentów jest akceptuję. To samo można zrobić w odwrotnej kolejności

void f(a) 
  int a;
{ 
    printf("%d", a);
}

void f(int);

Pierwsza definiuje funkcję za pomocą listy identyfikatorów, podczas gdy druga dostarcza jej prototyp, używając deklaracji zawierającej listę typów parametrów.

 91
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-20 13:55:45

void foo(void) jest lepszy, ponieważ wyraźnie mówi: żadne parametry NIE są dozwolone.

void foo() oznacza to, że możesz (pod niektórymi kompilatorami) wysyłać parametry, przynajmniej jeśli jest to deklaracja twojej funkcji, a nie jej definicja.

 18
Author: Uri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-09 10:22:33

C99 cytaty

Ta odpowiedź ma na celu zacytowanie i Wyjaśnienie odpowiednich części C99 n1256 standard draft .

Definicja zgłaszającego

Termin deklarator pojawi się wiele, więc zrozummy to.

Z gramatyki języka wynika, że następujące znaki podkreślenia są deklaratoriami:

int f(int x, int y);
    ^^^^^^^^^^^^^^^

int f(int x, int y) { return x + y; }
    ^^^^^^^^^^^^^^^

int f();
    ^^^

int f(x, y) int x; int y; { return x + y; }
    ^^^^^^^

Deklaracje są częścią zarówno deklaracji funkcji, jak i definicji.

Tam są 2 rodzaje zgłaszających:

  • lista typów parametrów
  • lista identyfikatorów

Lista typów parametrów

Deklaracje wyglądają następująco:

int f(int x, int y);

Definicje wyglądają następująco:

int f(int x, int y) { return x + y; }

Nazywa się to listą typów parametrów, ponieważ musimy podać typ każdego parametru.

Lista identyfikatorów

Definicje wyglądają następująco:

int f(x, y)
    int x;
    int y;
{ return x + y; }

Deklaracje wyglądają następująco:

int g();

Nie możemy zadeklarować funkcji z niepustą listą identyfikatorów:

int g(x, y);

Ponieważ 6.7.5.3 "deklaratory funkcji (w tym prototypy)" mówi:

3 lista identyfikatorów w deklaracji funkcji, która nie jest częścią definicji tej funkcji, jest pusta.

Nazywa się listą identyfikatorów, ponieważ podajemy tylko identyfikatory x i y na f(x, y), typy przychodzą po.

Jest to starsza metoda i nie powinna być już używana. 6.11.6 funkcja says:

1 Użycie deklaratorów funkcji z pustymi nawiasami (nie deklaratorów typu parametru prototype-format) jest funkcją przestarzałą.

I wprowadzenie wyjaśnia czym jest przestarzała funkcja:

Niektóre funkcje są przestarzałe, co oznacza, że można je uznać za wycofanie w przyszłych rewizjach tego standardu międzynarodowego. Są one zatrzymane, ponieważ z ich rozpowszechnionych wykorzystania, ale ich wykorzystanie w nowych implementacjach (do implementacji [1]) lub nowych programów (dla języka [6.11] lub funkcji biblioteki [7.26]) jest zniechęcany

F () vs f (void) dla deklaracji

Kiedy piszesz tylko:

void f();

Jest to koniecznie deklaracja listy identyfikatorów, ponieważ 6.7.5 "Deklarators" says definiuje gramatykę jako:

direct-declarator:
    [...]
    direct-declarator ( parameter-type-list )
    direct-declarator ( identifier-list_opt )

Więc tylko Wersja listy identyfikatorów może być pusta, ponieważ jest opcjonalna (_opt).

direct-declarator jest jedynym węzłem gramatycznym, który definiuje nawias (...) części deklaratora.

Jak więc disambiguate i używać lepszej listy typów parametrów bez parametrów? 6.7.5.3 deklaratory funkcji (w tym prototypy) mówi:

10 specjalny przypadek nienazwanego parametru typu void jako jedynej pozycji na liście określa, że funkcja nie ma parametrów.

Więc:

void f(void);

Jest sposób.

Jest to składnia magiczna jawnie dozwolona, ponieważ nie możemy użyć argumentu typu void w żaden inny sposób:

void f(void v);
void f(int i, void);
void f(void, int);

Co się stanie, jeśli użyję deklaracji f ()?

Może Kod się dobrze skompiluje: 6.7.5.3 deklaratory funkcji (w tym prototypy):

14 pusta lista w deklaracji funkcji, która nie jest częścią definicja tej funkcji określa, że brak informacji o liczbie lub rodzajach parametry są dostarczane.

Więc można uciec z:

void f();
void f(int x) {}
Jeśli masz szczęście, kompilator ci to powie, będziesz miał problem z wyjaśnieniem dlaczego:]}
void f();
void f(float x) {}

Zobacz: dlaczego pusta deklaracja działa dla definicji z argumentami int, ale nie dla argumentów float?

F () I F (void) dla definicji

f() {}

Vs

f(void) {}

Są podobne, ale nie identyczne.

6.7.5.3 deklaratory funkcji (w tym prototypy) says:

14 pusta lista w deklaracji funkcji, która jest częścią definicji tej funkcji, określa, że funkcja nie ma parametrów.

Który wygląda podobnie do opisu f(void).

Ale nadal... wydaje się, że:

int f() { return 0; }
int main(void) { f(1); }

Jest zgodne z niezdefiniowanym zachowaniem, podczas gdy:

int f(void) { return 0; }
int main(void) { f(1); }

Jest niezgodny z opisem na: dlaczego gcc zezwala na przekazywanie argumentów do funkcji zdefiniowanej jako bez argumentów?

Aby dokładnie zrozumieć dlaczego. Ma to związek z byciem prototypem czy nie. Zdefiniuj prototyp.

 8
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:02

Oprócz różnic składniowych, Wiele osób preferuje również stosowanie void function(void) ze względów praktycznych:

Jeśli używasz funkcji wyszukiwania i chcesz znaleźć implementację funkcji, możesz wyszukać function(void), A zwróci ona zarówno prototyp, jak i implementację.

Jeśli pominąłeś drugą void, musisz poszukać function() i dlatego też znajdziesz wszystkie wywołania funkcji, co utrudnia znalezienie rzeczywistej implementacji.

 3
Author: maja,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-29 11:20:13

W C++ nie ma różnicy w main() i main(void).

Ale W C, main() będzie wywoływana z dowolną liczbą parametrów.

Przykład:

main ( ){
    main(10,"abc",12.28);
    //Works fine !
    //It won't give the error. The code will compile successfully.
    //(May cause Segmentation fault when run)
}

main(void) zostanie wywołana BEZ żadnych parametrów. Jeśli spróbujemy przejść, to kończy się to błędem kompilatora.

Przykład:

main (void) {
     main(10,"abc",12.13);
     //This throws "error: too many arguments to function ‘main’ "
}
 1
Author: Sandeep_black,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-31 12:48:48