Azine
Om tidningen Azine
Köpa Azine
Kontakta Azine
Hjälpa till
Annonsera i Azine
About Azine English

Senaste numret
Nästa nummer
Arkiv

Kurser/Skolor
Intervjuer
Forum

ACG
SUA
ACG Göteborg
AmigaGuiden

Annonsörer:
GGS-Data

Sponsorer:
AmigaRulez (Forum)
GGS-Data
WestSide Praetorian
M.Andersson

Försäljningsställen
GGS-Data
Guru Meditation


Senaste Nummer 8 Nummer 7 Nummer 7 bilaga Nummer 6 Nummer 5++ Nummer 4 Nummer 3 Nummer 2 Nummer 1









Vill du ha din banner här?
Skriv till [email protected]!
Välkommen till hela Sveriges Amigatidning.


ACGs maskot

Grunderna i C, del 4.

I föra numret började vi att kika lite på pekare genom att gå igenom lite om arrayer och textsträngar. Nu ska vi ta lite mer om hur pekare fungerar.

Pekare

En pekare är en adress till ett objekt i minnet. En array är egentligen en pekare som pekar ut en area i minnet som är förallokerad. Skillnaden mot vad man kallar pekare är att pekaren inte har någon förallokerad area i minnet.
Så en array pekar ut en allokerad area medans en pekare inte behöver peka ut en area alls. Man kan naturligtvis låta en pekare peka ut en area som en array har allokerat. Man kan även allokera upp minne till en pekare så att denna får ett eget segment av minnet. Vad man måste tänka på med pekare är att om man allokerat upp minne så måste man avallokera det annars skapar man minnesläckor.
Vi tar och skriver in ett litet program som demonstrerar lite användning av pekare. Skriv in enligt Figur 1.

Figur 1.

#include 
#include 
#include #define BUFFSIZE    7int main(void)
{
  char *p = NULL;
  char str[BUFFSIZE] = "Magnus";
  p = str+3;
  printf("Result 1:\n%s\n%s\n",str,p);
  p[2] -= 15;
  printf("\nResult 2:\n%s\n%s\n",str,p);  if((p = (char *)malloc(BUFFSIZE
      *sizeof(char))) == NULL)
  {
    printf("Allkeringen misslyckades!");
    return 0;
  }
  strcpy(p,str);
  p[0] = p[2] + 1 + ('H'-'h');
  p[3] = 'r';
  p[4] = p[3] - 9;
  printf("\nResult 3:\n%s\n%s\n",str,p);  free(p);
  p = NULL;
  return 0;
}

Vad har vi gjort?

stdlib.h
Denhär includefilen innehåller information om funktionerna malloc() och free(). Mer om dessa lite senare.
string.h
Den här includefilen innehåller information om strcpy().
char *p = NULL;
Här skapar vi en pekare p som har värdet NULL (0). En pekare som inte pekar på någonting särskilt bör peka på NULL så att man vet att den inte pekar på någonting.
För att visa att det är en pekare och inte en bokstav (char) så sätter man en * före variablelnamnet.
p = str + 3;
Här sätter vi pekaren att peka på adressen för str + 3. Det vill säga det fjärde tecknet i strängen str.
p[2] -= 15;
Här använder vi pekaren på samma sätt som en array och sätter det tredje tecknet till det nuvarande tecknet - 15 (som vi sett i en tidigare del så är ju även bokstäver en siffra i grunden).
Det som händer här är att en bokstav i strängen str ändras. Detta eftersom att vi ändrar värdet i en position i minnet som pekaren pekar på, men den pekar ju på samma minnesarea som strängen (fast 3 tecken fram).
p = (char *)malloc(BUFFSIZE*sizeof(char))
Här allokerar vi en minnesarea. Storleken är BUFFSIZE (som är definierad till 7) * sizeof(char). Sizeof() är en funktion som returnerar storlekar. Eftersom till exempel typerna char och long är olika stora så kan det vara bra att allokera upp storlekar som passar den typ av data man ska lagra. I vårat fall är det char som är 1 byte (8 bitar). Malloc tar ett argument och det är storleken på aream man vill ha. Hade vi bara skrivit BUFFSIZE så hade vi fått BUFFSIZE många bytes och i vårat fall så spelar då inte sizeof() någon roll då det blir BUFFSIZE*1. Men jag tänkte det kunde vara bra att veta av funktionen sizeof().
Malloc returnerar en adress till den allokerade arean. För att kompilatorn ska bli nöjd med den pekaren vi har, en char *, så skriver vi (char *) före för att vi vill ha resultatet konverterat till det formatet.
Skulle malloc misslyckas så returnerar den NULL (0). Därför har vi funktionen i en if-sats som kollar om resultatet är NULL och då skriver ett meddelande och sedan avslutar (return 0).
free(p);
Eftersom vi har allokerat en minnesarea med malloc så måste vi avallokera den. Till detta använder vi oss utav funktionen free().
Man får inte glömma bort att avallokera allokerat minne!
Vissa kanske undrar varför vi inte kör en free vid det första returnet (i if-satsen), men här har ju allokeringen misslyckats, så det finns inget minne att frigöra.

