Zrozumienie różnicy między F () I F (void) w C i C++ raz na zawsze

Ok, więc słyszałem różne opinie na ten temat i chcę się upewnić, że dobrze rozumiem.

Dla C++

Deklaracje void f(); i void f(void); oznaczają dokładnie to samo, funkcja f nie przyjmuje żadnych parametrów. Tak samo dla definicji.

Dla C

Deklaracja void f(void); oznacza, że f nie przyjmuje żadnych parametrów.

Deklaracja void f(); oznacza, że funkcja f może lub nie może mieć parametrów, a jeśli tak, to nie wiedzieć, jakie to parametry, lub ile ich jest. Zauważ, że nie jest to to samo co elipsa, nie możemy użyć va_list.

Tutaj zaczyna być ciekawie.

Przypadek 1

Deklaracja:

void f();

Definicja:

void f(int a, int b, float c)
{
   //...
}

Przypadek 2

Deklaracja:

void f();

Definicja:

void f()
{
   //...
}

Pytanie:

Co dzieje się w czasie kompilacji w przypadkach 1 i 2, gdy wywołujemy f z poprawnymi argumentami, źle argumenty i żadnych argumentów? Co się dzieje w czasie biegu?

Dodatkowe pytanie:

Jeśli zadeklaruję f z argumentami, ale zdefiniuję je bez nich, czy to coś zmieni? Czy powinienem być w stanie zaadresować argumenty z ciała funkcji?

Author: Baum mit Augen, 2012-11-10

4 answers

Więcej terminologii (C, nie C++): prototyp funkcji deklaruje typy jej argumentów. W przeciwnym razie funkcja nie ma prototypu.

void f();                      // Declaration, but not a prototype
void f(void);                  // Declaration and prototype
void f(int a, int b, float c); // Declaration and prototype

Deklaracje, które nie są prototypami, są przechowywanymi z pre-ANSI C, z czasów K & R C. jedynym powodem używania deklaracji w starym stylu jest zachowanie binarnej kompatybilności ze starym kodem. Na przykład w Gtk 2 istnieje deklaracja funkcji bez prototypu - jest tam przez przypadek, ale nie można jej usunąć bez rozbijania binariów. Standard C99 komentarze:

6.11.6 deklaratory funkcji

Użycie deklaratorów funkcji z pustymi nawiasami (nie parametr prototype-format type declarators) jest funkcją przestarzałą.

Rekomendacja: proponuję zestawić cały kod C w GCC / Clang z -Wstrict-prototypes i -Wmissing-prototypes, oprócz zwykłego -Wall -Wextra.

Co się dzieje

void f(); // declaration
void f(int a, int b, float c) { } // ERROR

Deklaracja nie zgadza się z ciałem funkcji! Jest to błąd w czasie kompilacji, a to dlatego, że nie można mieć argumentu float w funkcji bez prototypu. Powodem, dla którego nie możesz użyć float w niechronionej funkcji jest to, że kiedy wywołujesz taką funkcję, wszystkie argumenty są promowane za pomocą pewnych domyślnych promocji. Oto stały przykład:

void f();

void g()
{
    char a;
    int b;
    float c;
    f(a, b, c);
}

W tym programie a jest promowany do int1 i c jest promowany do double. Więc definicja f() musi be:

void f(int a, int b, double c)
{
    ...
}

ZOB. C99 6.7.6 pkt 15,

Jeśli jeden typ ma listę typów parametrów, a drugi typ jest określony przez deklarator funkcji, który nie jest częścią definicji funkcji i który zawiera pusty lista identyfikatorów, Lista parametrów nie może mieć terminatora elipsy i typu każdego parametr musi być zgodny z typem wynikającym z zastosowania domyślne promocje argumentów.

Odpowiedź 1

Co dzieje się w czasie kompilacji w przypadkach 1 i 2, gdy wywołujemy f z poprawnymi argumentami, błędnymi argumentami i w ogóle bez argumentów? Co się dzieje w czasie biegu?

Kiedy wywołujesz f(), parametry są promowane przy użyciu domyślnych promocji. Jeśli promowane typy pasują do rzeczywistych typów parametrów dla f(), to wszystko jest dobre. Jeśli nie pasują, to prawdopodobnie skompiluje się, ale na pewno uzyskasz niezdefiniowane zachowanie.

