Czy po deklaracji funkcji w C ++ nie jest potrzebny średnik („;”)?

174

Niedawno przystąpiłem do testu programowania dla średnio zaawansowanych i jedno z pytań, które pomyliłem, brzmiało następująco:

Po deklaracji funkcji nie jest potrzebny średnik („;”).

Prawda czy fałsz.

Wybrałem „fałsz” (i popraw mnie, jeśli się mylę, ponieważ czuję, że oszalałem), deklaracja funkcji jest tym, co piszesz przed definicją (na górze kodu), aby kompilator znał funkcję call jeszcze przed jej wywołaniem, a definicja funkcji jest tym, co składa się na funkcję jako całość.

To znaczy,

Deklaracja:

int func();

Definicja:

int func() {
  return 1;
}

Czy odpowiedź na to pytanie nie powinna być fałszywa?

Logan
źródło
41
Definicja jest również deklaracją. Ale powiedziałbym, że twoja odpowiedź była poprawna.
216
To trudne pytanie i nie ma wpływu na czyjąkolwiek zdolność dobrego programowania.
fonetagger
40
Zawsze uważam, że pytania, które prowadzą do podwójnych negatywów, są mylące. Moim zdaniem takie pytania mają na celu podniecić uczniów. Dlaczego pytanie nie mogło zostać sformułowane w następujący sposób: „Po deklaracji funkcji zawsze potrzebny jest średnik (';'). Prawda czy fałsz.”? : /
Algirdas Preidžius
18
@phonetagger Całe to zamieszanie pokazuje, jak źle sformułowane jest pytanie.
François Andrieux,
34
Hanlon's Razor sugeruje, że autor testu pomieszał „deklarację” i „definicję”.
Sneftel,

Odpowiedzi:

161

Możesz mieć sytuację, w której deklarujesz i definiujesz funkcję w jednym kroku, tj. Jeśli umieścisz definicję funkcji w miejscu, w którym ją deklarujesz. Więc technicznie myślę, że prawda jest poprawna. Ale pytanie jest sformułowane w taki sposób, że odpowiedziałbym na nie tak, jak ty.

jwismar
źródło
10
Twierdzę, że prawda nie jest poprawna z powodu podanego przez Ciebie powodu. Jeśli są przypadki, w których potrzebny jest średnik, oznacza to, że jest fałszywy (lub nie jest prawdziwy). Prawda jest dla mnie absolutem, jeśli są jasne przypadki, kiedy jest to potrzebne, nie można powiedzieć prawdy.
I Funball
16
@IFunball Dobry argument. Głupie języki ojczyste. Zdanie „Średnik (';') nie jest potrzebny po deklaracji funkcji” można odczytać jako „Średnik (';') nie jest (nigdy) potrzebny po deklaracji funkcji” lub jako „Średnik (';') ) nie jest (zawsze) potrzebne po deklaracji funkcji ". Kwalifikacja stwierdzenia jako prawdziwego czy fałszywego zależy od wyboru interpretacji. Ściśle wypowiedziane pytanie jest niejasne i dlatego nie ma jasnej odpowiedzi.
Peter - Przywróć Monikę
6
@IFunball To dlatego, że „deklaracja”, bez dalszego kontekstu i bez stwierdzenia, że ​​jesteśmy prawnikami językowymi, jest powszechnie rozumiana jako „deklaracja nieokreślająca”. Pytanie było niesprawiedliwe.
Wyścigi lekkości na orbicie
2
Każde pytanie egzaminacyjne, które jest niejasne dla kogoś, kto zna testowaną treść, jest błędne.
Nat,
2
Wygląda na to, że musimy dodać niezdefiniowaną klauzulę dotyczącą zachowania do języka angielskiego
Nick Mertin
147

Oprócz tego, że „definicja jest również deklaracją”, poniżej przedstawiono poprawne C ++:

int f(), g();

To deklaruje dwie funkcje fi gobie bez argumentów i ze zwracanym typem int, ale po definicji fnie występuje (natychmiast) średnik. Podobnie jest to legalne:

int f(), i = 42;

Ale w rzeczywistości nie jest dozwolone całkowite pominięcie średnika w tych przypadkach, więc byłoby nieco zaskakujące, gdyby jeden z nich został wzięty jako przykład deklaracji bez następującego średnika. W rzeczywistości następujące działania są nielegalne:

void *p, f() {}

Inaczej niż (zwykła) deklaracja funkcji, definicji funkcji nie można łączyć z żadną inną deklaracją lub definicją do tego samego specyfikatora typu . (Gdyby to było zgodne z prawem, zdefiniowałoby zarówno a, jak void *pi a void f() {}.)

