- En C, totes les funcions són globals. És a dir, es defineixen totes a fora del main, i cap funció pot estar inclosa dins d'una altra funció.
- Per definir una funció, primer cal escriure el tipus del valor que retorna aquella funció, el nom de la funció, i la llista dels paràmetres entre parèntesi, especificant el tipus i el nom de cada paràmetre. Si la funció no retorna res (procediment), llavors cal indicar que retorna un void. Si no té paràmetres, cal posar void dins del parèntesi de paràmetres.
- Després de l'especificació del nom i el tipus dels paràmetres i del resultat, s'especifica entre claus les variables locals i les instruccions associades a aquella funció. Les instruccions, a més de les sentències C, poden ser crides a altres funcions (no hi ha límit de número de crides imbricades) o fins i tot a ella mateixa (recursivitat). Al final de la funció cal posar la sentència return per indicar entre parèntesi el resultat que ha de retornar, excepte en el cas que no retorni res.
ex 2.4: 1ª versió del programa que calcula el Màxim Comú Divisor de dos números. La funció Mcd() rep 2 enters per paràmetre i retorna un altre enter amb el MCD (aplicant l'algorisme d'Euler):
#include <stdio.h> int Mcd(int m, int n) /* defineix nom i tipus de funció */ { int x,y,r; /* variables locals a Mcd */ x = (m>n ? m : n); /* x pren per valor el màxim de m i n */ y = (m>n ? n : m); /* y pren per valor el mínim de m i n */ do { r = x % y; x = y; y = r; /* algorisme d'Euler */ } while (y != 0); return(x); /* retorn de resultat */ } void main(void) /* defineix nom i tipus de la funció principal */ { int x,y; /* variables locals a main */ printf("introdueix 2 números:\n"); scanf("%d",&x); scanf("%d",&y); printf("m.c.d. = %d\n",Mcd(x,y)); /* crida a la funció Mcd(), i després a la funció printf() */ }
- Alguns informàtics prefereixen escriure els programes començant per les funcions més importants i acabant per les més simples (mètode Top-Down). En C, però, abans de fer servir una funció s'ha de declarar (el mateix que amb els tipus, les estructures, les variables, etc.), amb la qual cosa no es pot escriure primer les funcions més importants perquè aquestes criden a les més simples. Per solventar això, es poden declarar només els prototipus de les funcions abans d'escriure el seu codi. Els prototipus són simplement el nom i els tipus dels paràmetres i el resultat, però seguits per un punt i coma. ex 2.5: Segona versió del MCD. Aquesta vegada especificant el prototipus abans de main() (és molt important posar el punt i coma darrera els prototipus, però no quan es defineix el cos de la funció):
#include <stdio.h> int Mcd(int m, int n); /* prototipus de la funció Mcd */ void main(void) { int x,y; printf("introdueix 2 números:\n"); scanf("%d",&x); scanf("%d",&y); printf("m.c.d. = %d\n",Mcd(x,y)); } int Mcd(int m, int n) /* implementació de la funció Mcd */ { int x,y,r; x = (m>n ? m : n); y = (m>n ? n : m); do { r = x % y; x = y; y = r; }while (y != 0); return(x); }
- A les funcions que només tinguin una línia de codi també s'han de posar les claus. Per exemple:
int maxim(int a, int b) { return (a>b ? a : b); } int minim(int a, int b) { return (a>b ? b : a); }
A la funció Mcd es cridarien de la següent forma:
int Mcd(int m, int n) { int x,y,r; x = maxim(m,n); y = minim(m,n); : :
- En els casos en que el codi de la funció sigui molt petit, es pot recórrer a la definició de macros, a través de la directiva define. Cal tenir en compte, però, que només es tracta d'una substitució literal en temps de compilació. És a dir, no es realitza cap mena de crida, i el codi es replica cada cop que s'invoca a la macro. Per exemple, es pot definir els símbols max i min de la següent forma:
#define max(a,b) (((a)>(b)) ? (a):(b)) #define min(a,b) (((a)>(b)) ? (b):(a))
els paràmetres a i b es substituiran quan es trobi un invocació al define, pel símbol que hi hagi en el seu lloc. Això s'indica posant els "pseudoparàmetres" entre parèntesi quan s'escriu el text de substitució: (a) i (b). Per invocar a la macro es fa com si fos una funció:
int Mcd(int m, int n) { int x,y,r; x = max(m,n); /* es substitueix per x = (m>n ? m:n); */ y = min(m,n); /* es substitueix per y = (m>n ? n:m); */ : :
- Dins dels Headers estàndard es troben els prototipus de les funcions de les llibreries estàndard, o bé un conjunt de macros predefinides. Per exemple, dins de STDIO.H es te el prototipus de la funció printf(), i dins de STDLIB.H hi ha les macros de max i min (en els llibres de C es troba la descripció de totes les funcions i macros estàndard disponibles):
#include <stdio.h> /* defineix el prototipus printf() */ #include <stdlib.h> /* defineix les macros max i min */ int Mcd(int m, int n) { int x,y; x = max(m,n); /* fa servir les macros max i min */ y = min(m,n); } void main(void) { printf("%d\n",Mcd(15,230)); /* fa servir la funció printf() */ }
- Els vectors i les matrius, en C, sempre es passen com a paràmetres per referència (de fet, és recomanable que sigui així, per no copiar molts valors a la pila). Quan es defineix un paràmetre en una funció, només s'indica el tipus dels elements, el nom del paràmetre array, i dos claudàtors sense número d'elements, ja que aquest número serà el de l'array que es passi per paràmetre quan es crida la funció (només es posa el nom de l'array a referenciar). ex 2.6: El programa MAXCAR calcula quin és el caràcter que apareix més vegades dins d'una cadena (un array). Cal observar com la funció inicialitzar() accedeix als components del vector que es passi com a paràmetre, perquè es passa per referència:
#include <stdio.h> void inicialitzar(int t[]) /* paràmetre array t */ { int i; /* se suposa que el vector té, com a mínim, 27 elements */ for(i=0; i<=26; i++) t[i]=0; /* accés per referència */ } char maxcar(char s[], int tc[]) /* paràmetres array s i tc */ { char c; int i, max, imax; for(i=0; s[i]!=0; i++) /* recorre el string associat a s */ { c = s[i]; if ((c >= 'a') && (c <= 'z')) tc[c-'a']++; } /* apunta quantes vegades apareix cada lletra */ max = 0; imax = -1; for(i=0; i<=26; i++) if (tc[i] > max) { max = tc[i]; imax = i; } /* detecta el número màxim de lletres aparegudes */ return(imax+'a'); } void main(void) { int ticar[27]; /* un número per cada lletra */ char c, frase[80]; /* string de caràcters a llegir del teclat */ inicialitzar(ticar); /* inicialitza el vector ticar a 0's */ printf("introdueix una frase:\n"); gets(frase); /* llegeix frase (referència) */ c = maxcar(frase,ticar); /* retorna lletra de moda */ printf("lletra que apareix més vegades = %c\n",c); }
- Els paràmetres estructurats (tuples) es poden passar per valor, és a dir, es copiarà tot el contingut d'una variable estructurada sobre el paràmetre associat (incloent-hi tots els elements dels vectors que contingui). Això pot sobrecarregar l'ús de la pila. Per tant, si les estructures són massa complexes cal passar paràmetres per referència (s'explicarà a la secció 3).
ex 2.7: En el programa PUNTS2D es defineix un tipus nou que consta de 2 reals simples (aquest tipus es pot fer servir per definir paràmetres i resultats de funcions). Es tracta d'introduir 2 punts 2D i calcular el punt mig, també en 2D:
#include <stdio.h> typedef struct { float x; float y;} punt2D; /* definició de tipus */ punt2D preguntar_punt(void) /* retorna una variable de tipus 2D */ { punt2D p; printf("\tcoordenada x : "); scanf("%f",&p.x); printf("\tcoordenada y : "); scanf("%f",&p.y); return(p); /* retorna dos floats */ } punt2D punt_mig(punt2D p1, punt2D p2) { /* rep 2 punts2D i retorna el punt mig */ punt2D r; r.x = (p1.x + p2.x) / 2; r.y = (p1.y + p2.y) / 2; return(r); } void main(void) { punt2D a,b,c; printf("introdueix punt 1\n"); a = preguntar_punt(); /* copia el resultat sobre a */ printf("introdueix punt 2\n"); b = preguntar_punt(); /* copia el resultat sobre b */ c = punt_mig(a,b); /* copia a i b sobre p1 i p2, i el resultat sobre c */ printf("\npunt mig = (%.2f,%.2f)\n",c.x,c.y); }
Modificar el fitxer PUNTS2D.C per obtenir el fitxer CERCLE2D.C corresponent a un programa en C que, a partir de 2 punts 2D, imprimeixi per pantalla les coordenades del centre i el radi del cercle que té com a diàmetre la recta que uneix els dos punts. És a dir, que imprimeixi el punt mig i la distància d'aquest punt mig a qualsevol dels 2 punts d'entrada.
Per fer la funció distancia() es necessitarà la funció arrel quadrada, que està definida a MATH.H. La funció té el següent prototipus: double sqrt(double x). la funció distancia() aplicarà el teorema de Pitàgores, segons el qual la distància entre 2 punts es pot calcular com a l'arrel quadrada de la suma de les diferències de coordenades al quadrat. Aquesta funció tindrà un prototipus com aquest:
float distancia(punt2D p1, punt2D p2);
- La funció main també pot rebre paràmetres i retornar resultat. Això s'entén si pensem que el Sistema Operatiu pot reconèixer les paraules (opcions) que s'escriuen darrera de la comanda a executar, i passar aquesta llista de paraules a l'aplicació que invoca la comanda. Per exemple;
C:\>DIR TC /w
invoca la comanda DIR i li passa com a paràmetre les paraules "DIR", "TC" i "/w", per a que el DIR operi en conseqüència (el nom de la comanda o programa també el considera com un paràmetre). El retorn de resultat es refereix a un enter que accepta el S.O. com a codi d'error de l'aplicació (0 : execució correcta; no 0 : número d'error).
- Els dos tipus de paràmetre que rep el main són el número de paraules (enter) i una taula de strings. El què retorna és només enter. Els noms dels paràmetres poden ser qualsevol, encara que en molts llibres apareixen com argc i argv.
ex 2.8: A PARAM.C, el programa imprimeix el número i la llista de paràmetres. Per verificar l'efecte del pas de paràmetres cap a main compilar el PARAM.C, i executar-lo escrivint algunes paraules darrera el nom del programa. Per verificar l'efecte del retorn de resultat, es pot estudiar i executar el fitxer NOPAR.BAT, que crida a PARAM una vegada amb paràmetres i l'altra sense, i canvia l'execució en funció del codi d'error retornat.
#include <stdio.h> int main(int n_param,char *t_param[]) { int i; printf("\nHas introduït %d paràmetres:\n\n",n_param); for(i=0; i<n_param; i++) printf("\t%s\n",t_param[i]); return(n_param==1); } /* retorna CERT si només s'ha passat el nom */