Pekere i C

Et kompakt eksempel som viser bruk av pekere i C:


#include <stdio.h>

int main() {

   // Variabler for dette kodeeksemplet
   int tall = 10; // int: størrelse lik 4 bytes på PC
   int tabell[3] = {11, 12, 13};

   // --------------------------------------------------------------------
   // EKSEMPLER PÅ BRUK AV PEKERE:
   // --------------------------------------------------------------------

   // DEKLARERING:
   int *peker1;
   int *peker2, *peker3; // to samtidig

   // INITIALISERING:
   peker1 = &tall; // peker til variabelen 'tall' (med verdi 10)
   peker2 = tabell; // peker til første elementet i 'tabell'
   peker2 = &tabell[0]; // samme resultat som over (alternativ måte)
   peker3 = &tabell[1]; // peker til andre elementet i 'tabell'
   peker3 = tabell+1; // samme resultat som over (alternativ måte)
   peker3 = tabell+2; // peker til tredje og siste elementet i 'tabell'

   // DEKLARERING OG INITIALISERING SAMTIDIG:
   int *peker4 = &tall; // peker til variabelen 'tall' (med verdi 10)
   int *peker5 = tabell; // peker til første elementet i 'tabell'

   // INKREMENTERING OG DEKREMENTERING:
   peker5++; // peker til andre elementet i 'tabell'
   peker5--; // peker til første elementet i 'tabell' igjen
   peker5 += 1; // samme resultat som øverst (alternativ måte)
   peker5 -= 1; // samme resultat som nestøverst (alternativ måte)

   // DIFFERANSE:
   int differanse =
   peker3 - peker5; // 2, fordi peker3 = siste element og peker5 = første element
   // printf("%d\n", differanse); // kontroll

   // SAMMENLIGNING:
   tabell == peker2; // 1 (sann), begge inneholder adressen til første element
   tabell == peker5; // 1 (sann), begge inneholder adressen til første element
   peker3 == peker5; // 0 (usann), adressen til element 3 er ikke lik element 1
   tabell == &tabell[0]; // 1 (sann), begge har samme adresse
   &peker1 == &tall; // 0 (usann), pekerens adresse er ikke lik adressen til 'tall' 
   // printf("%d %d %d %d %d\n", tabell == peker2, tabell == peker5, peker3 == peker5, tabell == &tabell[0], &peker1 == &tall); // kontroll

   // INDIREKSJON (AVLESING):
   *peker1; // 10
   *peker2; // 11
   *peker3; // 13
   *peker4; // 10
   *peker5; // 11
   *(tabell+1); // 12
   *(tabell+2); // 13
   // printf("%d %d %d %d %d %d %d\n", *peker1, *peker2, *peker3, *peker4,  *peker5, *(tabell+1), *(tabell+2)); // kontroll

   return 0;
}

TBC

Spesialtegn i tekst for C

C har en rekke spesialtegn man kan benytte seg av, når man skal skrive (og formatere) tekst som skal vises i terminalvinduet:

c-escape-sequences

Av alle disse er nok \n mest brukt, siden denne gir linjeskift. Jeg kan knapt huske å ha benyttet de andre noe særlig. Eksempel: printf("Dette er en test!\n");

Benytter man \a vil dette antagelig bare irritere brukeren, hvis den i det hele tatt er støttet på maskinen til vedkommende.

Operatørprioritering

Jeg begynte nylig å repetere og forfriske kunnskapen min i C. Da kom jeg over følgende tabell som viser operatørprioritet («operator precedence» på engelsk):

c-operators-precedence

Dette er den type kunnskap jeg aldri brydde meg noe særlig om. Fordi jeg alltid brukte mye paranteser og jeg pleide å bryte opp komplekse logikkuttrykk. Men, man bør vite litt om hvordan ulike operatører prioriteres likevel ..

AWS Lambda

Etter å ha knotet med AWS Lambda i over en måned har jeg lært mye nyttig som kanskje kan hjelpe andre som skal bruke denne superkjekke tjenesten.

(Det er lite kodeeksempler i AWS’ API-dokumentasjon så hver gang man skal ta i bruk ny funksjonalitet blir det gjerne noe prøving og feiling kombinert med googling.)

Første problemet man som regel opplever er at ens egen Lambda-funksjon avslutter før kall til andre AWS-tjenester returnerer et resultat (eller error). Dette er simpelthen fordi kallet er asynkront og kanskje trenger flere hundre millisekunder før det fullfører.

For å vente på asynkrone kall har man heldigvis flere muligheter.

Promise

En mye brukt, men fortsatt ganske ny løsning, er promise:

const AWS = require('aws-sdk');

exports.handler = async () => {

   var dynamodb = new AWS.DynamoDB(...);
   await dynamodb.putItem(...).promise().then(function() {
      // Kode for suksess her
   }).catch(function(error) {
      // Kode for feil her
   });

   // Synkron kode fortsetter her
};

Her er det await og promise som styrer showet. Og på promise lenkes then også catch. Etterpå kan man fortsette med annen synkron kode. Dette er en veldig ryddig måte å gjøre det på. Dessverre er det ikke alle AWS API-er som har promise.

Om det er flere asynkrone kall som skal gjennomføres bør man fjerne await over og vente på kallene samtidig med Promise.all:

...
var dynamodb = new AWS.DynamoDB(...);
var promise1 = dynamodb.putItem(...).promise();
var promise2 = <annet asynkront API-kall her>.promise();
// o.s.v.
Promise.all([promise1, promise2, ...])

Også henger man på then og catch her i stedet for å ha det på hver promise over. Men dette er selvsagt bare mulig så lenge kallene kan kjøres uavhengig av hverandre.

Hendelser

Man har også anledning til å bruke hendelser:

const AWS = require('aws-sdk');

exports.handler = () => {

   var dynamodb = new AWS.DynamoDB(...);
   var kall = dynamodb.putItem(...);
   kall.on('success', function(respons) {
      // Kode for suksess her
   });
   kall.on('error', function(error) {
      // Kode for feil her
   });
   kall.send();

   // Synkron kode
   // fortsetter her
};

Tilbakekall

Alternativt har man bruk av tilbakekall (altså «callback» på engelsk), men hvor man dessverre havner i "callback helvete" hvis man skal gjøre mange kall:

const AWS = require('aws-sdk');

exports.handler = () => {

   var dynamodb = new AWS.DynamoDB(...);
   dynamodb.putItem(..., function(error, data) {
      if (error) {
         // Kode for feil her
      } else {
         // Kode for suksess her
      }
   });
};

Problemet med alle eksemplene over er at det blir unødig venting. Og siden man betaler for hver påbynte bolk med 100 millisekunder er det viktig å begrense ventingen så mye som mulig.

Eksemplet nedenfor viser den raskeste måten jeg har funnet til nå:

const AWS = require('aws-sdk');

exports.handler = (hendelse, kontekst, tilbakekall) => {
   kontekst.callbackWaitsForEmptyEventLoop = false;

   var dynamodb = new AWS.DynamoDB(...);
   dynamodb.putItem(..., function(error) {
      if (error) {
         // Kode for feil her
      } else {
         // Kode for suksess her
      }

      tilbakekall(null, ...);
   });
};

Gjentatte tester viste at tidsforbruket minst ble halvert.

Å eksperimentere med AWS Lambda er noe man aldri blir ferdig med ..

TBC

Hallo verden i Assembly

Et enkelt testprogram i assembly for å bli våt på beina:

; alt bak et semikolon er kommentar

section .data
   hallo: db "Hallo, verden!",10
   ; db (som står for define bytes) benyttes for å opprette tekststrengen 'Hallo, verden!'
   ; etterpå følger tallet 10 for å få ett linjeskift
    
section .text
   global start

start:
   mov eax,4     ; signalkode for å skrive noe
   mov ebx,1     ; signalkode for standardutskrift (dvs. terminal)
   mov ecx,hallo ; man angir de ønskede dataene
   mov edx,15    ; man velger hvor mye av dataene som skal tas med
   int 80h       ; man forstyrrer systemet / ber om oppmerksomhet, for å få kjørt instruksjonene over

   mov eax,1     ; signalkode for å avslutte
   mov ebx,0     ; signalkode som sier at alt gikk greit
   int 80h       ; nytt kall til systemet for å kjøre nye instruksjoner (de to nye over)

Utskriften i terminalen blir som følger:

Hallo, verden!
   

Dette programmet kan f.eks. kjøres på rextester.com. Der benyttes NASM-assembleren så da slipper man å laste ned selv, ordne innholdet i fil, o.s.v.

Instruksjoner

Mange sier assemblyspråk er vanskelig, men hver type assemblyinstruksjon er faktisk veldig kortfattet og forståelig.

FASM

Nedenfor følger instruksjoner for FASM – en mye brukt assembler. Det finnes selvsagt alternative assemblere, da vil instruksjonene være litt annerledes.

mov

For å flytte (dvs. kopiere) data brukes instruksjonen mov:

mov destinasjon,kilde

Noen eksempler:

mov eax,8CBh
mov eax,edx
mov si,cx