Det om pekare

Så nu har vi gått igenom det viktigaste om pekare. Vad man hela tiden måste hålla koll på är att man inte skriver data till pekare som pekar fel i minnet. AmigaOS (som det ser ut just nu) har inget minnesskydd, så pekar pekaren fel så kan man orsaka en krasch så att man måste starta om datorn (tur att Amiga bootar fort!). Så ha alltid kontroll på pekarna och sätt dem gärna till NULL då ni har frigjort minnet med free() samt kolla att den är NULL innan återanvänder pekarvaribeln till en ny malloc() (annars blir det minnesläckor). Skriv dessutom inte för långt i allokerat minne, för då leker ni med elden!

Filer

Nu är det äntligen dax för att kika lite på hur man hanterar filer i C. Filer är lätt att hantera i C, vilket vi ska se.
För att öppna en fil använder man funktionen fopen(). Fopen() tar två argument. Det första är filnamnet och det andra är en flagga för vad man vill göra med filen. Flaggorna är 'r’ för att läsa, 'w’ är för att skriva (den skapar även filen om den inte finns och tar bort innehåller om det fanns en fil redan) och 'a’ är för att fylla data i en fil. Fopen() returnerar en pekare till en fil (FILE *).
Naturligtvis så måste man ju stänga filen då man har använt den klart och detta kan C sköta själv då programmet avslutas, men man kan även göra det själv med fclose(). Jag använder alltid fclose() själv för att hålla koll på allt jag öppnar. Så en tumregel kan vara att om man gör en fopen() så ska man göra en fclose().
I vårat nästa program så kommer vi att ta och börja skicka argument till programmet. Detta gör man med att ändra lite i 'main'-satsens början. Så den kommer att se ut enligt följande:
int main(int argc, char *argv[])
Argc står för argument count, vilket är antalet argument som skickats till programmet. Argv står för argument variable. Här ser vi att den har både en * och []. Eftersom hakarna saknar någon siffra (som arrayer) betyder det att det är en pekare. Så det är alltså en dubbel pekare. Dvs en pekare som pekar på pekare. Detta för att det är en lista med strängar vi får. Pekaren pekar ut en pekare till en sträng. Lite krångligt? Inte egentligen.
Mer funtioner vi kommer att använda i programmet är getc, som läser ett tecken från en fil, och putc, som skriver ett tecken till en fil.
Mer information än så lär ni inte behöva för att förstå vad som händer i nästa program. Skriv in enligt Figur 2.

Figur 2.

#include 
#include int main(int argc, char *argv[])
{
  char c;
  FILE *infil, *utfil;  if(argc != 3)
  {
    printf(”Usage: %s infil utfil\n”,
             argv[0]);
    return 0;
  }  infil = fopen(argv[1],”r”);
  utfil = fopen(argv[2],”w”);
  if((infil == NULL)  || 
     (utfil == NULL))
  {
    printf(“Kunde inte 
               öppna filerna!\n”);
    return 0;
  }  while((c = getc(infil)) != EOF)
  {
    putc(c,utfil);
  }  fclose(infil);
  fclose(utfil);  return 0;
}