W każdym razie wydaje się, że jest to pytanie typu „gotcha”, które nie powinno być przedmiotem pośredniego testu programowania.

(A tak przy okazji, proszę nie pisać takiego kodu int f(), i = 42;).

Arne Vogel
źródło
2
Możliwe jest również użycie typedef do zdefiniowania typu funkcji, a następnie wykorzystanie tego do zadeklarowania wielu funkcji naraz, np. typedef int fooProc(int); fooProc a,b.c.d.e;Nie jestem pewien, dlaczego standardowe nagłówki dla kompilatorów opartych na dyskietkach nie robiły tego w dnia, ponieważ wydaje mi się, że pozwoliłoby to plikom nagłówkowym na znacznie mniejsze, a tym samym szybsze przetwarzanie.
supercat
Rozważ również int f(int(&g)(),int(*h)()){return g()+h();}To ma trzy deklaracje funkcji, z których po jednej następuje otwarty nawias klamrowy, po drugiej przecinek, a po trzeciej zamknięty nawias.
David Hammen
1
@DavidHammen: To nie deklaruje ściśle funkcji innych niż int f(stuff). Nawet w zakresie funkcji gjest automatyczną zmienną typu odniesienia do funkcji i hjest wskaźnikiem do funkcji .
Peter Cordes,
83

Inne odpowiedzi i komentarze wskazują na kilka z wielu sposobów, na jakie jest to okropne, mylące i źle napisane pytanie. Ale jest inny problem, którego nikt inny jeszcze nie zidentyfikował. Pytanie brzmi:

Po deklaracji funkcji nie jest potrzebny średnik („;”). Prawda czy fałsz.

OK, spójrzmy na deklarację funkcji:

int func();       /* */
/*           ^       */
/*           |       */
/* That whitespace is "after the function declaration". */

To wszystko jest deklaracją . Deklaracja nie jest int func(), a potem następuje; . Deklaracja jest int func();zakończona białymi znakami.

Pytanie brzmi więc: czy po deklaracji potrzebny jest średnik ? Oczywiście nie. Deklaracja zawiera już średnik, który ją kończy. Średnik po deklaracji byłby bezcelowy. Z int func(); ;kolei po deklaracji funkcji byłby średnik .

Pytanie prawie na pewno miało na celu zadanie pytania „prawda czy fałsz: ostatnim tokenem w deklaracji funkcji jest zawsze średnik”. Ale to nie jest pytanie, które napisali, ponieważ autor quizu nie myślał jasno o problemie.

Radzę całkowicie unikać quizów z języka programowania. Są okropne.


Zabawny fakt, skoro już jesteśmy na ten temat. W C # to wszystko jest legalne:

class C {}
class D {};
struct E {}
struct F {};

W języku C # deklaracja klasy lub struktury może kończyć się średnikiem lub nie, według własnego uznania. Ta dziwna mała funkcja została dodana z korzyścią dla programistów C / C ++ przybywających do C #, którzy mają na wyciągnięcie ręki, że deklaracje typu kończą się bezcelowym średnikiem; zespół projektantów nie chciał ich karać za ten nawyk. :-)

Eric Lippert
źródło
Komentarze nie służą do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu .
Samuel Liew
25

Możesz również zadeklarować taką funkcję:

int func(){
    return 1;
}

Stwierdzenie jest bardzo niejednoznaczne. Prawidłowa odpowiedź powinna brzmieć: to zależy od tego, jak zadeklarujesz funkcję.

W każdym razie wybrałbym też fałsz i może możesz zgłosić komuś pytanie.

Luca Corsini
źródło
3
W każdym razie nie stawiaj tego na osobistym poziomie. Ważne jest, abyś zrozumiał, jak działa definicja deklaracji funkcji, więc nie przejmuj się tym zbytnio, po prostu upewnij się, że pytanie zostanie przynajmniej sprawdzone i kontynuuj
Luca Corsini
11
Absolutnie. Szczerze mówiąc, dowiedziałem się więcej o definicji deklaracji funkcji od błędnego sformułowania pytania, niż bym zrobił to poprawnie.
Logan
1
@Logan nie martw się zbytnio. Jeśli wiesz, jak pisać i czytać funkcję, to wszystko, czego potrzebujesz. Osobiście nienawidzę tego rodzaju pytań, które 1. nie są dobrze zdefiniowane 2. sprawdzają twoją teoretyczną wiedzę na temat składni. Dla mnie to jak pamięć mięśni. Kiedy piszę, każda cyfra idzie bez wysiłku do klawisza, który ma iść, ale jeśli dasz mi test na to, jakie klawisze należy nacisnąć, byłbym całkowicie beznadziejny bez klawiatury, aby fizycznie wykonać akcję ...
bolov
2
... Pisanie wspólnej składni (np. Takiej jak funkcja) stanie się dla Ciebie drugą naturą. A kiedy zepsujesz to, ponieważ właśnie zmieniłeś języki, cóż ... inteligencja i podświetlanie składni zapewniają szybkie i wydajne rozwiązania. Zainwestuj swój czas i energię w coś bardziej użytecznego.
bolov
20

