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?
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 int
1 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.
Można przejść
char *
do funkcja, która oczekujevoid *
i odwrotnie.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.
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.
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)
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
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