"generell pekartyp" i funktion?

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: "generell pekartyp" i funktion?

Inlägg av jesse »

njaee... det finns ju operanden ++ där också som jobbar direkt på pekaren. Det funkar ju inte om pekaren inte har bestämd typ.
johano
Inlägg: 1943
Blev medlem: 22 januari 2008, 10:07:45
Ort: Stockholm

Re: "generell pekartyp" i funktion?

Inlägg av johano »

Du får göra casten i en parentes:

Kod: Markera allt

void ee_read(void* SRAM_pekare, void* EEPROM_pekare, uint8_t antal_bytes) 
{
   for(uint8_t i=0; i < antal_bytes; i++) 
      *((uint8_t*)SRAM_pekare)++ = eeprom_read_byte(((uint8_t*)EEPROM_pekare)++);
}
Edit: skulle ha provat det först, det funkade inte att göra så, du får 'lvalue required as increment operand'.
Det är inkrementeringen ++ som är problemet med sådan castning, precis som Jesse skrev...

/johan
monstrum
Inlägg: 620
Blev medlem: 13 januari 2005, 05:38:32
Ort: Göteborg

Re: "generell pekartyp" i funktion?

Inlägg av monstrum »

Att ha två rader kod spelar väl ingen roll. Det kommer ju ändå resultera i samma maskinkod i slutet.
SvenW
Inlägg: 1155
Blev medlem: 24 april 2007, 16:23:10
Ort: Göteborg

Re: "generell pekartyp" i funktion?

Inlägg av SvenW »

Jag vet inte om det bara är olika tyckande här, eller om det faktiskt finns någon praxis?

Just det där med hur man *brukar* eller *bör* göra inom programmering tycker jag är något man skriver väldigt lite om i läroböckerna. Böckerna koncentrerar sig till 99% på syntax och funktion. Mindre på hur det ska struktureras eller vilka sätt som är "lämpligast" ur läsbarhetssynpunkt.
qsort() är väl ett bra exempel på hur man brukar göra.
Vad som är "lämpligast" finns säkert många olika meningar om.
De flesta verkar vara överens om att C är ett rätt effektivt språk, inte minst för att skjuta sig själv i foten.
blueint
Inlägg: 23238
Blev medlem: 4 juli 2006, 19:26:11
Kontakt:

Re: "generell pekartyp" i funktion?

Inlägg av blueint »

Lite intressant att högskolor och universitet är väldigt inne på att använda hårt typade programspråk. Men i matematik mm så är det knappast typat på något sätt. Ändå förutsätts man lära det på samma förutsättningar. :vissla:
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: "generell pekartyp" i funktion?

Inlägg av jesse »

SvenW skrev:qsort() är väl ett bra exempel
void qsort(void *base, size_t n, size_t size, int (*compar) (const void *, const void *) );
SvenW
Inlägg: 1155
Blev medlem: 24 april 2007, 16:23:10
Ort: Göteborg

Re: "generell pekartyp" i funktion?

Inlägg av SvenW »

Ett litet exempel på hur man kan använda qsort!
Kompilera gärna och kör!

Programmet hanterar beställnigar av elektronikkomponenter och sorterar i storleksordning så att de viktigaste beställningarna kommer först.
Bara triviala kompletteringar som bankgironummer och sånt återstår :)

Kod: Markera allt

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

int
compare (const void *a, const void *b)
{
  return *((int *) b) - *((int *) a);
}

void
skriv_utbetalningsblanketter (int a, char *s)
{
  printf ("Autogirera %i till %s \n", a, s);
}

int
main ()
{
  int i;
  int utbetalningar[4] = { 5, 10, 50, 15};
  char *leverantorer[4] = { "elfa", "mig", "classe", "swech" };

  /* Exkludera förtsta elementet från jämförelse */
  qsort ((void *) &utbetalningar[1], 4, 1 , compare);

  for (i = 0; i < 4; i++)
    skriv_utbetalningsblanketter (utbetalningar[i], leverantorer[i]);
}
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: "generell pekartyp" i funktion?

Inlägg av jesse »

Så du har gjort 50 beställningar från classe? :lol:

Den här konstruktionen hade jag aldrig sett förr, och den var ju väldigt smidig, om jag har fattat rätt:

Kod: Markera allt

char *leverantorer[4] = { "elfa", "mig", "classe", "swech" };
Du skapar alltså en lista med pekare som pekar på texterna. Så att texten packas fint i minnet även om orden är olika långa. Dock tjänar du nog inget minne i det här fallet då varje pekare tar upp ett antal bytes också (är det 2 eller 4 bytes? I 8-bitars AVR är det väl 2 bytes).

Du sorterar ju "utbetalningar", men leverantören sorteras aldrig, så de kommer väl alltid att skrivas ut i samma ordning? För att även "leverantorer" (och all annan data tillhörande posten) ska kunna sorteras måste väl man ha dessa i samma post?

EDIT: testade att köra, och jag vet inte vad den har gjort:
qsort.png
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Användarvisningsbild
kimmen
Inlägg: 2042
Blev medlem: 25 augusti 2007, 16:53:51
Ort: Stockholm (Kista)