Øverst er det verdien 8CBh som flyttes. Dette er en heksadesimalverdi da det er hengt på en h til slutt. For flytting av data mellom to forskjellige registre må begge registrene være av samme størrelse.

add

Om man skal legge sammen to verdier brukes add:

add destinasjon,kilde

Noen eksempler:

add eax,11b
add eax,edx
add si,cx

I øverste eksempel legges verdien 11b til verdien som allerede finnes i eax. Fordi det henger en b til slutt er dette et binærtall. Hvis man legger sammen registre må begge registrene være av samme størrelse, sånn som greia også er ved flytting.

sub

Motsetningen til add blir sub, for å trekke fra:

sub destinasjon,kilde

Noen eksempler:

sub eax,4h
sub cl,dl

Øverst trekkes 4h fra verdien som allerede finnes i eax. Størrelseskravet gjelder også her ved bruk av to forskjellige registre.

inc

Som et mulig alternativ til add har man inc:

inc destinasjon

Noen eksempler:

inc eax
inc cl
inc dl

Her inkrementeres verdien som allerede finnes i registeret, dvs. at den økes med 1.

dec

Motsetningen til inc blir dec:

dec destinasjon

Noen eksempler:

dec eax
dec cl
dec dl

Verdien som allerede finnes i registeret blir dekrementert, dvs. redusert med 1.

mul

Skal man multiplisere sammen to verdier bruker man mul og oppgir kun èn kilde:

mul kilde

Resten er allerede bestemt:

  • Kilde nr. 2 skal være al (8-bit), ax (16-bit) eller eax (32-bit).
  • Valgt kilde nr. 2 må være like stor som kilde nr. 1!
  • Destinasjonens størrelse blir dobbelt så stor som kildene. Så 8-bits kilder gir destinasjon som er 16-bit, o.s.v.
  • Mulige destinasjoner er
    • ah kombinert med al som sammen gir 16-bit.
    • dx kombinert med ax som sammen gir 32-bit.
    • edx kombinert med eax som sammen gir 64-bit.

Før mul kjøres må selvsagt begge kilder gis verdi – se komplett eksempel:

mov si,2h           ; verdi satt for første kilde
mov ax,11b          ; verdi satt for andre kilde
mul si              ; resultatet skal tilsvare 6 i desimaltallsystemet

OBS: mul foretar usignert multiplikasjon, for signert må imul brukes i stedet.

div

Som med multiplikasjon oppgir man kun èn kilde for divisjon (div):

div kilde        ; dette er nevner, altså den verdien det skal deles på

Resten er bestemt og ganske likt som for mul:

  • Den andre kilden (dvs. teller) blir en av
    • ah kombinert med al som sammen gir 16-bit.
    • dx kombinert med ax som sammen gir 32-bit.
    • edx kombinert med eax som sammen gir 64-bit.
  • Delregistrene over benyttes også for å lagre resulterende kvotient og rest
    • Rest lagres øverst, i ah (8-bit), dx (16-bit) eller edx (32-bit).
    • Kvotient lagres nederst, i al (8-bit), ax (16-bit) eller eax (32-bit).

Før div kjøres må selvsagt begge kilder ha verdi – se eksempel:

mov si,2h           ; verdi satt for nevner
mov eax,110b        ; verdi satt for teller
div si              ; resultatet skal tilsvare 3 i desimaltallsystemet

OBS: Som med mul jobber også div usignert, man har da idiv hvis man ønsker signert.

TBC

Registre

Enhver prosessor har registre for oppbevaring av forskjellige viktige data ved kjøring av instruksjoner i et program.

På Intel-plattformen er det mye tilbakekompatibilitet, som blant annet viser seg i hvordan prosessorens mange registre som regel er designet:

Illustrasjonen viser et typisk eksempel, nemlig registeret rax. Dette er en utvidelse av eax. Videre er eax en utvidelse av ax. Og ax består av ah ("a high") og al ("a low").

Med et slikt design kan man faktisk hente verdiene i eax, ax, ah og al bare ved å avlese rax. Bokstaven a står for akkumulator (dvs. «accumulator» på engelsk). Registeret brukes gjerne for aritmetikk og logikk.

Noen typer

I hovedsak finnes det tre typer registre og noen typer har igjen egne undergrupper.

Generelle

For generelle registre er det tre grupper av registre siden formålene er forskjellige.

Dataregistre

Det er ganske opplagt hva et dataregister brukes til, det er data.