Vad har vi gjort nu?

Nu har vi byggt oss ett eget kopieringskommando för CLI! Programmet kontrollerar först att det är tre argument (programnamnet själv är medräknat). Sedan öppnar programmet filerna, den första för att läsa och den andra för att skriva. Sedan läser man tecken för tecken från infilen, och skriver ner dessa i utfilen, tills man får EOF (End Of File) från getc(). Då vet man att filen är slut!

Lite mer avancerat

Man kan läsa ut data från filer med andra kommandon än putc/getc. Till exempel så kan man använda sig utan fprintf och fscanf. Dessa använder sig utav strängar och inte ett tecken i taget. Naturligtvis så gäller det ju att det ska vara textfilern för att fprintf och fscanf ska fungera.
Vi tar och hoppar över att göra ett exempel på detta till lite senare i den här delen. Först ska vi ta och titta lite mer på hur vi kan lagra data internt i programmen för att sedan göra ett gemensamt exempel för filer och lagringen av data i programmet.

Strukturer

För att gruppera variabler använder man sig utav någonting som heter strukturer (struct). En struktur består av flera olika variabler som grupperas ihop till ett objekt.
Detta kan vara bra då man vill gruppera data som hör ihop. Till exempel så kan man vilja ha en struktur som kan lagra namn och adresser till olika personer. Då kan man skapa en struktur som innehåller förnamn, efternamn, gatuadress och postadress.
För att skapa en struct skriver man in som i början av Figur 3.

Använda strukturer

Att använda strukturer är ganska lätt. Som i exemplet i Figur 3 visar så har en struktur medlemsvariabler och dessa har ju namn, till exempel firstname och lastname.
Vi gör ett litet exempel för att visa hur man kan använda strukturer. Vi håller oss på en ganska lätt nivå här och kommer att vara lite mer avancerade i nästa del av programmeringsskolan. Skriv in enligt Figur 3.

Figur 3.

#include 
#include 
#include #define MAXP 20struct person
{
  char firstname[20];
  char lastname[20];
  int age;
};int main(int argc, char *argv[])
{
  struct person persons[MAXP];
  FILE *datafile = NULL;
  char tmpstr[1024];
  int pos = -1, NotQuit = 1, i;  if(argc < 2)
  {
    printf(”Usage: %s datafile\n”,argc[0]);
    return 0;
  }  for(i = 0; i < MAXP; i++)
  {
    strcpy(persons[i].firstname,””);
    strcpy(persons[i].lastname,””);
    persons[i].age = -1;
  }  datafile = fopen(argv[1],”r”);
  if(datafile != NULL)
  {
    while(fscanf(datafile,”%s”,tmpstr) == 1)
    {
      if(strncmp(tmpstr,”FIRSTN=”,strlen(”FIRSTN=”)) == 0)
      {
        pos++;
        if(pos == MAXP)
        {
          break;
        }        strcpy(persons[pos].firstname,tmpstr+strlen(”FIRSTN=”));
      }
      else if(strncmp(tmpstr,”LASTN=”,strlen(”LASTN=”)) == 0)
      {
        strcpy(persons[pos].lastname,tmpstr+strlen(”LASTN=”));
      }
      else if(strncmp(tmpstr,”AGE=”, strlen(”AGE=”)) == 0)
      {
        persons[pos].age = atoi(tmpstr+strlen(”AGE=”));
      }
      else
      {
        break;
      }
    }    fclose(datafile);
  }
  else
  {
    printf(”Could not open file %s!\n”,argc[1]);
  }  datafile = NULL;
  pos++;  while(NotQuit)
  {
    printf(”\nMeny\n----\n”);
    printf(”1. Lista alla poster\n”);
    printf(”2. Lägg till post\n”);
    printf(”3. Spara data\n”);
    printf(”0. Avsluta\n\n”);
    printf(”Val: “);
    scanf(”%s”,tmpstr);
  	
    switch(atoi(tmpstr))
    {
      case 0: NotQuit = 0;
              break;
      case 1: printf(”+----------------------+----------------------+-------+\n”);
              printf(”| Förnamn             | Efternamn          |Ålder |\n”);
              printf(”+----------------------+----------------------+-------+\n”);
              for(i = 0; i < MAXP; i++)
              {
                if(strlen(persons[i].firstname)==0)
                  break;  
                printf(”| %20s | %20s | %5d |\n”,persons[i].firstname, 
                       persons[i].lastname,persons[i].age);
                printf(”+----------------------+----------------------+-------+\n”);
              }
              break;
      case 2: if(pos < MAXP)
              {
                printf(”\nSkriv in förnamn: “);
                scanf(”%s”,tmpstr);
                strcpy(persons[pos].firstname,tmpstr);
                printf(”Skriv in efternamn: “);
                scanf(”%s”,tmpstr);
                strcpy(persons[pos].lastname,tmpstr);
                printf(”Skriv in ålder: “);
                scanf(”%d”,&(persons[pos].age));
                pos++;
              }
              break;
      case 3: datafile = fopen(argv[1],”w”);
              if(datafile == NULL)
              {
                printf(”Could not reopen file %s!\n”,argc[1]);
                break;
              }              i = 0;              while(strlen(persons[i].firstname) != 0)
              {
                fprintf(datafile,”FIRSTN=%s\n”,persons[i].firstname);
                fprintf(datafile,”LASTN=%s\n”,persons[i].lastname);
                fprintf(datafile,”AGE=%d\n”,persons[i].age);
                i++;
              }              printf(”Data sparat!\n\n”);
              fclose(datafile);              break;
      default: break;
    }
  }
  return 0;
}