Re: "generell pekartyp" i funktion?

Inlägg av kimmen »

Tjaa.

Som skrivet skall den skyffla om de fyra första byten med start från andra elementet av utbetalningar (utbetalningar[1]), baserat på värdet av en int som börjar på dessa fyra adresser.

Från början är arrayen hexadecimalt (antaget att det är en little-endianmaskin och 32-bitars intar)

05 00 00 00 | 0A 00 00 00 | 32 00 00 00 | 0F 00 00 00

och så sorterar den om de rödmarkerade byten, men den jämför hela intar. Talen som jämförs är

0A 00 00 00 (för första röda byten)
00 00 00 32 (för andra röda byten)
00 00 32 00 (för tredje röda byten)
00 32 00 00 (för fjärde röda byten)

och eftersom 0A 00 00 00 är det lägsta talet hamar det elementet sist och arrayen blir

05 00 00 00 | 00 00 00 0A | 32 00 00 00 | 0F 00 00 00

som är vad du får ut! :)

Korrekt anrop för att sortera talen är
qsort ((void *) utbetalningar, 4, sizeof(int) , compare);
SvenW
Inlägg: 1155
Blev medlem: 24 april 2007, 16:23:10
Ort: Göteborg

Re: "generell pekartyp" i funktion?

Inlägg av SvenW »

kimmen är smart!

Personligen tycker jag inte att det är så farligt att 'skjuta sig i foten.'
Men jag sysslar inte med ekonomi!

Med lite vana vid analysverktyg (gdb, gprof, valgrind...) och lite tid att testa
sina programdelar så hittar man rätt kvickt problemen.
Dock kan det i sällsynta fall bli 'djupa buggar'.
Alltid bra med en god kodningsstil. Och porterbarhet är svårt!

Här ett lite förbättrat exempel:
(som är lite överkommenterat bara)

Kod: Markera allt

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

/* Typedeffar kan man lägga i headerfiler om programmet är stort */
typedef struct
{
  int kronor;
  char *leverantor;
} UtBetalningar;


int
compare (const void *a, const void *b)
{
  /* Tar obetydligt längre tid så här , om ens något! */
  return ((UtBetalningar *) b)->kronor - ((UtBetalningar *) a)->kronor;
}


void
skriv_utbetalningsblanketter (UtBetalningar * a)
{
  printf ("Skriv manuellt ut %i till %s \n", a->kronor, a->leverantor);
}


int
main ()
{
  int i;
  /* const belastar vanligen inte binären, ofta bättre än #define */
  const int start = 1;
  
  /* Här kan det bli problem med const int, eftersom talet styr arraystorleken */
  #define N_UTBETALNINGAR  4
  
  /* Typdeffar är bra. Knyter samman saker och möjliggör typkontroll */
  UtBetalningar utbetalningar[N_UTBETALNINGAR ] = {
    {
     5, "elfa"},
    {
     10, "mig"},
    {
     50, "classe"},
    {
     15, "swech"}
  };
  
  /* 
   * Exkludera första elementet från jämförelse
   * Extremt viktigt med rätt parametrar när kompilatorn inte kan göra typkontroll.
   * Datorn är säkrare än en själv!
   */
  
  qsort (&utbetalningar[start],  N_UTBETALNINGAR  - start , sizeof (utbetalningar[0]),
         compare);
  
  for (i = 0; i <  N_UTBETALNINGAR; i++)
    skriv_utbetalningsblanketter (&utbetalningar[i]);  /* Pekare tar mindre plats i stackframe */

  return 0;
}
Användarvisningsbild
bit96
Inlägg: 2526
Blev medlem: 3 september 2007, 10:04:29
Ort: Säffle

Re: "generell pekartyp" i funktion?

Inlägg av bit96 »

Många inlägg här ovan visar just på hur mycket man kan göra med C.
Inga säkerhetsbälten, full frihet, risk för fel, och hur komplicerade och underbara lösningar som helst.
Kan det bli bättre. :D

Men för att återgå till frågan. Så här resonerar i alla fall jag:
Om man skriver en egen funktion som hanterar läs/skriv mellan sram och eeprom måste man bestämma sig exakt för vad den skall göra.
Här verkar det som TS har valt att skriva byte för byte.
Varför man valt byte spelar programmeringsmässigt ingen roll, det handlar egentligen om fysiska/elektriska/konstruktionsmässiga krav.
Och nu är det byte som gäller i detta fall.

Funktionen skall alltså använda två pekare till fält med bytes, och ett värde som anger antalet bytes.
Funktionerna bör dessutom döpas om till nåt i stil med ee_save_bytes() eller liknande om det inte är helt självklart att det är bytes som gäller.

På så sätt har man skapat en funktion som gör EN sak och som förhoppningsvis gör det bra. :tumupp:

I sitt program kan man sen anropa ee_save_bytes() för att spara bytes, integer, float, fält med pekare till funktioner eller vad som helst men då gör man en typecast.

Om man sen har återkommande behov av t.ex. att spara en massa float kan man skriva en ny funktion som heter ee_save_float().
Den funktionen kan sedan i sin tur anropa ee_save_bytes() om det är lämpligt.

