C/Funkcje: Różnice pomiędzy wersjami

Z Wikibooks, biblioteki wolnych podręczników.
< C
Usunięta treść Dodana treść
Linia 347: Linia 347:


<source lang=c>
<source lang=c>
// https://www.tutorialspoint.com/cprogramming/c_passing_arrays_to_functions.htm

void myFunction(int *param) // Formal parameters as a pointer =
void myFunction(int *param) // Formal parameters as a pointer =



Wersja z 17:08, 15 gru 2016

W matematyce pod pojęciem funkcji rozumiemy twór, który pobiera pewną liczbę argumentów i zwraca wynik[1]. Jeśli dla przykładu weźmiemy funkcję sin(x) to x będzie zmienną rzeczywistą, która określa kąt, a w rezultacie otrzymamy inną liczbę rzeczywistą - sinus tego kąta.

W C funkcja (czasami nazywana podprogramem, rzadziej procedurą) to wydzielona część programu, która przetwarza argumenty i ewentualnie zwraca wartość, która następnie może być wykorzystana jako argument w innych działaniach lub funkcjach. Funkcja może posiadać własne zmienne lokalne. W odróżnieniu od funkcji matematycznych, funkcje w C mogą zwracać dla tych samych argumentów różne wartości.

Po lekturze poprzednich części podręcznika zapewne mógłbyś podać kilka przykładów funkcji, z których korzystałeś. Były to np.

  • funkcja printf(), drukująca tekst na ekranie, czy
  • funkcja main(), czyli główna funkcja programu.

Główną motywacją tworzenia funkcji jest unikanie powtarzania kilka razy tego samego kodu. W poniższym fragmencie:

for(i=1; i <= 5; ++i) {
  printf("%d ", i*i);
}
for(i=1; i <= 5; ++i) {
  printf("%d ", i*i*i);
} 
for(i=1; i <= 5; ++i) {
  printf("%d ", i*i);
}

widzimy, że pierwsza i trzecia pętla for są takie same. Zamiast kopiować fragment kodu kilka razy (co jest mało wygodne i może powodować błędy) lepszym rozwiązaniem mogłoby być wydzielenie tego fragmentu tak, by można go było wywoływać kilka razy. Tak właśnie działają funkcje.

Innym, niemniej ważnym powodem używania funkcji jest rozbicie programu na fragmenty wg ich funkcjonalności. Oznacza to, że jeden duży program dzieli się na mniejsze funkcje, które są "wyspecjalizowane" w wykonywaniu określonych czynności. Dzięki temu łatwiej jest zlokalizować błąd. Ponadto takie funkcje można potem przenieść do innych programów.

Tworzenie funkcji

Dobrze jest uczyć się na przykładach. Rozważmy następujący kod:

int iloczyn (int x, int y)
{
  int iloczyn_xy;
  iloczyn_xy = x*y;
  return iloczyn_xy;
}

int iloczyn (int x, int y) to nagłówek funkcji, który opisuje, jakie argumenty przyjmuje funkcja i jaką wartość zwraca (funkcja może przyjmować wiele argumentów, lecz może zwracać tylko jedną wartość)[2]. Na początku podajemy typ zwracanej wartości - u nas int. Następnie mamy nazwę funkcji i w nawiasach listę argumentów.

Ciało funkcji (czyli wszystkie wykonywane w niej operacje) umieszczamy w nawiasach klamrowych. Pierwszą instrukcją jest deklaracja zmiennej - jest to zmienna lokalna, czyli niewidoczna poza funkcją. Dalej przeprowadzamy odpowiednie działania i zwracamy rezultat za pomocą instrukcji return.

Ogólnie

Funkcję w języku C tworzy się następująco:

typ identyfikator (typ1 argument1, typ2 argument2, typ_n argument_n)
{
  /* instrukcje */
}

Oczywiście istnieje możliwość utworzenia funkcji, która nie posiada żadnych argumentów. Definiuje się ją tak samo, jak funkcję z argumentami z tą tylko różnicą, że między okrągłymi nawiasami nie znajduje się żaden argument lub pojedyncze słówko void - w definicji funkcji nie ma to znaczenia, jednak w deklaracji puste nawiasy oznaczają, że prototyp nie informuje jakie argumenty przyjmuje funkcja, dlatego bezpieczniej jest stosować słówko void.

Funkcje definiuje się poza główną funkcją programu (main). W języku C nie można tworzyć zagnieżdżonych funkcji (funkcji wewnątrz innych funkcji).