Vad har vi gjort?

Först inkuder-ar vi de headerfiler vi behöver. Sedan sätter vi upp ett define på som heter MAXP och som har värdet 20. Varje gång vi skriver in MAXP i koden så kommer kompilatorn att byta ut det mot 20. Så vill vi öka antalet maxposter så räcker det att ändra definet (om man nu har använt det korrekt i koden).
I början av programmet så har vi ´struct person persons[MAXP];´. Här sätter vi upp en array med ´struct person´.
Därefter kollar vi att antalet argument till programmet är korrekt och sedan går vi igenom alla object i arrayen persons och sätter alla medlemsvariabler som är strängar till tomma strängar och integern till -1. Detta för att vi inte ska ha skräp här som programmet kan tro är riktigt data.
Därefter öppnar vi filen och läser ut det som kan stå i filen. Till detta använder vi oss utav ´fscanf´ som läser in data från filen. Här använder vi oss utav ´%s´ och läser då in hela strängar från filen. Vi jämför början på varje sträng vi läst med olika strängar som vi själv skriver in (till exempel ´FIRSTN=´) och hittar vi en träff så lägger vi in det som står efter ´=´ i arrayen. Hittar vi inget som passar våra jämförelser eller att vi uppnått vårat definierade max så avbryter vi inläsningen.
Nu skriver vi ut en meny med olika val vi kan göra och läser in ett nummer.
För att veta vad som är valt använder vi oss av funktionen ´atoi()´ som gör om en sträng till en siffra och resultatet använder vi i en ´switch-sats´. Switchen använder sig utav ´case´ för att kolla vad det är för siffervärde som matats in.
Varje case avslutas med en break för att sluta leta. Det finns även en default-test som heter just default. Den lägger man sist och den körs om den inte avbrytit innan med en annan träff.
Lite beroende på vad vi matar in så gör programmet lite olika. ´1´ skriver ut allt vi har skrivit in, ´2´ använder vi för att mata in nya poster till våran array och ´3´ använder vi för att spara ner informationen på en fil som vi kan läsa in nästa gång vi startar programmet. Observera hur vi använder till exempel ´FIRSTN=´ här och hur det matchar förnamnet i läsningen av filen tidigare i programmet.
Det har blivit lite mer avancerat nu och har ni frågor så maila dem till mig på [email protected] Vi vill ju se mer som programmerar på Amigan!
Nästa gång ska vi kika på hur man använder pekare och strukturer för att kunna göra dynamiska listor utan maxantal.


Hemsidorna underhålls av Magnus Andersson