Po deklaracji funkcji nie jest potrzebny średnik („;”).

Prawda czy fałsz.

Prawda . Po żadnej deklaracji nie jest potrzebny średnik. Ani po żadnej definicji. Ani po żadnym oświadczeniu.

Wiele rodzajów deklaracji musi kończyć się średnikiem, jak określa składnia w sekcji 7 [dcl.dcl]. Ale po tym nigdy nie ma potrzeby pisać drugiego.

Marc van Leeuwen
źródło
1
Widzę, że Eric Lippert już się z tym spierał. Myślę, że wszystkie pozytywne głosy sprawiły, że to przeoczyłem. Zapraszam do oddania tam swoich głosów.
Marc van Leeuwen
Prawie każde pytanie, które brzmi: „X jest zawsze prawdziwe: prawda czy fałsz?” otrzyma odpowiedź „fałsz”. Do licha, nigdzie nie ma potrzeby stosowania średnika ; kompilator może narzekać i odmówić kompilacji twojego programu, ale to jeszcze nie koniec świata; Nie nazwałbym tego podstawową potrzebą . ;)
Quuxplusone
@Quuxplusone jeśli kompilator odrzuci swój program, program nie posiada żadnych deklaracji funkcji w nim :)
Ben MILLWOOD
6

Zależy to od tego, czy deklarujemy, czy definiujemy funkcję. Jeśli deklarujemy funkcję, musimy dołączyć średnik ( ;), a jeśli definiujemy funkcję, średnik nie jest potrzebny.

Deklaracja wygląda tak:

int add(int, int);

A definicja jest taka:

int add(int a, int b)
{
    // ...
}
Rocx En Ruff
źródło
10
Problem z tą odpowiedzią polega na tym, że sugeruje ona, iż definicje i deklaracje wzajemnie się wykluczają. W rzeczywistości każda definicja jest deklaracją; definicje są podzbiorem deklaracji.
MSalters
6

Chociaż zgadzam się z prawie wszystkimi pozostałymi odpowiedziami, stwierdzając, że pytanie jest sformułowane bardzo niejednoznacznie, a twoja odpowiedź jest poprawna technicznie, pozwól mi przyjąć inną perspektywę:

Tak je zawsze nazywałam:

void func();  // The function prototype

...

void func()
{
    // The function definition
}

Zakładam, że pytanie zostało sformułowane z myślą o tej terminologii.

Definicja i deklaracja to dla mnie ta sama koncepcja. "Definiuję x = y" == "Deklaruję x = y".

Ale oczywiście istnieje duża różnica między prototypem funkcji (na górze) a rzeczywistą definicją funkcji.

Opifex
źródło
Dla mnie twój prototyp jest deklaracją opartą na tym, jak się dowiedziałem (nie mówiąc jednak, że się mylisz), ale wtedy spodziewałbym się również, że prototyp określi liczbę i typ argumentów lub nieważne, ale spodziewam się, że to pominąłeś dla zwięzłości.
David S,
David S: Tak, oczywiście zawierałby również liczbę i typ argumentów, ale rzeczywiście pominąłem je dla zwięzłości (zauważ, że w rzeczywistej deklaracji funkcji nie ma również argumentów). Jednak tak naprawdę nie zgadzam się, kiedy mówisz, że deklaracja pełnej funkcji nazywa się prototypem. Cytuję Wikipedię: „prototyp funkcji lub interfejs funkcji to deklaracja funkcji, która określa nazwę funkcji i podpis typu (arity, typy danych parametrów i typ zwracany), ale pomija treść funkcji”.
Opifex
@DavidS: W C ++ deklaracje funkcji są zawsze prototypami (lub definicjami) i void func();są dokładnie równoważne z void func(void);. To bardzo różni się od C , gdzie void func();nie mówi kompilatorowi nic o argumentach i nie jest tym samym, co void func(void);. Późniejszy prototyp lub definicja są dobrym pomysłem, w przeciwnym razie wywołujący musi zastosować domyślne promocje arg (np. Float -> double i wąskie typy całkowite do int. Te same zasady, co dla argumentów do funkcji wariadycznych).
Peter Cordes
Moje przeprosiny, skończyłem tutaj, patrząc na coś związanego z C i nie zauważyłem zmiany języka. Nie będę usuwał swojego komentarza w celu zachowania przejrzystości, ale uważam go za wycofany.
David S
6

