"generell pekartyp" i funktion?
Re: "generell pekartyp" i funktion?
njaee... det finns ju operanden ++ där också som jobbar direkt på pekaren. Det funkar ju inte om pekaren inte har bestämd typ.
Re: "generell pekartyp" i funktion?
Du får göra casten i en parentes:
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
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)++);
}
Det är inkrementeringen ++ som är problemet med sådan castning, precis som Jesse skrev...
/johan
Re: "generell pekartyp" i funktion?
Att ha två rader kod spelar väl ingen roll. Det kommer ju ändå resultera i samma maskinkod i slutet.
Re: "generell pekartyp" i funktion?
qsort() är väl ett bra exempel på hur man brukar göra.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.
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.
Re: "generell pekartyp" i funktion?
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. 

Re: "generell pekartyp" i funktion?
void qsort(void *base, size_t n, size_t size, int (*compar) (const void *, const void *) );SvenW skrev:qsort() är väl ett bra exempel
Re: "generell pekartyp" i funktion?
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
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]);
}
Re: "generell pekartyp" i funktion?
Så du har gjort 50 beställningar från classe?
Den här konstruktionen hade jag aldrig sett förr, och den var ju väldigt smidig, om jag har fattat rätt:
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:

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 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:
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Re: "generell pekartyp" i funktion?
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);
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);
Re: "generell pekartyp" i funktion?
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)
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;
}
Re: "generell pekartyp" i funktion?
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.
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.
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.
Inga säkerhetsbälten, full frihet, risk för fel, och hur komplicerade och underbara lösningar som helst.
Kan det bli bättre.

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.

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.
Re: "generell pekartyp" i funktion?
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.
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.
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.

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.
Re: "generell pekartyp" i funktion?
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.
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.
Re: "generell pekartyp" i funktion?
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) ...
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) ...
Re: "generell pekartyp" i funktion?
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.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: