Umu logo

Umeå Universitet
Institutionen för datavetenskap
Programmeringsteknik för tekniska fysiker


Niklas Frykholm, 24 februari 1999

Gruppövning 3 - svar

Här är några förslag på lösningar till uppgifterna. Kom dock ihåg att det alltid finns mer än ett sätt att lösa en uppgift.

1. Finn sex fel

Det rättade programmet ser ut som.

g3_1.c


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void sort(char **s, int n);

/* Detta program läser in tre strängar och skriver ut
   dem i sorterad ordning. */
int main(void)
{
  char *s[3];
  int i;

  s[0] = malloc(100);
  s[1] = malloc(100);
  s[2] = malloc(100);

  for (i=0; i<3; i++)
    scanf("%s", s[i]);

  sort(s,3);

  for (i=0; i<3; i++)
    printf("%s\n", s[i]);

  free(s[0]); free(s[1]); free(s[2]);
  return 0;
}

/* Sorterar en vektor med strängar i bokstavsordning. */
void sort(char **s, int n)
{
  int i, j;

  for (i=0; i<n-1; i++)
    for (j=i+1; j<n; j++)
      if (strcmp(s[i],s[j])>0) {
    char *temp = s[i];
    s[i] = s[j];
    s[j] = temp;
      }
}

2. Hisstopp

g3_2.c


#include <stdio.h>
#include <math.h>
#include <stdlib.h>

int akt_vaningar(int vaningsstopp[], int antal);

int main(void)
{
  int stopp, sum, *vaningar, i;

  printf("Hur många stopp? ");
  scanf("%d", &stopp);

  vaningar = malloc(stopp*sizeof(int));
  for (i=0; i<stopp; i++)
    scanf("%d", &vaningar[i]);

  sum = akt_vaningar(vaningar, stopp);

  printf("Hissen har sammanlagt åkt %d våningar.\n", sum);
  if (sum >10000)
    printf("Det är dags för service.\n");
  return 0;
}

int akt_vaningar(int vaningsstopp[], int antal)
{
  int i, sum = 0;

  for (i=1; i<antal; i++)
    sum += abs(vaningsstopp[i] - vaningsstopp[i-1]);
  return sum;
}

Jag har valt att allokera vektorn med våningsstopp dynamiskt (med malloc) det gör att vi kan göra den precis lagom stor.

3. Baklängessträng

g3_3.c


#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

int main(void)
{
  char *s, *p;

  s = malloc(100*sizeof(char));
  assert(s);

  scanf("%s", s);
  p = s;
  while (*p)
    p++;

  p--;
  while (p>=s) {
    putchar(*p);
    p--;
  }

  free(s);
  return 0;
}

En annan idé är att sätta p = s + strlen(s) - 1; för att direkt hoppa till slutet av strängen.

4. Pekarträsk I

Problemet kan lösas på flera olika sätt, bl a genom att rita bilder eller genom att räkna på adresser. Jag visar den algebraiska metoden eftersom den är enklast att skriva ned. Kom ihåg de grundläggande räknereglerna.

a[i] <=> *(a+i)
&*p <=> *&p <=> p
*(i[1] + 1) <=> i[1][1] <=> i[0][0] + 4 <=> *(i[0] + 0) + 4 <=>
<=> *i[0] + 4 <=> **(i+0) + 4 <=> **i + 4 <=> 5 + 4 <=> 9

5. Pokerhänder

g3_5.c


#include <stdio.h>

int full_hand(int hand[5]);

int main(void)
{
  int h[5], i;

  printf("Mata in fem kortnr: ");
  for (i=0; i<5; i++)
    scanf("%d", &h[i]);

  if (full_hand(h))
    printf("Du har kåk.\n");
  else
    printf("Du har inte kåk.\n");
  return 0;
}

int full_hand(int hand[5])
{
  /* Håller reda på hur många kort vi sett av varje valör. */
  int count[13] = {0};

  int i;
  int treval = 0, tvaval = 0;

  /* Om vi numrerar korten så att 0 är hjärter ess 1 hjärter två, ..
     13 klöver ess, 14 klöver två osv, kan vi beräkna ett korts
     valör som kortnr % 13. */
  for (i=0; i<5; i++)
    count[hand[i]%13]++;

  /* Om count innehåller en valör med tre kort och en med två kort
     har vi en kåk. */
  for (i=0; i<13; i++) {
    if (count[i] == 3)
      treval = 1;
    if (count[i] == 2)
      tvaval = 1;
  }

  return treval && tvaval;
}

En annan metod än att använda en vektor för att räkna hur många kort vi har av varje valör är att först sortera korten i handen i storleksordning och sedan kontrollera.

  if ( hand[0]%13 == hand[1]%13 && hand[3]%13 == hand[4]%13 &&
    (hand[2]%13 == hand[1]%13 || hand[2]%13 == hand[3]%13))

Men det är litet krångligare...

6. Kodade strängar

g3_6.c


#include <stdio.h>

void print_code(char *s);
void print_code_rec(char *s);

int main(void)
{
  char s[100];

  printf("Skriv en sträng: ");
  scanf("%s", s);

  printf("print_code: ");
  print_code(s);
  printf("\nprint_code_rec: ");
  print_code_rec(s);
  printf("\n");

  return 0;
}


void print_code(char *s)
{
  for (;*s;s++)
    putchar( *s == 'Z' ? 'A' : *s + 1);
}

void print_code_rec(char *s)
{
  if (*s) {
    putchar(*s =='Z' ? 'A' : *s + 1);
    print_code_rec(s+1);
  }
}

Notera de luriga skrivsätten i for-loopen och putchar-satsen. Det är en god övning att försöka förstå hur dessa fungerar.

7. Matrismultiplikation

A är deklarerad som en vektor eftersom vi inte på förhand vet hur många rader matrisen innehåller (det bestäms av värdet på n). Se sid 279 i kursboken. Om vi vill komma åt elementet på rad i, kolumn j får vi lov att skriva A[i*n + j]. Det fungerar eftersom elementen ligger efter varandra i minnet.

c är ett argument eftersom funktioner inte kan returnera verktorer. När vi anropar en funktion med en vektor är det egentligen en pekare som skickas. Det betyder att de ändringar vi gör i c kommer att påverka variabeln i den anropande funktionen (call-by-reference). Paramtern c kan därför användas för att returnera resultatet.

g3_7.c


#include <stdio.h>

void matrix_vector(int n, double A[], double b[], double c[]);

int main(void)
{
  double A[3][3] = { {1, 0, 1}, {2, 2, 2}, {0, 0, 1}};
  double b[3] = {1, 1, 1}, c[3];

  matrix_vector(3, &A[0][0], b, c);

  printf("c = {%lf, %lf, %lf}\n", c[0], c[1], c[2]);
  return 0;
}

void matrix_vector(int n, double A[], double b[], double c[])
{
  int i, k, sum;

  for (i=0; i<n; i++) {
    sum = 0;
    for (k=0; k<n; k++)
      sum += A[i*n + k] * b[k];
    c[i] = sum;
  }
}

8. Pekarträsk II

Jag använder den algebraiska metoden, som på förra uppgiften.

*p1 <=> *&i[0] <=> i[0]
*p2 <=> *&i[2] <=> i[2]
p1[3] <=> *(p1 + 3) <=> *(&i[0] + 3) <=> *(i + 3) <=> i[3]
p2[-1] <=> *(p2 - 1) <=> *(&i[2] - 1) <=> *(&*(i+2) -1) <=> *(i + 1) <=> i[1]
*(p1 + 1) <=> *(&i[0] + 1) <=> *(&*(i+0) + 1) <=> *(i + 1) <=> i[1]

Raden p2[-1] = 13 sätter alltså i[1] till 13. Det är detta värde som sedan skrivs ut när vi skriver ut *(p1+1).

9. Söka bland lösenord

g3_9.c


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

void read_fields(char *fields[7]);

int main(void)
{
  char *f[7];
  char *head[7] = {"Namn:", "Användarnamn:", "Lösenord (kodat):",
           "Användar-ID:", "Grupp-ID:", "Hemkatalog:",
           "Skalprogram:"};
  int i;

  read_fields(f);

  /* %-20s är en kod för att få strängen litet snyggare utskriven.
     Läs i kapitel 11 om sådana koder. */
  for (i=0; i<7; i++)
    printf("%-20s %s\n", head[i], f[i]);

  /* Jag slarvar litet och gör inte free här.*/
  return 0;
}

void read_fields(char *fields[7])
{
  int i, c;

  for (i=0; i<7; i++) {
    /* Vi använder en buffert för att lagra undan den sträng vi läser.
       När vi har läst in hela bufferten allokerar vi en lagom stor pekare
       och kopierar bufferten dit. */
    char buffer[200], n=0;

    while (1) {
      c = getchar();
      if (c == ':' || c=='\n' || c==EOF)
    break;
      buffer[n] = c;
      n++;
    }
    buffer[n] = 0;

    fields[i] = malloc((n+1)*sizeof(char));
    assert(fields[i]);
    strcpy(fields[i], buffer);
  }
}

Testkör programmet genom att skriva.

ypcat passwd | grep niklasf | a.out

Prova att byta ut niklasf mot ditt eget användarnamn.


© Niklas Frykholm, februari 1999