Og det finnes fire stk – hvor disse utvider eksisterende mindre registre:

  • RAX (64-bit) <== EAX (32-bit) <== Accumulator (AX) (16-bit) <== AH og AL (8-bit).
  • RBX (64-bit) <== EBX (32-bit) <== Base (BX) (16-bit) <== BH og BL (8-bit).
  • RCX (64-bit) <== ECX (32-bit) <== Counter (CX) (16-bit) <== CH og CL (8-bit).
  • RDX (64-bit) <== EDX (32-bit) <== Data (DX) (16-bit) <== DH og DL (8-bit).

Pekerregister

Et pekerregister inneholder adressen til det som det skal pekes til. Man har f.eks. peker som viser til neste instruksjon som skal kjøres.

To pekerregistre:

  • ESP (32-bit) som utvider Stack Pointer (SP) (16-bit).
  • EBP (32-bit) som utvider Base Pointer (BP) (16-bit).

Indeksregister

Man bruker gjerne et indeksregister slik man bruker vanlige tellere i et hvilket som helst programmeringsspråk, når man skal forholde seg til tabeller eller vektorer.

To indeksregistre:

  • ESI (32-bit) som utvider Source Index (SI) (16-bit)
  • EDI (32-bit) som utvider Destination Index (DI) (16-bit).

Kontroll

Registre for kontroll handler om hva som har skjedd og hva som skal skje:

  • Flaggregister (32-bit) som inneholder flagg (dvs. 0 eller 1), her er noen:
    • Overflow Flag (OF)
    • Direction Flag (DF)
    • Interrupt Flag (IF)
    • Trap Flag (TF)
    • Sign Flag (SF)
    • Zero Flag (ZF)
    • Auxiliary Carry Flag (AF)
    • Parity Flag (PF)
    • Carry Flag (CF)
  • Instruction Pointer (IP) (32-bit), som peker til neste instruksjon.

Segment

Det finnes flere segmentregistre. De viktigste er for stack, kode og data.

TBC

Webserver i Node.js

Her følger en enkel webserver i Node.js som kan tilpasses slik man vil:

var html = "";
  html += "<!doctype html>\n";
  html += "<html>\n";
  html += "<head>\n";
  html += "<title>TITTEL</title>\n";
  html += "<meta charset='utf-8'>\n";
  html += "</head>\n";
  html += "<h1>TITTEL</h1>\n";
  html += "<p>INNHOLD</p>\n";
  html += "</html>\n";

var http = require("http");

var server = http.createServer(function(forespørsel, respons) {

  // Mal
  var side = html;

  // Behandling av forespørsel
  if (forespørsel.url === "/") {
    side = side.split("TITTEL").join("Front");
    side = side.split("INNHOLD").join("Hei! Dette er frontsiden.");

  } else if (forespørsel.url === "/kontakt") {
    side = side.split("TITTEL").join("Kontakt");
    side = side.split("INNHOLD").join("E-post: post@ovebakken.no");

  } else {
    side = side.split("TITTEL").join("Feil");
    side = side.split("INNHOLD").join("Forespurt side eksisterer ikke.");
  }

  // Send innhold
  respons.writeHead(200, {"Content-Type": "text/html"});
  respons.write(side);
  respons.end();
});
server.listen(80);

HTML-koden kan endres til hva som helst, og det er enkelt å legge til eller fjerne sider.

Portnummer her er 80, men kan også endres så lenge man velger en port som er ledig.

TBC

Funksjoner i Vue.js

Alle apps har datamedlemmer som brukes i funksjoner for å lage nytt innhold.

methods

Her følger et eksempel hvor to tall skal legges sammen:

Tall 1: <input type="text" v-model:value="tall1">
Tall 2: <input type="text" v-model:value="tall2">
Svar: <input v-bind:value="svar()">

Det er to felt for å taste inn tall, og et felt for å vise svaret.

Funksjonen svar tar seg av arbeidet:

var app = new Vue({
  el: "#app",
  data: {
    tall1: 0,
    tall2: 0
  },
  methods: {
    svar: function() {
      return parseFloat(this.tall1) + parseFloat(this.tall2);
    }
  }
});

computed

Alternativt kan man gjøre som følger:

var app = new Vue({
  el: "#app",
  data: {
    tall1: 0,
    tall2: 0
  },
  methods: {
  },
  computed: {
    svar: function() {
      return parseFloat(this.tall1) + parseFloat(this.tall2);
    }
  }
});

Her er svar flyttet til computed i stedet for, dette muliggjør følgende:

Tall 1: <input type="text" v-model:value="tall1">
Tall 2: <input type="text" v-model:value="tall2">
Svar: <input v-bind:value="svar">

Her henvises det til svar som om dette er et datamedlem. Dette blir litt mer elegant.

TBC