Szkoda, że ​​pytanie, które zadałeś, nie mówi „bezpośrednio po”. Moglibyśmy na przykład napisać to:

int func()  /* My function */ ;

Albo mógłbym napisać:

int func()
int a = 42;

W pierwszym przypadku średnik nie znajduje się bezpośrednio po deklaracji, ale byłoby OK.

W drugim przypadku średnik znajduje się „po” zgłoszeniu, ale nie bezpośrednio po nim.

Myślę, że Eric Lippert ma słuszny pomysł w swojej odpowiedzi .

To tak, jakby powiedzieć „czy po angielsku powinna być kropka?”. Prawdopodobnie zdanie ma już na końcu kropkę (w przeciwnym razie nie byłoby to zdanie) i dlatego po zdaniu nie powinno być kropki.

Nick Gammon
źródło
4
Miły. Kończąc zdanie dodatkowym okresem. Widzę, co tu zrobiłeś.
David S,
2
int func() int a=42;nie kompiluje się. Potrzebujesz przecinka, a nie innego int. Zobacz odpowiedź @ Arne opublikowaną dzień wcześniej. Jedyną nowością w tej odpowiedzi jest ostatni akapit, z analogią do zdań angielskich.
Peter Cordes
1
Nie powiedziałem, że drugi przykład został skompilowany. Zwracałem uwagę, że stwierdzenie, że średnik jest potrzebny „po” deklaracji, jest niejednoznaczne. Mój przykład miał średnik po deklaracji, ale się nie kompiluje.
Nick Gammon
1
Ten sam problem występuje w komunikatach o błędach; ulubionym przykładem z C # jest „ Parametr params musi być ostatnim parametrem na formalnej liście parametrów ”. Teraz przypuśćmy, że zamiast tego powiedziałem: „Frob musi być ostatnią glupą na liście gloobów”. Czy to oznacza, że ​​(1) Każda lista gloobów ma dokładnie jedną frob na końcu, tak jak każde pytanie ma dokładnie jeden znak zapytania na końcu, (2) Lista gloobów może mieć dowolną liczbę frobów, ale jeśli ma jedną lub więcej frobów , ostatnia pozycja musi być frobem, tak jak liczba parzysta może mieć dowolną liczbę 02468, ale jedną z nich musi być ostatnia lub ...
Eric Lippert
... (3) lista gloobów może mieć zero lub jeden frob, a jeśli ma, to kończy się? Jeśli nie znasz kontekstu, myślę, że (1) jest najbardziej sensownym wyjaśnieniem, ale w przypadku „parametru params” (3) jest właściwym wyjaśnieniem. Wiele nieformalnych opisów elementów języka programowania ma właściwość, którą moi znajomi redaktora technicznego nazywają „COIK” - wyczyść tylko wtedy, gdy jest znana. Jeśli nie rozumiesz jeszcze dokładnie materiału, jego opis jest dla Ciebie bezużyteczny, ale jeśli już dobrze go rozumiesz, nie potrzebujesz opisu!
Eric Lippert
4

Możesz używać ;tylko do prototypów.

M7off
źródło
4

To trochę trudne pytanie, ale użyli deklaracji słowa, które oznacza coś takiego:

int example();

Więc to prawda w tym przypadku.

Gdyby użyli słowa „ implementacja”, to byłoby fałszywe.

dark_3nergy
źródło
2

Średnik (;) służy do informowania kompilatora, że ​​po tym średniku (;) rozpoczyna się nowa instrukcja.

Myślę więc, że średnik (;) jest wymagany tylko podczas deklaracji funkcji. Więc według mnie odpowiedź będzie prawdziwa.

Jatinder
źródło
Jednak deklaracje nie są oświadczeniami.
HolyBlackCat
ale po deklaracji funkcji wykonujemy nową linię kodu za pomocą kompilatora. więc myślę, że przed wykonaniem nowej linii kodu kompilator musi wiedzieć, gdzie kończy się poprzednia linia kodu, dopiero wtedy kompilator może wygenerować kod natywny (tj. 0101).
Jatinder,
2

Gdy funkcje są zdefiniowane przed main () :

  • Nie potrzebuję średnika, ponieważ funkcja jest już zdefiniowana

Gdy funkcje są zdefiniowane po main () :

  • Potrzebujesz średnika, ponieważ tworzysz prototyp tej funkcji i mówisz kompilatorowi, że funkcja kończy działanie.
shiv shah
źródło