De fundamentala datatyperna Variabler och konstanter måste vara deklarerade före de används. Deklarationer måste göras innan satserna. Början på ett program kan se ut såhär Räkna binärt! Lär ut!!!! 2-komplement! Ta bitwis komplement + 1 Alltså 2 = 0000 0010 -2 = 1111 1101 + 1 = 1111 1110
Variabler Genom att varje variabel har en typ så vet kompilatorn hur mycket minne som ska reserveras för att lagra variabelns värde. Vissa operationer, t.ex. addition, kommer att utföras på olika sätt beroende på de variabler som är inblandade. (För datorn så är det är stor skillnad på att addera två heltal och att addera två flyttal). Att vissa operatorer som + har olika funktioner kallas överlagring.
Uttryck/satser Nedanstående uttryck är ett exempel på ett tilldelnings uttryck i = 7 Hela uttrycket har värdet 7 När ett uttryck följs av ett semi-kolon så kallas det för en sats. i = 7; printf(”The plot thickens!\n”); De två följande satserna är helt korrekta men de utför inget riktigt arbete. Sista punkten. Inte garanterat! 3.777; a + b;
Exempel Ett exempel på en sats är ett tilldelningsuttryck följt av ett semikolon. Detta har följande generella form variable = expr; Först så beräknas värdet på expr. Det värdet tilldelas variable och det värdet blir hela uttryckets värde (satsen har inget värde). Även om tilldelningsuttryck inbland liknar matematiska ekvationer så är de båda begreppen olika. Den matematiska ekvationen x + 2 = 0 blir inte ett tilldelnings uttryck genom att skriva x + 2 = 0 Där har vi ett uttryck till vänster om lika med tecknet, inte en variabel. Uttycket kan inte tilldelas ett värde. Däremot är satsen x = x + 1; giltlig. Variabeln x tilldelas det aktuella värdet av x + 1. Däremot så är x = x + 1 meningslös som matematisk ekvation. Subtrahera x från båda leden och vi får 0 = 1
Datatyper C tillhandahåller många inbyggda datatyper. Det är viktigt att vara medveten om att det finns olika begränsningar för hur stora tal som kan lagras med de olika typerna. char signed char unsigned char short int long unsigned short unsigned unsigned long float double long double Syntaxen för en generell deklaration är declaration ::= type identifier {, identifier}0+ ; float price, tax; int nr_items;
Tecken I C så kan tecken representeras av variabler av vilken heltalstyp (char, signed char, unsigned char, short, int, long, unsigned short, unsigned, unsigned long) som helst då char’s värden kan lagras i de andra typerna. Normal använder man dock char för lagra tecken. ASCII är en standard för vilka tecken som motsvarar vilket tal. Dessa tecken är inte bara de skrivbara (t.ex. ’a’, ’b’,...) utan även kontrolltecken som används för att byta ”skrivrad” (return), ljudsignal, tabulatortecken osv. Att tecken har en motsvarighet i siffror kan ses i följande exempel printf(”%c”, ’A’); printf(”%c”, 65); /* A har nummer 65 i ASCII-tabellen */ ASCII tabellen Båda satserna är ekvivalenta.
Specialtecken
Datatypen int Datatypen int används tillsammans med char, short int och long int för att lagra heltal i C. Matematisk sett finns det oändligt många heltal. På en dator måste det införas en begränsning på hur stora heltalen kan bli. Normalt sparas ett heltal i ett maskin-ord. De flesta datorer använder idag 4 byte (4*8=32 bitar) för att spara ett ord. Men det kan skilja sig åt mellan olika datorer. I limits.h så finns bl.a. makron definerade som anger hur stora värden man kan lagra i int. Om vi antar att en int lagras som 4 bytes så betyder det att den kan anta 232 olika värden. Hälften av värdena används för att lagra negativa heltal och hälften för att lagra positiva heltal. Om i är en variabel av typen int, så kan i anta heltalsvärden inom intervallet [-231, 231-1]. Eftersom variabler har begränsad lagringskapacitet kan man drabbas av overflow, t.ex. om man försöker addera två variabler som tillsammans blir större än det maximala värdet som en variabel kan lagra.
Flyttal (reella tal) ANSI C tillhandahåller tre flyttalstyper: float, double och long double. Variabler av denna typ kan innehålla reella värden som 0.001, 2.0 eller 3.14159. Ett suffix kan läggas till som anger vilken typ konstanten är. f eller F anger att konstanten är float och l eller L anger att den är long double. Ett flyttal utan suffix är av typen double. T.ex. 3.7F säger att vi har en flyttalskonstant av typen float. Vi kan lagra heltal som flyttalskonstanter men vi måste ange dom i formen 1.0 och 2.0. Däremot så är 3 en heltalskonstant. Flyttal kan också anges som 1.234567e5 vilket motsvarar 1.234567*105 Hur stor precision en variabel av float, double eller long double har är systemberoende. Ofta så är precisionen för en double bättre än en float och long double bättre än double, men detta är upp till kompilatorn att bestämma. Att ange ett tal som 1.0 kan vara viktigt i vissa fall. T.ex. double tal; tal = 2/3; /* tal får värdet 0.0 pga heltalsdivision */ Väldigt vanligt nybörjarfel... Minst en av talen i division måste vara av flyttalstyp, annars blir det heltalsdivision.
Float, double och long double På många maskiner så har en float en ungefärlig precision av 6 signifikanta siffror och en ungefärlig omfång (range) på 10-38 till 10+38. 0.d1d2d3d4d5d6 * 10n där di är en siffra och -38 <= n <= +38 (en float representeras egentligen i datorn i bas 2). Double har ofta 15 signifikanta siffror och ett omfång av 10-308 till 10+308. Om vi antar att x är en variabel av typen double så kommer x = 123.45123451234512345; /* 20 signifikanta siffor */ att resultera i att x får ett värde som lagras i formen (bara nästan sant) 0.123451234512345 * 10+3 /* 15 signifikanta siffror */
Exempel Att reella tal inte lagras exakt kan ge upphov till vissa problem. Om vi antar att vår dator kan lagra ett tal med maximalt 5 signifikanta siffor (och att float lagrar med 5 signifikanta siffor). float y; y = 12345F + 0.0001F; /* problem */ Eftersom både 12345F och 0.0001F representeras som float så kommer 12345F att lagras som 0.12345 * 105 och 0.0001F att lagras som 0.10000 * 10-3. Men vid t.e.x. addition måste båda exponentdelarna vara av samma styrka. 1/( (1-a) -1 ) om a = 0.0000000001 så kan vi få division by zero 0.12345 * 105 => 0.12345 * 105 0.10000 * 10-3 => 0.00000 * 105 0.12345 * 105 + 0.00000 * 105 = 0.12345 * 105
typedef Med typedef kan en programmerare associera en datatyp med en identifierare. typedef char uppercase; typedef int INCHES, FEET; typedef unsigned long size_t; /* stddef.h */ När man sedan deklarerar en variabel så kan man använda identifieraren istället. uppercase u; INCHES length, width; Förutom att man slipper skriva långa deklarationer, så får man datatyper som antyder den avsedda användningen av variabeln.
sizeof Operatorn sizeof används för att veta hur många bytes som krävs för att lagra ett objekt. sizeof(object) sizeof returnerar det heltal som representerar det antal bytes som behövs för lagringen. Objekt kan vara en datatyp, ett uttryck (a+b) eller en vektor eller struct typ (för mer om struct, se kap 9). C är flexibelt vad gäller utrymmeskrav för att lagra de fundamentala datatyperna. Detta betyder tyvärr att det kan skilja sig åt mellan olika maskiner, vilket leder till minskad portabilitet.
Lagring Följande gäller dock alltid på alla datorer. sizeof(char) = 1 sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) sizeof(signed) = sizeof(unsigned) = sizeof(int) sizeof(float) <= sizeof(double) <= sizeof(long double) Enligt reglerna ovan så kan t.ex. en long lagras i en byte (8 bitar) och därmed bara anta 28 =256 olika värden (ofta så lagrar man dock long i 8 byte = 64 bitar).
getchar() och putchar() getchar() och putchar() är egentligen makron definerade i stdio.h men används på samma sätt som en funktion. De används för att läsa tecken från tangentbordet (getchar()) och att skriva tecken till skärmen (putchar()). getchar() returnerar en int och putchar() tar en int som argument. I minnet så lagras en char som en byte och en int lagras ofta som 2 eller 4 byte. Därför kan en int lagra alla värden som en char kan. Vi kan tänka oss char som en liten heltalstyp och int som en stor teckentyp. Vi kan alltså lagra tecken i int lika gärna som i en char. I vissa fall kan det vara nödvändigt som vi ser i nästa exempel.
double_out.c Varför kunde vi inte deklarera c som en char?
capitalize.c
Matematiska funktioner Det finns inga inbyggda matematiska funktioner i C utan de tillhandahålls via matematik biblioteket. Matematikbiblioteket är konceptuellt en del av standardbiblioteket (där funktioner som printf() och scanf() finns). Alla matematikfunktioner (förutom pow()) tar ett enda argument av typen double och returnerar en double. En vanligt funktion i många språk är abs() som returnerar absolut-beloppet av argumentet. Men i C så tar abs() bara heltal som argument. För att ta reda på absolutbeloppet av ett flyttal så använd istället fabs(). Att använda abs() där man istället skulle ha behövt fabs() är ett vanligt misstag. Misstaget förvärras av att kompilatorn normalt inte ens ger en varning.
Konvertering Ett uttryck som x+y har både ett värde och en datatyp. Om både x och y är av typen int, så får x+y typen int. Däremot om både x och y är av typen short, så kommer x+y att få typen int. Detta på grund av att short alltid konverteras till int. Heltalskonverteringar En char eller short (signed eller unsigned) kan användas där en int eller unsigned int förväntas. Om t.ex. en funktion tar en int som argument, så kan man skicka in en char istället Däremot om funktionen returerar en int, så bör man inte ta emot det i en char.
Aritmetiska konverteringar Om operanderna till en binär operator är av olika typ så kan en av dom konverteras. Anta att i är int och f är float. I uttrycket i+f så konverteras i till en float och hela uttrycket får typen float. Vid tilldelningar kan också konvertering ske Om vi antar att i är av typen int och d är av typen double, så kommer värdet av d = i att konverteras till double och hela uttrycket bli double. En utökning av en variabel, som sker i fallet med d = i går ofta bra men däremot i = d kan orsaka problem. Decimaldelen av d kommer att försvinna och i kommer att tilldelas heltalsdelen. Exempel på s. 133 i tabellen! double d = 2.34; int i; i = d; /* i == 2 */
Explicita konverteringar Explicita konverteringar (eng. casts) låter programmeraren byta typ på ett uttryck. Om vi antar att i är av typen int så kommer (double) i att konvertera värdet av i så att uttrycket har typen double. Variabeln i påverkas inte av konverteringen och kommer även i fortsättningen att vara av typen int. (long) (’A’ + 1.0) f = (float) ((int) d + 1) d = (double) i / 3 (double) (x = 77)