"Undefined behavior" is spec-speak for "Nie gwarantujemy, co się stanie."Może twój program się zawiesi, może będzie działał dobrze, może zaprosi teściów na kolację.

Istnieją dwa sposoby uzyskania diagnostyki w czasie kompilacji. Jeśli masz zaawansowany kompilator z możliwościami analizy statycznej między modułami, prawdopodobnie otrzymasz komunikat o błędzie. Możesz również uzyskać Komunikaty dla nie-prototypowych deklaracji funkcji z GCC, używając -Wstrict-prototypes -- które polecam włączyć we wszystkich Twoje projekty(z wyjątkiem plików, które używają Gtk 2).

Odpowiedź 2

Jeśli zadeklaruję f z argumentami, ale zdefiniuję je bez nich, czy to coś zmieni? Czy powinienem być w stanie zaadresować argumenty z ciała funkcji?

Nie powinno się kompilować.

Wyjątki

W rzeczywistości istnieją dwa przypadki, w których argumenty funkcji mogą nie zgadzać się z definicją funkcji.

  1. Można przejść char * do funkcja, która oczekuje void * i odwrotnie.

  2. Dobrze jest przekazać podpisaną liczbę całkowitą funkcji, która oczekuje niepodpisanej wersji tego typu, lub odwrotnie, o ile wartość jest reprezentowalna w obu typach (tzn. nie jest ujemna i nie jest poza zakresem podpisanego typu).

Przypisy

1: jest możliwe , że char Promuje się do unsigned int, ale jest to bardzo rzadkie.

 51
Author: Dietrich Epp,
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-06-11 19:44:12

Cała sprawa jest kwestią sporną, jeśli używasz C99 lub nowszego (i, jeśli nie utknąłeś na starym systemie wbudowanym lub czymś takim, prawdopodobnie powinieneś używać czegoś bardziej nowoczesnego).

Sekcja C99/C11 6.11.6 Future language directions, Function declarators stwierdza:

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

Dlatego powinieneś unikać używania takich rzeczy jak void f(); w ogóle.

Jeśli pobiera parametry, wymienia je, tworząc odpowiedni prototyp. Jeśli nie, my void, aby ostatecznie wskazać, że nie przyjmuje żadnych parametrów.

 6
Author: paxdiablo,
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-02-29 05:59:51

W C++, f () I F (void) to to samo

W C, są różne i można przekazać dowolną liczbę argumentów podczas wywołania funkcji f (), ale żaden argument nie może być przekazany w F (void)

 3
Author: rs2012,
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
2012-11-10 10:06:38

W czystym C powoduje to błąd: error C2084: function 'void __cdecl f(void )' already has a body

void f(void);
void f( );

int main() {
  f(10);
  f(10.10);
  f("ten");

  return 0;
}

void f(void) {

}

void f( ) {

}

.

 fvoid.c line(19) : error C2084: function 'void __cdecl f(void )' already has a body

Ale w czystym C++, skompiluje się bez błędów.

Funkcje przeciążające (tylko C++, C nie ma przeciążenia)

Przeciążasz nazwę funkcji f deklarując więcej niż jedną funkcję o nazwie f w tym samym zakresie. Deklaracje f muszą różnić się od siebie typami i / lub liczbą argumentów na liście argumentów. Gdy wywołujesz przeciążoną funkcję o nazwie f, poprawna funkcja jest wybierana przez porównanie listy argumentów wywołania funkcji z listą parametrów każdej z przeciążonych funkcji kandydujących o nazwie f.

Przykład:

#include <iostream>
using namespace std;

void f(int i);
void f(double  f);
void f(char* c);


int main() {
  f(10);
  f(10.10);
  f("ten");

  return 0;
}

void f(int i) {
  cout << " Here is int " << i << endl;
}
void f(double  f) {
  cout << " Here is float " << f << endl;
}

void f(char* c) {
  cout << " Here is char* " << c << endl;
}

Wyjście:

 Here is int 10
 Here is float 10.1
 Here is char* ten
 -1
Author: Software_Designer,
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
2012-11-10 11:50:47