Programmering i ANSI-C

Standardströmmarna

redigera

stdio.h

redigera

För att användaren av ett program skall kunna mata in data med tangentbordet och läsa textutskrifter på skärmen så finns det ett antal funktioner i biblioteket "stdio.h". Dessa använder dom fördefinerade standardströmmarna "stdin" och "stdout". Här ett skelettprogram som kan användas till exemplen på den här sidan. Programmet inkluderar bilioteket "stdio.h" samt definierar huvudfunktionen "main" där sedan exempelkoderna kan infogas på angiven plats.

/* Inkludering av "stdio.h" */

#include <stdio.h>
 
int main ( void )
{
    Variabeldeklarationer

    Kod för utskrift till skärmen.

    return ( 0 );
}

Utskrift till skärmen

redigera

För att skriva ut enstaka tecken till strömmen används funktionen "putchar" som definieras på följande sätt:

int putchar ( int tecken );

"tecken" är tecknet som skall skrivas till strömmen (skärmen). Det kan anges direkt i parameterlistan eller med lämplig variabel. Om allt gått väl reuneras det medskickade tecknet annars symbolen "EOF" som felindikator.

/* Utskrift med "putchar" */

char ett_b = 'b';

putchar ( 'a' );
putchar ( ett_b );
putchar ( ett_b = 'c' );

Vilket ger följande utskrifter:
abc
Den sista raden där parametern är "ett_b = 'c'" är ett exempel på en egenhet hos programspråket C, nämligen att alla uttryck har ett värde. Ett uttryck där en variabel tilldelas ett värde har samma värde som variabeln tilldelas; i det här exempel tilldelas variabeln "ett_b" värdet "c" i ett uttryck som i sin tur får värdet "c", vilket är det värde som ges som argument till funktionen.

För att skriva ut oformaterade strängkonstanter används funktionen "puts". Funktionen är i princip densamma som hos "putchar" med den skillnaden att här skickas inte enstaka tecken med till funktionen utan hela stängar av tecken. Funktionsdefinition:

int puts ( const char *sträng );

"*sträng" är en pekare till texten som skall skrivas. Vid fel retuneras "EOF". Efter raden skrivits ut så infogas automatiskt ett nyradstecken ("\n"). Därför kommer, om funktionen upprepas, varje ny post skrivas på en egen rad.

/* Utskrift med "puts" */

const char ett_ord[] = "Symfoniorkesterbatterist!";

puts ( "Vilket yrke har en person som spelar timpani?" );
puts ( ett_ord );

Vilket ger följande utskrifter:
Vilket yrke har en person som spelar timpani?
Symfoniorkesterbatterist!

Utskrift av formaterad text

redigera

Funktionen "printf" används för att skriva ut formaterad text. "printf" kan omvandla binära tal till text, infoga strängar och liknande. Funktionen är definierad på följande sätt:

int printf ( const char *format[, parametrar, ...] );

Formatsträngen ("*format") är det som skrivs ut. Vilka och hur många parametrar som skall skickas med bestäms av texten i formatsträngen. Är funktionsanropet framgånsrikt så retuneras ett heltal som anger antalet bytes som skrivits ut. Vid fel retuneras "EOF".

/* Utskrift med "printf" */

int resultat;

resultat = 7 * 7;
printf ( "7 * 7 = %i \n", resultat );    /* Koden "\n" anger radbrytning. */

Vilket ger följande utskrift:
7 * 7 = 49

För att ange vilken typ av dataobjekt som skall skrivas finns ett antal formateringskommandon. Till exempel för att omvandla ett flyttal till en textsträng eller för att infoga ytterligare textsträngar till den befintliga. Nedan följer en tabell som visar vilka typer som kan omvandlas och till vilket format dessa omvandlas med hjälp av dom olika formateringskommandona:

Typomvandlare vid utskrift med "printf" :
c, char, infogar enstaka tecken


d, som i
Ld, som Li

E, double, flyttal på exponentform
LE, long double, i övrigt som E
e, ger litet "e" i resultattexten i övrigt som E
Le, long double, i övrigt som e

f, double, flyttal på decimalform
Lf, long double, i övrigt som f

G, double, om stor exponent som E annars som f
LG, long double, i övrigt som G
g, double, om stor exponent som e annars som f
Lg, long double, i övrigt som g

i, int, heltal på decimal form
Li, long int, annars som i

o, unsigned int, heltal på oktal form
Lo, unsigned long int, annars som o

s, *char, infogar strängar.

u, unsigned int, heltal på decimal form
Lu, unsigned long int, annars som u

x, unsigned int, heltal på hexadecimal form, gemena a-f
Lx, unsigned long int, annars som x
X, versaler A-F i övrigt som x
LX, versaler A-F i övrigt som Lx

Samma koder används även vid utskrift till valfri ström med funktionen "fprintf" eller vid opereration på predefinerade strängar i minnet med funktionen "sprintf".

För att använda formationskoderna så sätts ett procenttecken in i den inledande strängen "%" och sedan följer formateringstecknet. Om flera dataobjekt skall skrivas ut så infogas ytterligare procenttecken till strängen. Sedan skickas dataobjekten med som ytterligare parametrar till funktionen.