Procedury

Przyjęło się, że procedura od funkcji różni się tym, że ta pierwsza nie zwraca żadnej wartości. Zatem, aby stworzyć procedurę należy napisać:

void identyfikator (typ1 argument1, typ2 argument2, typn argument_n)
{
  /* instrukcje */
}

void (z ang. pusty, próżny) jest słowem kluczowym mającym kilka znaczeń, w tym przypadku oznacza "brak wartości".

Generalnie, w terminologii C pojęcie "procedura" nie jest używane, mówi się raczej "funkcja zwracająca void".

Stary sposób definiowania funkcji

Zanim powstał standard ANSI C, w liście parametrów nie podawało się typów argumentów, a jedynie ich nazwy. Również z tamtych czasów wywodzi się oznaczenie, iż puste nawiasy (w prototypie funkcji, nie w definicji) oznaczają, że funkcja przyjmuje nieokreśloną liczbę argumentów. Tego archaicznego sposobu definiowania funkcji nie należy już stosować, ale ponieważ w swojej przygodzie z językiem C Czytelnik może się na nią natknąć, a co więcej standard nadal (z powodu zgodności z wcześniejszymi wersjami) dopuszcza taką deklarację to należy tutaj o niej wspomnieć. Otóż wygląda ona następująco:

typ_zwracany nazwa_funkcji(argument1, argument2, argumentn)
  typ1 argumenty /*, ... */;
  typ2 argumenty /*, ... */;
  /* ... */
{
  /* instrukcje */
}

Na przykład wcześniejsza funkcja iloczyn wyglądałaby następująco:

int iloczyn(x, y)
  int x, y;
{
  int iloczyn_xy;
  iloczyn_xy = x*y;
  return iloczyn_xy;
}

Najpoważniejszą wadą takiego sposobu jest fakt, że w prototypie funkcji nie ma podanych typów argumentów, przez co kompilator nie jest w stanie sprawdzić poprawności wywołania funkcji. Naprawiono to (wprowadzając definicje takie jak je znamy obecnie) najpierw w języku C++, a potem rozwiązanie zapożyczono w standardzie ANSI C z 1989 roku.

Deklarowanie funkcji

Czasami możemy chcieć przed napisaniem funkcji poinformować kompilator, że dana funkcja istnieje. Niekiedy kompilator może zaprotestować, jeśli użyjemy funkcji przed określeniem, jaka to funkcja, na przykład:

int a()
{
  return b(0);
}

int b(int p)
{
  if( p == 0 )
    return 1;
  else
    return a();
}
 
int main()
{
  return b(1);
}

W tym przypadku nie jesteśmy w stanie zamienić a i b miejscami, bo obie funkcje korzystają z siebie nawzajem. Rozwiązaniem jest wcześniejsze zadeklarowanie funkcji. Deklaracja funkcji (zwana czasem prototypem) to po prostu przekopiowana pierwsza linijka funkcji (przed otwierającym nawiasem klamrowym) z dodatkowo dodanym średnikiem na końcu. W naszym przykładzie wystarczy na samym początku wstawić:

int b(int p);

W deklaracji można pominąć nazwy parametrów funkcji:

int b(int);

Bardzo częstym zwyczajem jest wypisanie przed funkcją main samych prototypów funkcji, by ich definicje umieścić po definicji funkcji main, np.:

int a(void);
int b(int p);

int main()
{
  return b(1);
}

int a()
{
  return b(0);
}

int b(int p)
{
  if( p == 0 )
    return 1;
  else
    return a();
}

Z poprzednich rozdziałów pamiętasz, że na początku programu dołączaliśmy tzw. pliki nagłówkowe. Zawierają one właśnie prototypy funkcji i ułatwiają pisanie dużych programów. Dalsze informacje o plikach nagłówkowych zawarte są w rozdziale Tworzenie bibliotek.

Zmienna liczba parametrów

Zauważyłeś zapewne, że używając funkcji printf() lub scanf() po argumencie zawierającym tekst z odpowiednimi modyfikatorami mogłeś podać praktycznie nieograniczoną liczbę argumentów. Zapewne deklaracja obu funkcji zadziwi Cię jeszcze bardziej:

int printf(const char *format, ...);
int scanf(const char *format, ...);

Jak widzisz w deklaracji zostały użyte trzy kropki. Otóż język C ma możliwość przekazywania teoretycznie nieograniczonej liczby argumentów do funkcji (jedynym ograniczeniem jest rozmiar stosu programu). Cała zabawa polega na tym, aby umieć dostać się do odpowiedniego argumentu oraz poznać jego typ (używając funkcji printf, mogliśmy wpisać jako argument dowolny typ danych). Do tego celu możemy użyć wszystkich ciekawostek, zawartych w pliku nagłówkowym stdarg.h.

Załóżmy, że chcemy napisać prostą funkcję, która dajmy na to, mnoży wszystkie swoje argumenty (zakładamy, że argumenty są typu int). Przyjmujemy przy tym, że ostatni argument będzie 0. Będzie ona wyglądała tak:

 #include <stdarg.h>
 
 int mnoz (int pierwszy, ...)
 {
   va_list arg;
   int iloczyn = 1, t;
   va_start (arg, pierwszy);
   for (t = pierwszy; t; t = va_arg(arg, int)) {
     iloczyn *= t;
   } 
   va_end (arg);
   return iloczyn;
 }

va_list oznacza specjalny typ danych, w którym przechowywane będą argumenty, przekazane do funkcji. "va_start" inicjuje arg do dalszego użytku. Jako drugi parametr musimy podać nazwę ostatniego znanego argumentu funkcji. Makropolecenie va_arg odczytuje kolejne argumenty i przekształca je do odpowiedniego typu danych. Na zakończenie używane jest makro va_end - jest ono obowiązkowe!

Oczywiście, tak samo jak w przypadku funkcji printf() czy scanf(), argumenty nie muszą być takich samych typów. Rozważmy dla przykładu funkcję, podobną do printf(), ale znacznie uproszczoną:

#include <stdarg.h>

void wypisz(const char *format, ...) {
  va_list arg;
  va_start (arg, format);
  for (; *format; ++format) {
    switch (*format) {
    case 'i': printf("%d" , va_arg(arg, int)); break;
    case 'I': printf("%u" , va_arg(arg, unsigned)); break;
    case 'l': printf("%ld", va_arg(arg, int)); break;
    case 'L': printf("%lu", va_arg(arg, unsigned long)); break;
    case 'f': printf("%f" , va_arg(arg, double)); break;
    case 'x': printf("%x" , va_arg(arg, unsigned)); break;
    case 'X': printf("%X" , va_arg(arg, unsigned)); break;
    case 's': printf("%s" , va_arg(arg, const char *)); break;
    default : putc(*format);
    }
  }
  va_end (arg);
}

Przyjmuje ona jako argument ciąg znaków, w których niektóre instruują funkcję, by pobrała argument i go wypisała. Nie przejmuj się jeżeli nie rozumiesz wyrażeń *format i ++format. Istotne jest to, że pętla sprawdza po kolei wszystkie znaki formatu.

Wywoływanie funkcji

Funkcje wywołuje się następująco:

identyfikator (argument1, argument2, argumentn);

Jeśli chcemy, aby przypisać zmiennej wartość, którą zwraca funkcja, należy napisać tak:

zmienna = funkcja (argument1, argument2, argumentn);

Przykładowo, mamy funkcję:

void pisz_komunikat()
{
  printf("To jest komunikat\n");
}

Jeśli teraz ją wywołamy:

pisz_komunikat;   /* ŹLE    */
pisz_komunikat(); /* dobrze */

to pierwsze polecenie nie spowoduje wywołania funkcji. Dlaczego? Aby kompilator C zrozumiał, że chodzi nam o wywołanie funkcji, musimy po jej nazwie dodać nawiasy okrągłe, nawet, gdy funkcja nie ma argumentów. Użycie samej nazwy funkcji ma zupełnie inne znaczenie - oznacza pobranie jej adresu. W jakim celu? O tym będzie mowa w rozdziale Wskaźniki.

Przykład

A oto działający przykład, który demonstruje wiadomości podane powyżej:

 #include <stdio.h>
 
 int suma (int a, int b)
 {
   return a+b;
 }
 
 int main ()
 {
   int m = suma (4, 5);
   printf ("4+5=%d\n", m);
   return 0;
 }

Zwracanie wartości

return

return to słowo kluczowe języka C.

W przypadku funkcji służy ono do:

  • przerwania funkcji (i przejścia do następnej instrukcji w funkcji wywołującej)
  • zwrócenia wartości.

W przypadku procedur powoduje przerwania procedury bez zwracania wartości.

Użycie tej instrukcji jest bardzo proste i wygląda tak:

return zwracana_wartość;

lub dla procedur:

return;

Możliwe jest użycie kilku instrukcji return w obrębie jednej funkcji. Wielu programistów uważa jednak, że lepsze jest użycie jednej instrukcji return na końcu funkcji, gdyż ułatwia to śledzenie przebiegu programu.

Zwracana wartość

W C zwykle przyjmuje się, że 0 oznacza poprawne zakończenie funkcji:

return 0; /* funkcja zakończona sukcesem */

a inne wartości oznaczają niepoprawne zakończenie:

return 1; /*funkcja zakończona niepowodzeniem */

Ta wartość może być wykorzystana przez inne instrukcje, np. if .

Jak zwrócić kilka wartości?

Jeśli chcesz zwrócić z funkcji kilka wartości, musisz zrobić to w trochę inny sposób. Generalnie możliwe są dwa podejścia:

  • "upakowanie" zwracanych wartości – można stworzyć tak zwaną strukturę, która będzie przechowywała kilka zmiennych (jest to opisane w rozdziale Typy złożone).
  • zwracanie jednej z wartości w normalny sposób ( return), a pozostałych jako parametrów. Jeśli chcesz zobaczyć przykład, możesz przyjrzeć się funkcji scanf() z biblioteki standardowej.

Za pomocą struktur

Przykład [3]

#include<stdio.h>
 
typedef struct{
	int integer;
	float decimal;
	char letter;
	char string[100];
	double bigDecimal;
}Composite;
 
Composite f()
{
	Composite C = {1, 2.3, 'a', "Hello World", 45.678};
	return C;
}
 
 
int main()
{
	Composite C;
          

        C = f();
 
	printf("Values from a function returning a structure : { %d, %f, %c, %s, %f}\n", C.integer, C.decimal, C.letter, C.string, C.bigDecimal);
 
	return 0;
}

Za pomocą wskaźników (parametrów)

Gdy wywołujemy funkcję, wartość argumentów, z którymi ją wywołujemy, jest kopiowana do funkcji. Kopiowana - to znaczy, że nie możemy normalnie zmienić wartości zewnętrznych dla funkcji zmiennych. Formalnie mówi się, że w C argumentyprzekazywane przez wartość, czyli wewnątrz funkcji operujemy tylko na ich kopiach.

Możliwe jest modyfikowanie zmiennych przekazywanych do funkcji jako parametry - ale do tego w C potrzebne są wskaźniki.

Przykład

Funkcja swap wczytuje 2 wartości i zamienia je miejscami

#include <stdio.h>
// gcc s.c -Wall
// ./a.out
void swap (int *a, int *b)  {
    int temp = *a;
    *a = *b;
    *b = temp;
}


int main()
{
int x=3, y=4;

printf("x=%d ; y= %d\n", x,y); 
swap(&x, &y);
printf("x=%d ; y= %d\n", x,y); 


 return 0;

}

Wynik programu :


 x=3 ; y= 4
 x=4 ; y= 3


Tablice jako parametr funkcji

Istnieją 3 sposoby :

  • wskaźnik
  • tablica z podaną wilkością
  • tablica bez podanej wielkości


// https://www.tutorialspoint.com/cprogramming/c_passing_arrays_to_functions.htm 

void myFunction(int *param) // Formal parameters as a pointer  = 

void myFunction(int param[10]) // Formal parameters as a sized array −

void myFunction(int param[]) //Formal parameters as an unsized array −

Ponieważ nazwa tablicy jest wskaźnikiem do jej pierwszego elementu, to możemy korzystać z tablic w ten sposób:[4]

#include<stdio.h>

void read(int c[],int i)
{
    int j;
    for(j=0;j<i;j++)
        scanf("%d",&c[j]);
    fflush(stdin);
}

void display(int d[],int i)
{
    int j;
    for(j=0;j<i;j++)
        printf("%d ",d[j]);
    printf("\n");
}
   
  
int main()
{
    int a[5];
    printf("Wprowadź 5 elementów listy \n");
    read(a,5);
    printf("Elementami listy są : \n");
    display(a,5);
    return 0;
}

Funkcje nie tylko mają dostęp do tablicy, ale i mogą ją zmieniać.


Przekazywanie wielowymiarowych tablic:

Specjalne funkcje

Funkcja main()

Do tej pory we wszystkich programach istniała funkcja main(). Po co tak właściwie ona jest? Otóż jest to funkcja, która zostaje wywołana przez fragment kodu inicjującego pracę programu. Kod ten tworzony jest przez kompilator i nie mamy na niego wpływu. Istotne jest, że każdy program w języku C musi zawierać funkcję main().

Istnieją dwa możliwe prototypy (nagłówki) omawianej funkcji:

  • int main(void);
  • int main(int argc, char **argv); [7]

Argument argc jest liczbą nieujemną określającą, ile ciągów znaków przechowywanych jest w tablicy argv. Wyrażenie argv[argc] ma zawsze wartość NULL. Pierwszym elementem tablicy argv, czyli argv[0] (o ile istnieje[8]) jest nazwa programu[9] czy komenda, którą program został uruchomiony. Pozostałe przechowują argumenty podane przy uruchamianiu programu.

Zazwyczaj jeśli program uruchomimy poleceniem:

program argument1 argument2 

to argc będzie równe 3 (2 argumenty + nazwa programu), a argv będzie zawierać napisy program, argument1, argument2 umieszczone w tablicy indeksowanej od 0 do 2.

Weźmy dla przykładu program, który wypisuje to, co otrzymuje w argumentach argc i argv:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
  int i;
  for (i = 0; i<argc; ++i) {
    printf("%s\n", argv[i]);
  }
  return EXIT_SUCCESS;
}

Uruchomiony w systemie typu UNIX poleceniem ./test foo bar baz powinien wypisać:

./test
foo
bar
baz

Na razie nie musisz rozumieć powyższych kodów i opisów, gdyż odwołują się do pojęć takich jak tablica oraz wskaźnik, które opisane zostaną w dalszej części podręcznika.

Jeśli program nie wczytuje  żadnych argumentów to : [10]

int main(int argc, char **argv)
{
  (void) argc;
  (void) argv;
  return 0;
}



Co ciekawe, funkcja main nie różni się zanadto od innych funkcji i tak jak inne może wołać sama siebie (patrz rekurencja niżej), przykładowo powyższy program można zapisać tak[11]:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
  if (*argv) {
    puts(*argv);
    return main(argc-1, argv+1);
  } else {
    return EXIT_SUCCESS;
  }
}

Ostatnią rzeczą dotyczącą funkcji main jest zwracana przez nią wartość. Już przy omawianiu pierwszego programu wspomniane zostało, że jedynymi wartościami, które znaczą zawsze to samo we wszystkich implementacjach języka są 0, EXIT_SUCCESS i EXIT_FAILURE[12] zdefiniowane w pliku nagłówkowym stdlib.h. Wartość 0 i EXIT_SUCCESS oznaczają poprawne zakończenie programu (co wcale nie oznacza, że makro EXIT_SUCCESS ma wartość zero), natomiast EXIT_FAILURE zakończenie błędne. Wszystkie inne wartości są zależne od implementacji.

Funkcje rekurencyjne

Poniżej przekażemy ci parę bardziej zaawansowanych informacji o funkcjach w C, jeśli nie masz ochoty wgłębiać się w szczegóły, możesz spokojnie pominąć tę część i wrócić tu później.

Język C ma możliwość tworzenia tzw. funkcji rekurencyjnych. Jest to funkcja, która w swojej własnej definicji (ciele) wywołuje samą siebie. Najbardziej klasycznym przykładem może tu być silnia. Napiszmy sobie zatem naszą funkcję rekurencyjną, która oblicza silnię:

int silnia (int liczba)
{
  int sil;
  if (liczba<0) return 0; /* wywołanie jest bezsensowne, zwracamy 0 jako kod błędu */
  if (liczba==0 || liczba==1) return 1;
  sil = liczba*silnia(liczba-1);
  return sil;
}

Musimy być ostrożni przy funkcjach rekurencyjnych, gdyż łatwo za ich pomocą utworzyć funkcję, która będzie sama siebie wywoływała w nieskończoność, a co za tym idzie będzie zawieszała program. Tutaj pierwszymi instrukcjami if ustalamy "warunki stopu", gdzie kończy się wywoływanie funkcji przez samą siebie, a następnie określamy, jak funkcja będzie wywoływać samą siebie (odjęcie jedynki od argumentu, co do którego wiemy, że jest dodatni, gwarantuje, że dojdziemy do warunku stopu w skończonej liczbie kroków).

Warto też zauważyć, że funkcje rekurencyjne czasami mogą być znacznie wolniejsze niż podejście nierekurencyjne (iteracyjne, przy użyciu pętli). Flagowym przykładem może tu być funkcja obliczająca wyrazy ciągu Fibonacciego:

 #include <stdio.h>
 
 unsigned count;
 
 unsigned fib_rec(unsigned n) {
   ++count;
   return n<2 ? n : (fib_rec(n-2) + fib_rec(n-1));
 }
 
 unsigned fib_it (unsigned n) {
   unsigned a = 0, b = 0, c = 1;
   ++count;
   if (!n) return 0;
   while (--n) {
     ++count;
     a = b;
     b = c;
     c = a + b;
   }
   return c;
 }
 
 int main(void) {
   unsigned n, result;
   printf("Ktory element ciagu Fibonacciego obliczyc? ");
   while (scanf("%d", &n)==1) {
     count = 0;
     result = fib_rec(n);
     printf("fib_rec(%3u) = %6u  (wywolan: %5u)\n", n, result, count);
 
     count = 0;
     result = fib_it (n);
     printf("fib_it (%3u) = %6u  (wywolan: %5u)\n", n, result, count);
   }
   return 0;
 }

W tym przypadku funkcja rekurencyjna, choć łatwiejsza w napisaniu, jest bardzo nieefektywna.

Funkcje zagnieżdżone

W języku C nie można tworzyć zagnieżdżonych funkcji (funkcji wewnątrz innych funkcji).

Ezoteryka C

C ma wiele niuansów, o których wielu programistów nie wie lub łatwo o nich zapomina:

  • jeśli nie podamy typu wartości zwracanej w funkcji, zostanie przyjęty typ int (według najnowszego standardu C99 nie podanie typu wartości jest zwracane jako błąd);
  • jeśli nie podamy żadnych parametrów funkcji, to funkcja będzie używała zmiennej liczby parametrów (inaczej niż w C++, gdzie przyjęte zostanie, że funkcja nie przyjmuje argumentów). Aby wymusić pustą listę argumentów, należy napisać int funkcja(void) (dotyczy to jedynie prototypów czy deklaracji funkcji);
  • jeśli nie użyjemy w funkcji instrukcji return, wartość zwracana będzie przypadkowa (dostaniemy śmieci z pamięci).
  • W języku C nie jest możliwe przekazywanie typu jako argumentu.

Kompilator C++ użyty do kompilacji kodu C najczęściej zaprotestuje i ostrzeże nas, jeśli użyjemy powyższych konstrukcji. Natomiast czysty kompilator C z domyślnymi ustawieniami nie napisze nic i bez mrugnięcia okiem skompiluje taki kod.

Zobacz też

Przypisy

  1. Aby nie urażać matematyków sprecyzujmy, że chodzi o relację między zbiorami X i Y (X jest dziedziną, Y jest przeciwdziedziną) takie, że każdy element zbioru X jest w relacji z dokładnie jednym elementem ze zbioru Y.
  2. Bardziej precyzyjnie można powiedzieć, że funkcja może zwrócić tylko jedną wartość typu prostego lub jeden adres do jakiegoś obiektu w pamięci.
  3. Przykład z rosettacode
  4. Pass array to function at java2s
  5. 2 wymiarowa tablica jako argument funkcji - java2s.com
  6. Przekazywanie wielowymiarowych tablic do funkcji - wyjaśnienia
  7. Czasami można się spotkać z prototypem int main(int argc, char **argv, char **env);, który jest definiowany w standardzie POSIX, ale wykracza już poza standard C.
  8. Inne standardy mogą wymuszać istnienie tego elementu, jednak jeśli chodzi o standard języka C to nic nie stoi na przeszkodzie, aby argument argc miał wartość zero.
  9. Stackoverflow : Is “argv[0 = name-of-executable” an accepted standard or just a common convention? ]
  10. stackoverflow question : is-it-better-to-use-c-void-arguments-void-foovoid-or-not-void-foo?rq=1
  11. Jeżeli ktoś lubi ekstrawagancki kod ciało funkcji main można zapisać jako return *argv ? puts(*argv), main(argc-1, argv+1) : EXIT_SUCCESS;, ale nie radzimy stosować tak skomplikowanych i, bądź co bądź, mało czytelnych konstrukcji.
  12. Uwaga! Makra EXIT_SUCCESS i EXIT_FAILURE te służą tylko i wyłącznie jako wartości do zwracania przez funkcję main(). Nigdzie indziej nie mają one zastosowania.
  13. Kompilator GCC do wersji 4.3 nie obsługuje tychże rozszerzeń