Kuitenkin muutama kirjoitusvirhe on ylitse muiden:
Mikäli saat hirveän listan täysin käsittämättömiltä tuntuvia virheilmoituksia, saattaa syynä olla puuttuva kommentin loppumerkki.
Nykyisten ohjelmointiympäristöjen (Borland C++, MS Visual C++) värilliset syntaksin esitystavat auttavat huomattavasti poistamaan näitä virheitä. Siis seuraa värejä ja hurraa niiden keksijöille.
Lisäksi tulee muistaa, että
'a' - kirjainvakio a (tavu 61H) "a" - merkkijonovakio jossa tavut (61H ja 00)Lainausmerkeissäkin editorin värit auttavat!
Myös dynaamisesti varatun jonon ylittäminen on vaarallista:
char *p; p = tee_jono("Kissa!"); strcpy(p,"Kissa istuu puussa!"); /* VÄÄRIN! */
char *ali(...) { char *p; ... p = tee_jono("Kissa!"); ... if ( f == NULL ) return VIRHE; /* !!! poistutaan vapauttamatta p:n tilaa !!! */ ... free(p); return NULL; }Jokaisella aliohjelman kutsukerralla varataan uusi tila merkkijonolle ja mikäli kutsuja tulee riittävästi, muisti loppuu pelkkiin Kissoihin! Tätä voidaan C++:ssa välttää ovelalla hajottimien käytöllä.
char *p, jono[50]; strcpy(p,"Kissa"); /* VÄÄRIN! */ p = jono+3; strcpy(p,"Kissa"); /* Kelpaa */
int luvut[10];niin viimeinen indeksi onkin 9!
Lisäksi toinen erittäin vaarallinen tilanne syntyy, mikäli format- osassa on eri määrä parametrejä kuin varsinaisessa parametrilistassa.
Myös parametrin tyypin ja format- osassa olevan formaatin sotkeminen saattaa kaataa koko koneen.
int i; scanf("%lf",&i); /* VÄÄRIN! */Aina kun kirjoittaa jonkin scanfin sukuisen lauseen, pitää havahtua ja tarkistaa ainakin 3 kertaa lauseen olevan kunnossa. Tämänkin monisteen malliohjelmia kirjoitettaessa scanf on ollut kymmeniä kertoja väärin!
Usein myös newline merkki '\n' unohtuu rivin lopusta. Tällöin tietysti kaikki tulosteet tulevat enempi tai vähempi sekaisin näytölle.
#define b 10*10 ... a = 5.0/b; /* - > a==5.0 !!!! */Erityisesti runsas sulkujen käyttö auttaa välttämään ongelmia.
'a' = 0x61 = 0110 0001 - > 0000 0000 0110 0001 (unsigned) 0000 0000 0110 0001 (signed) 'ä' = 0x84 = 1000 0100 - > 0000 0000 1000 0100 (unsigned) 1111 1111 1000 0100 (signed)Erityisesti tämä on muistettava käytettäessä kirjaimia indekseinä:
char c; ... kirjaimet[c]++; /* Lisätään kirjainten lkm. */ /* VÄÄRIN */ ... kirjaimet[(unsigned char)c]++; /* OIKEIN! */ ...
#include <stdio.h> int main(void) { double d; int i; i = 5; d = i/2; printf("d = %4.2lf\n",d); return 0; }Edellinen ohjelma tulostaa d = 2.00. Lausekkeen arvo kussakin vaiheessa on sama kuin sen laskemisessa siihen saakka tarvitun "monimutkaisimman" tyypin arvo. Mallissa ollaan kokoajan arvossa int. Siis myös jakolaskun tulos on int. Vika voidaan korjata kahdella tavalla:
d = i/2.0; d = ((double)i)/2;
#include <stdio.h> int main(void) { float d = 0.0001; int i; i = 10000; i = d*i; printf("i = %4d\n",i); return 0; }
Miksikö? Koska reaaliluku 0.0001 voisi sisäisenä esityksenä olla jotakin 0.000099999 ja kun tämä kerrotaan 10000:lla, tulee vähän alle 1 joka kokonaisluvuksi katkaistuna on 0!
char *nimet[] = { "Mikko" "Pekka", "Matti" }Tästä seuraisi kahden nimen taulukko "MikkoPekka", "Matti"!
char *kissa() { char jono[50]; strcpy(jono,"kissa"); return jono; /* VÄÄRIN! */ return "kissa"; /* OIKEIN! */ }
char rivi[80],*p1,*p2; int j; ... lue_jono(N_S(rivi)); p1 = palanen(rivi," ",&j); ... /* Täällä ei viitata p1:een! */ lue_jono(N_S(rivi)); p2 = palanen(rivi," ",&j); if ( strcmp(p1,p2 ) ... /* ON AINA p1=p2!!! */
double d; d = 2,5; // Laskee 2 ja 5 ja sijoittaa d = 2;