/* Utskrift av formaterad text med "printf" */

int x = 180;
float pi = 3.141592;

printf ( "En radian är lika med %f / %i eller %f \n", pi, x, (float) pi / x );
 
Vilket ger följande utskrift:
En radian är lika med 3.141592 / 180 eller 0.017453
Det här är också anledningen till "prinf", "fprintf" och "sprintf" använder ett variabelt antal parameterar. Den inledande strängen avänds på det här sättet för att hålla reda på hur många parametrar som skickats med till funktionen. Om strängen innehåller fyra procenttecken med olika formateringskoder så skall fyra dataobjekt av valda typer skickas med till funktionen. Alternativet vore att använda en funktion för varje möjligt antal parametrar som skall skrivas ut. Vilket vore betydligt klumpigare.

Även tecknet "#" har en funktion vid utskrift av oktala samt hexadecimala tal. Om formatkoden ser ut såhär "%#o" kommer en inledande nolla "0" skrivas före det oktala talet och vid hexadecimaler "%#x" skrivs "0x" som inledande tecken, (vilket är mer eller mindre standard i text idag).

/* Utskrift av hexadecimala eller oktala tal */

int x = 180;

printf ( "%i är oktalt = %#o och hexadecimalt = %#X. \n", x, x, x );
 
Vilket ger följande utskrift:
180 är oktalt = 0264 och hexadecimalt = 0xB4. 

Antalet teckenpositioner som skrivs ut kan även anges i formateringskoden. Det görs genom att ange antalet tecken som skall användas efter procenttecknet men före typomvandlaren. Siffran anger det minsta antalet positioner som skall skrivas ut. Är talet större så skrivs alla siffror ut oavsett det valda antalet. Skulle talet däremot vara kortare än det angivna antalet tecken så infogas blanktecken i stället för siffror. Vid flyttal så kan både antalet heltalsiffror eller antalet decimaler anges. Om bara det ena skall begränsas så skrivs bara en av siffrorna dit men decimalpunkten kvarstår. "%2.3f" ger 00.000, "%2.f" ger 00.x och "%.3f" ger x.000. Om antalet decimaler är fler än det valda så avrundas den sista av dom valda siffrorna.Inleds siffran med en nolla så kommer även nollor att användas i stället för blanktecken. Ett mellanslag efter procenttecknet ger utrymme för talets tecken, (även om talet är positivt men det skrivs då inte dit). Ett inledande minustecken "-" ger västersjusterad utskrift och ett plustecken "+" anger att talets tecken alltid skall infogas, även vid positiva tal.

/* Formaterad utskrift med ett bestämt antal positioner */

printf ( "Talen är: %+10.3f, %06i, % 2i.\n", 123.4567, 100, -999 );

Vilket ger följande utskrift:
Talen är:    +123.457, 000100, -999.

Även strängar går att formatera på det här sättet. Även då kan det minsta antalet tecken som skall skrivas ut anges med en siffra. Är den valda strängen kortare än antalet valda teckenpositioner så fylls det på med blanktecken, (ej nollor eller teckensymboler dock).

/* Utskrift av strängar */

char *text = "kommer här!";

printf ( "Mera text %s \n", text );
printf ( "Mera text %24s \n", text );

Vilket ger följande utskrifter:
Mera text kommer här!
Mera text             kommer här!

Andra format:

Som nämnts ovan går det även att välja vilken stöm som skall skrivas med funktionen "fprinf" som är definierad på följande sätt:

int fprintf ( char *ström, const char *format[, parametrar, ...] );

Skriver formaterad text till "*ström" som är en öppen fil. Se filhantering för mer information om strömmar och filer.

Med funktionen "sprintf" går det även att skapa formaterade stängar som lagras i minnet. Funktionsdefinition:

int sprintf ( char *sträng, const char *format[, parametrar, ...] );

Skriver formaterad text till "*sträng" som är en textbuffer.

Inmatning från tangentbordet

redigera

Den mest primitiva sättet att läsa standardströmmen "stdin", som normalt är knuten till tangentbordet, är genom att mata in ett tecken i tagen med funktionen "getchar".

int getchar ( void );

Reurvärdet är det inlästa tecknet om allt gått väl. Har däremot ett fel uppstått retuneras "EOF" som indikator.

/* Inmatning av enstaka tecken */

int ett_tecken;

ett_tecken = getchar ( );

Inmatningen avslutas med vagnretur och kan vara en hel sträng. Funktionen retunerar endast det första tecknet som har skrivits till strömmen. Vill man däremot mata in en hela strängar så används funktionen "gets" som är definierad på följande sätt:

char *gets ( char *sträng );

Hämtar en sträng från strömmen "stdin" (tangentbordet) och sparar den i minnesblocket som pekaren "*sträng" anger, (som måste vara tillräckligt stort för att rymma hela strängen). Strängen skall avslutas med ett vagnreturstecken (return), det ersätts vid sparningen med ett nolltecken \0 som är det normala sättet att ange radslut i C.

/* Inmatning av textsträngar */

char text [ 256 ];

gets ( text );