På så sätt håller man koden enkel, ren och lättläst. Dessutom minskar risken för buggar.

Med andra ord så har jesse alltså gjort rätt från början.
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: "generell pekartyp" i funktion?

Inlägg av jesse »

I verkligheten använder jag min funktion ee_save för att spara lite varierande data.. Det är sällan samma typ. Det kan vara olika strukturer eller arrayer med olika typer. Så jag tyckte det var smidigt att göra en gemensam spara-rutin för dessa. Att jag kör byte-för byte är ju helt enkelt av två orsaker: (a) 8-bitars AVR, och (b) det kan i praktiken vara ett ojämnt antal bytes som ska sparas - då är det bara bytes som gäller.

EE_save_bytes är ju ett namn som visserligen berättar mer exakt vad den gör... ee_save säger inte så värst mycket faktiskt, så där har du en poäng. Fast att jag inte ska använda void* är jag fortfarande tveksam till.... save_bytes säger ju vad den gör, och att argumentet "antal" syftar på antalet bytes och inget annat. Å andra sidan är ju inte funktionen begränsad att ta data av just typen char, unsigned char, int8_t eller uint8_t.... Vad datan har för format spelar faktiskt ingen roll - den går utmärkt att spara med min funktion ändå! Och just därför borde void* vara mer korrekt, för då vet jag att jag faktiskt kan spara vad som helst och att det ändå blir rätt. Med enbart typen char* som argument så vet jag ju faktiskt inte om funktionen är beroende av att data som skickas måste ha exakt det formatet eller om det kanske kan gå bra även med andra format.... (om jag inte detaljstuderar koden inne i funktionen) och eftersom funktionen främst är avsedd att hantera varierande format bör ändå void* vara mest rätt. :D

Bara jag definierar noga att argumentet "antal" syftar på antalet bytes och inget annat, och detta får man fram med hjälp av sizeof(data) . Kanske "antal" i så fall är ett missvisande ord, och det skulle hellre heta storlek eller size för att man lättare ska förstå att det handlar om minnesurymmet, och inte antalet element i ett fält t.ex.
Användarvisningsbild
Icecap
Inlägg: 26610
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: "generell pekartyp" i funktion?

Inlägg av Icecap »

Jag gör på samma sätt, definierar en struct med alla inställningar som ska kunde ändras.

Sedan har jag ett par enkla rutiner som skriver o läser en buffer till/från EEPROM.
#define Save_Config() (Write_EE_Buffer(CONFIG_LOCATION,(BYTE*)&Config,sizeof(Config)))
#define Load_Config() (Read_EE_Buffer(CONFIG_LOCATION,(BYTE*)&Config,sizeof(Config)))
(CONFIG_LOCATION är adressen som sparandet börjar på i EEPROM)

Sedan är det bara att skriva:
Save_Config();
för att spara alla inställningar från minnet till EEPROM och såklart
Load_Config();
för att hämta dom från EEPROM till minnet.

Write_EE_Buffer(unsigned int Address, unsigned char * From_Buffer, unsigned int Number_Of_Bytes) och
Read_EE_Buffer(unsigned int Address, unsigned char * To_Buffer, unsigned int Number_Of_Bytes)
är båda rutiner jag har skrivit i min fil där jag inkluderar all EEPROM-hantering.

Jag tycker att det är intiutivt, klart anger funktionen och ger bra läsbarhet.
SvenW
Inlägg: 1155
Blev medlem: 24 april 2007, 16:23:10
Ort: Göteborg

Re: "generell pekartyp" i funktion?

Inlägg av SvenW »

Vi är nog ganska överens.
Och det är väldigt bra att editorn konfigurerad med TAGS-filer så
att man blixtsnabbt kan se hur structer, makron och typdefinitioner ser ut!
Inte minst de som finns i c- och h-filer som man inte själv har skrivit.

---

Hittade för övrigt en pdf; Handbook of C Programming Style:
http://www.jfm3.org/c_handbook.pdf
Se speciellt 6 – Choose Tools Wisely

---

Sedan funderar jag på från mitt exempel ovan

skall man skriva
UtBetalningar utbetalningar[N_UTBETALNINGAR ] = ...
eller såhär
UtBetalning utbetalning[N_UTBETALNINGAR ] = ...
eller såhär
UtBetalning utbetalningar[N_UTBETALNINGAR ] = ...

respektive
sizeof (utbetalningar[0]) ...
eller
sizeof (UtBetalning) ...
Nerre
Inlägg: 27143
Blev medlem: 19 maj 2008, 07:51:04
Ort: Upplands väsby

Re: "generell pekartyp" i funktion?

Inlägg av Nerre »

blueint skrev:Men i matematik mm så är det knappast typat på något sätt. Ändå förutsätts man lära det på samma förutsättningar. :vissla:
Visst är matematik hårt typat? Fast där är det ju upp till den som använder det att använda rätt "typer", finns inga deklarationer så en kompilator kan hjälpa till att varna när man klantar sig.
Skriv svar