Właśnie natknąłem się na czyjś kod C, którego nie rozumiem, dlaczego się kompiluje. Są dwa punkty, których nie rozumiem.
Po pierwsze, prototyp funkcji nie ma parametrów w porównaniu z rzeczywistą definicją funkcji. Po drugie, parametr w definicji funkcji nie ma typu.
#include <stdio.h>
int func();
int func(param)
{
return param;
}
int main()
{
int bla = func(10);
printf("%d", bla);
}
Dlaczego to działa? Przetestowałem to w kilku kompilatorach i działa dobrze.
c
parameters
function-prototypes
function-parameter
AdmiralJonB
źródło
źródło
-Wstrict-prototypes
za pomocą zarówno :, jakint func()
iint main()
: xc: 3: ostrzeżenie: deklaracja funkcji nie jest prototypem. Należy zadeklarowaćmain()
jakmain(void)
również.int func();
jest kompatybilnyint func(arglist) { ... }
.int main(void)
.Odpowiedzi:
Wszystkie pozostałe odpowiedzi są poprawne, ale tylko w celu uzupełnienia
I znowu ze względu na kompletność. Ze specyfikacji C11 6: 11: 6 (strona: 179)
źródło
W C
func()
oznacza, że możesz przekazać dowolną liczbę argumentów. Jeśli nie chcesz żadnych argumentów, musisz zadeklarować jakofunc(void)
. Typ przekazywany do funkcji, jeśli nie zostanie określony, przyjmuje wartość domyślnąint
.źródło
func(42,0x42);
( wszystkie połączenia muszą używać formularza dwóch argumentów).unsigned
to po prostu inna nazwa dla tego samego typu counsigned int
.int func();
to przestarzała deklaracja funkcji z dni, w których nie było standardu C, tj. dni K&R C (przed 1989 rokiem, w którym opublikowano pierwszy standard „ANSI C”).Pamiętaj, że w K&R C nie było prototypów, a słowo kluczowe
void
nie zostało jeszcze wynalezione. Wystarczyło powiedzieć kompilatorowi o typie zwracanej funkcji. Pusta lista parametrów w K&R C oznacza „nieokreśloną, ale stałą” liczbę argumentów. Naprawiono oznacza, że musisz wywoływać funkcję z tą samą liczbą argumentów za każdym razem (w przeciwieństwie do funkcji variadic, np.printf
Gdzie liczba i typ mogą się różnić dla każdego połączenia).Wiele kompilatorów zdiagnozuje ten konstrukt; w szczególności
gcc -Wstrict-prototypes
powie Ci, że „deklaracja funkcji nie jest prototypem”, która jest na miejscu, ponieważ wygląda jak prototyp (szczególnie jeśli jesteś zatruty przez C ++!), ale tak nie jest. Jest to deklaracja typu powrotu K&R C. w starym stylu.Ogólna zasada: nigdy nie pozostawiaj pustej deklaracji listy parametrów pustej, użyj,
int func(void)
aby być konkretnym. Dzięki temu deklaracja typu zwrotu K&R staje się właściwym prototypem C89. Kompilatory są zadowolone, programiści są zadowoleni, statyczne kontrolery są zadowolone. Ci, którzy wprowadzają w błąd przez ^ W ^ Wfond z C ++, mogą się jednak denerwować, ponieważ muszą pisać dodatkowe znaki, gdy próbują ćwiczyć swoje umiejętności językowe :-)źródło
int
.Uważam jednak, że każda kompilacja, która ją przejdzie, nie ma skonfigurowanego poziomu ostrzeżeń / błędów, nie ma sensu dopuszczać faktycznego kodu.
źródło
To deklaracja i definicja funkcji stylu K&R . Z normy C99 (ISO / IEC 9899: TC3)
Sekcja 6.7.5.3 Deklaratory funkcji (w tym prototypy)
Sekcja 6.11.6 Deklaratory funkcji
Sekcja 6.11.7 Definicje funkcji
Której stary styl oznacza K & R styl
Przykład:
Deklaracja:
int old_style();
Definicja:
źródło
C zakłada,
int
że nie podano żadnego typu dla typu zwracanej funkcji i listy parametrów . Tylko w przypadku tej zasady możliwe są następujące dziwne rzeczy.Definicja funkcji wygląda następująco.
Jeśli piszesz prototyp
W prototypie można określić tylko typ parametrów. Nazwa parametrów nie jest obowiązkowa. Więc
Również jeśli nie określisz typu parametru, ale nazwa
int
zostanie przyjęta jako typ.Jeśli pójdziesz dalej, następujące działania również działają.
Kompilator zakłada
int func()
, że piszeszfunc()
. Ale nie umieszczajfunc()
w ciele funkcji. To będzie wywołanie funkcjiźródło
int func()
nie jest niejawną formąint func(int)
.Jak stwierdzono @Krishnabhadra, wszystkie poprzednie odpowiedzi od innych użytkowników mają poprawną interpretację, a ja po prostu chcę dokonać bardziej szczegółowej analizy niektórych punktów.
W Old-C, podobnie jak w ANSI-C, „ beztypowy parametr formalny ”, weź wymiar rejestru pracy lub możliwości głębokości instrukcji (rejestry cienia lub cykl skumulowany instrukcji), w 8-bitowym MPU, będzie int16, w 16-bitowym MPU i tak będzie int16 i tak dalej, w przypadku architektury 64-bitowej może wybrać kompilację opcji takich jak: -m32.
Chociaż implementacja na wysokim poziomie wydaje się prostsza, przy przekazywaniu wielu parametrów praca programisty w kroku typu danych dimmencji sterującej staje się bardziej wymagająca.
W innych przypadkach, w przypadku niektórych architektur mikroprocesorów, spersonalizowane kompilatory ANSI wykorzystały niektóre z tych starych funkcji w celu zoptymalizowania wykorzystania kodu, zmuszając lokalizację tych „nietypowych parametrów formalnych” do pracy w rejestrze pracy lub poza nim, dziś otrzymujesz prawie tak samo z użyciem „lotnych” i „rejestrów”.
Należy jednak zauważyć, że najnowocześniejsze kompilatory nie rozróżniają dwóch typów deklaracji parametrów.
Przykłady kompilacji z gcc w systemie Linux:
W każdym razie lokalna instrukcja prototypu jest bezużyteczna, ponieważ nie ma wywołania bez parametrów odniesienie do tego prototypu zostanie pominięte. Jeśli używasz systemu z „beztypowym parametrem formalnym”, w przypadku wywołania zewnętrznego przejdź do generowania deklaratywnego prototypowego typu danych.
Lubię to:
źródło
int
, który ogólnie jest albo wielkością rejestru pracy, albo 16 bitów, w zależności od tego, który jest mniejszy.int
typ, który nie byłby w stanie utrzymać wszystkich wartości w tym zakresie - 32767 do +32767 (nota -32768 nie jest wymagana), a także może przechowywać wszystkiechar
wartości (co oznacza, że jeślichar
jest 16 bitów, albo musi być podpisany, alboint
musi być większy).Jeśli chodzi o typ parametru, odpowiedzi są już poprawne, ale jeśli chcesz usłyszeć je z kompilatora, możesz spróbować dodać kilka flag (flagi i tak prawie zawsze są dobrym pomysłem).
kompiluję twój program używając
gcc foo.c -Wextra
:o dziwo
-Wextra
nie łapie tegoclang
(-Wmissing-parameter-type
z jakiegoś powodu nie rozpoznaje , być może historycznych wymienionych powyżej), ale-pedantic
:A w przypadku problemu z prototypem, jak wspomniano powyżej,
int func()
odnosi się do dowolnych parametrów, chyba że zostanie to dokładnie zdefiniowane jako takie,int func(void)
które dałoby błędy zgodnie z oczekiwaniami:lub
clang
jako:źródło
Jeśli deklaracja funkcji nie ma parametrów, tzn. Jest pusta, przyjmuje nieokreśloną liczbę argumentów. Jeśli chcesz, aby nie przyjmował żadnych argumentów, zmień go na:
źródło
Dlatego zazwyczaj radzę ludziom kompilować swój kod:
Te flagi wymuszają kilka rzeczy:
Te flagi są również domyślnie używane w wielu projektach Open Source. Na przykład FreeBSD ma te flagi włączone podczas budowania z WARNS = 6 w twoim Makefile.
źródło