Funktionen hämtar en sträng från tangentbordet och sparar den i minnesblocket som pekaren "*text" anger. Blocket måste vara tillräckligt stort för att rymma hela strängen. Strängen skall avslutas med ett vagnreturstecken (return). Det ersätts vid sparningen med ett nolltecken "\0" som är det normala sättet att ange radslut i C.

Inmatning av formaterad text

redigera

För att mata in formaterad text och omvandla den till binär representation i olika variabler används funktionen "scanf", vilken är definierad på följande sätt:

int scanf ( const char *format[, variabelpekare, ...] );

Strängen "*format" anger till vilket format de inmatade tecknen skall typomvandlas och sedan sparas i dom angivna variablerna. Listan nedan visar vilka typer som det är möjligt att omvandla de inmatade tecknen till:

Typomvandlare vid inmatning med "scanf" :
c, char, tecken (vanligtvis 8 bit ASCII)


d, int, heltal på decimal form
hd, short int, i övrigt som d
ld, long int, i övrigt som d

e, float, reella tal på decimal form
le, double, i övrigt som e
Le, long double, i övrigt som e

f, som e
lf, double, i övrigt som e
Lf, long double, i övrigt som e

g, som e
lg, double, i övrigt som e
Lg, long double, i övrigt som e

o, int, heltal på oktal form
ho, short int, i övrigt som o
lo, long int, i övrigt som o

i, som d
hi, short int, i övrigt som d
li, long int, i övrigt som d

s, *char, sträng i övrigt som c

u, unsigned int, heltal på decimal form
hu, unsigned short int, i övrigt som u
lu, unsigned long int, i övrigt som u

x, int, heltal på hexadecimal form
hx, short int, i övrigt som x
lx, long int, i övrigt som x

I normala fall används koderna vid inmatning med "scanf" men det är även möjligt att välja vilken ström som avses med funktionen "fscanf" eller operera på predefinerade strängar i minnet med funktionen "sscanf".

Precis som hos "prinf" så inleds varje post i formatsträngen med ett procenttecken "%". Strängen skall endast innehålla formatkoder, ej någon text. Vill man att raden skall ha en förklarande text så kan den skrivas ut med "printf" eller "puts" innan anropet till "scanf". Användaren matar sedan in beloppen från tangentbordet, (avslutas med [Return]-tangenten). Om flera belopp skall matas in på samma rad så separeras posterna med ett blanktecken, (mellanslag [Space] eller tabulering [Tab]). Parametrarna som skickas med till funktionen skall vara pekare till dom valda variablerna för att systemet skall kunna hitta rätt minnescell¹ som värdet sedan skall sparas i. För att ange en pekare till en variabel, (som inte redan är en pekare), så används ett et-tecken "&" som markör.

¹ Om det inte görs så kommer det aktuella värdet på variabeln i stället att skickas med. Funktionen kommer då att tolka värdet som en pekare och skriva över minnet på den adress som motsvarar beloppet. Vilket antagligen medför att datorn kraschar.
/* Inmatning av variabler */

int   heltal;
float flyttal;

printf ( "Mata först in ett heltal och sedan ett flyttal: " );
scanf ( "%i%f", &heltal, &flyttal );
printf ( "Ett heltal: %i, och ett flyttal: %f \n", heltal, flyttal );

Om talet som matas in är för stort för att rymmas i den angivna variabeln så kommer något oväntat att hända. Om det är ett heltal så kommer det troligtvis att förlora sina mest signifikanta bitar (w:sv:MSD). Om det är ett flyttal så kommer antingen mantissan eller exponenten, kanske båda, att anta oväntade värden. Det kan därför vara lämpligt att ange begränsningarna i en förklarande text. Se: Begränsningar för heltalstyper samt Begränsningar för flyttalstyper.

/* Hur begränsningar kan anges i text vid "promten" */

#include<limits.h>
#include<stdio.h>

int main( void )
{
    short int heltal;

    printf ( "Ange värde (Min:%i, <-> Max:%i); ", SHRT_MIN, SHRT_MAX );
    scanf ( "%hi", &heltal );
    printf ( "Ett heltal: %i", heltal );

    return 0;
}

Testa vad som händer om du matar in för stora eller för små värden på variabeln. Vilka belopp skrivs ut? Överkursen är att försöka förstå varför =) Ledtråd: begränsningarna beror på hur många bitar som används och att signerade heltal vanligtvis lagras i w:sv:tvåkomplementsform i variablerna.

Andra format:

Som nämnts ovan går det även att välja från vilken stöm variablerna skall hämtas med funktionen "fscanf" som är definierad på följande sätt:

int fscanf ( FILE *ström, const char *format[, variabelpekare, ...] );

Skriver formaterad text till "*ström" som är en öppen fil. Se filhantering för mer information om strömmar och filer.

Med funktionen "sscanf" går det även att hämta värden från strängar som är lagrade i datorns minne. Funktionsdefinition:

int sscanf ( const char *sträng, const char *format[, variabelpekare, ...] );

Hämtar formaterad data i textformat från "*sträng" och typomvandlar dessa till ett binärt format som sedan sparas som variabler i minnet.