[ANSI C] struct.member pointers

Pagina: 1
Acties:

  • decoy
  • Registratie: November 1999
  • Laatst online: 13-01-2018
Op een 8-bits controller heb ik in software een struct gedefinieerd met 24 members.
Ergens in de code wil ik telkens een serie members van plaats wisselen => herschikken.

Voor de uitleg ff een simpele struct:
C:
1
2
3
4
5
6
7
8
struct punt {
   unsigned char x;
   unsigned char y;
};

/* globalen */
struct punt puntje = { 10, 20 };
unsigned char tmp;


Nu wissel ik puntje.x en puntje.y met een macro:
C:
10
11
12
13
14
15
#define swap_macro(a,b,x)    ((x) = (a), (a) = (b), (b) = (x))  /* swap a and b */

void een_routine (void)
{
   swap_macro(puntje.x, puntje.y, tmp);
}



Het nadeel hiervan is dat het gewoon een inline functie is die ongewenst veel data en code geheugen verbruikt,... denk ik :?

Nu wil ik de swap in een functie uitvoeren.
zoiets als:
C:
10
11
12
13
14
15
16
17
18
19
20
21
void swap_functie (unsigned char *a, unsigned char *b)
{
   unsigned char tmp;
   tmp = *a;
   *a = *b;
   *b = tmp;
}

void een_routine (void)
{
   swap_functie (&puntje.x, &puntje.y);
}


En dat slikt de compiler niet: "Function argument incompatible with its declaration" op regel 20.

De struct members hebben een gedefinieerd geheugen adres, dus moet er ook naar te pointen zijn })
Helaas kan ik nergens iets vinden over het expleciet naar struct.members pointen, terwijl het bijvoorbeeld wel werrukt in een Borland compiler in een C omgeving ??!!!

Wie o wie denkt mee?
PS: Denk er wel aan dat het geheugen gelimiteerd is...

  • Gondor
  • Registratie: September 2003
  • Laatst online: 21-08 13:36
Zou dit helpen:
swap_functie ( &(puntje.x) , &(puntje.y) );

"Peace cannot be kept by force. It can only be achieved by understanding"-Albert Einstein-


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 21-08 12:02

.oisyn

Moderator Devschuur®

Demotivational Speaker

Waarom denk je dat die macro ongewenst veel data en code geheugen gebruikt :? Ik denk dat dat met je swap_functie () eerder het geval is, omdat daar 2 waarden op de stack gepushed moeten worden, en er moet een call uitgevoerd worden. En een slimme compiler maakt er in het eerste geval gewoon een xchg van, wat nog sneller is ook.

En bovendien werkt je swap_macro op elk type, je swap_functie werkt alleen op unsigned char's

waarom het niet compilet kan ik echter niet echt ontdekken. Is dat wel je volledige source?

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 18:33
Als ik jou was zou ik eerst eens in je map/list file kijken of t'ie inderdaad wel zoveel code en data geheugen in beslag neemt.

De macro zelf neemt volgens mij minder code in beslag ( en is sneller dan je functiecall ) , en zeker minder stackruimte (RAM).

Oops, toch sneller leren tikken :)

Verder lijkt het me geen al te snuggere compiler, dus zou ik functiecalls zoveel mogelijk beperken.

[ Voor 25% gewijzigd door farlane op 14-10-2003 18:18 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • decoy
  • Registratie: November 1999
  • Laatst online: 13-01-2018
@Gondor:
Nee, dat helpt niet.

@.oisyn/farlane:
Dat is dus de vraag, ben het wel met je eens dat push en pop acties plus aanroep routine redelijk veel code vereisen.

Ff denken... 2 * 2 bytes pushen, 4 bytes functie call, 2 * 2 bytes poppen... :z

Daar had ik zelf ook aan kunnen denken 8)7

Bedankt.

BTW: Farlane, beetje oftopic; waarom lijkt het je een niet al te snuggere compiler?

  • igmar
  • Registratie: April 2000
  • Laatst online: 29-06 18:56

igmar

ISO20022

decoy schreef op 14 October 2003 @ 17:33:
C:
1
2
3
4
5
6
7
8
struct punt {
   unsigned char x;
   unsigned char y;
};

/* globalen */
struct punt puntje = { 10, 20 };
unsigned char tmp;


Nu wissel ik puntje.x en puntje.y met een macro:
C:
10
11
12
13
14
15
#define swap_macro(a,b,x)    ((x) = (a), (a) = (b), (b) = (x))  /* swap a and b */

void een_routine (void)
{
   swap_macro(puntje.x, puntje.y, tmp);
}



Het nadeel hiervan is dat het gewoon een inline functie is die ongewenst veel data en code geheugen verbruikt,... denk ik :?
Macro's zijn altijd preprocessor werk, nooit die van de compiler. De pest met macro's is dat ze typeloos zijn, en de meest wazige foutmeldingen kunnen genereren.

Inline functies zijn een luxe, die moet je expliciet aangeven, en zijn slechts een hint. Macros worden zeker niet omgezet in inline functies.
C:
1
void swap_functie (unsigned char *a, unsigned char *b)
is

code:
1
2
3
4
void swap(struct punt *a, struct punt *b)
{
     ....
}


niet veel handiger ?

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 13-08 16:46

curry684

left part of the evil twins

igmar schreef op 14 October 2003 @ 20:39:
Inline functies zijn een luxe, die moet je expliciet aangeven, en zijn slechts een hint.
Inline functies zijn zelfs zo luxe dat ze niet in ANSI C zitten maar een feature van C++ zijn :)

Professionele website nodig?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 21-08 12:02

.oisyn

Moderator Devschuur®

Demotivational Speaker

Pssst, inline is ok in C99 ;)
igmar: Macros worden zeker niet omgezet in inline functies.
dat niet, maar het idee is hetzelfde: ze kunnen beide compileren naar inline code (bij macro's is dat per definitie zo, bij inline functies kan de compiler het bepalen)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • igmar
  • Registratie: April 2000
  • Laatst online: 29-06 18:56

igmar

ISO20022

curry684 schreef op 14 October 2003 @ 20:52:
Inline functies zijn zelfs zo luxe dat ze niet in ANSI C zitten maar een feature van C++ zijn :)
Kwa standaard klopt dat inderdaad, er zijn ook wat C compilers die inlines kennen. GNU cc is d'r een van. Ik gebruik ze overigs zelf nooit, en anders met iets als :

#ifdef HAVE_INLINE
#define __inline__ inline
#else
#define __Iinline
#endif

en dan de functies in deze vorm :

code:
1
2
3
int __inline__ x(void)
{
}

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 21-08 12:02

.oisyn

Moderator Devschuur®

Demotivational Speaker

Zoals ik al zei, inline is tegenwoordig dus wel ANSI C

[ Voor 19% gewijzigd door .oisyn op 14-10-2003 22:10 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • decoy
  • Registratie: November 1999
  • Laatst online: 13-01-2018
Igmar,
Zie topic beicht 1, 3 en 4.
Ik dacht ook dat het handiger zou zijn maar ben met twee anderen tot de conclusie gekomen dat het alleen maar meer geheugen kost.

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 18:33
decoy schreef op 14 oktober 2003 @ 20:17:
BTW: Farlane, beetje oftopic; waarom lijkt het je een niet al te snuggere compiler?
Omdat aan de code verder nix fout is. :)

Bovendien werk ik zelf ook geregeld met dergelijke compilers, en er zijn er niet veel die foutloos werken.

Overigens, IMHO hebben snuggere C compilers wel een optie om inline functies te maken.

[ Voor 15% gewijzigd door farlane op 15-10-2003 09:15 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 13-08 16:46

curry684

left part of the evil twins

.oisyn schreef op 14 October 2003 @ 21:35:
Pssst, inline is ok in C99 ;)
:X

* curry684 houdt standaarden niet zo hard bij... ;)

Professionele website nodig?


Verwijderd

overigens heeft een van onze gerespecteerde goeroes (weet niet zo meer even of het nou curry,oisyn of iemand anders was) al eens de meest effectieve swap in c gepost :

C:
1
2
3
4
5
6
void swap(int *a,int *b)
{
  *a = *a ^ *b;
  *b = *b ^ *a;
  *a = *a ^ *b;
}

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 21-08 12:02

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik was het iig niet, maar *a ^= *b ^= *a ^= *b is korter ;)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 13-08 16:46

curry684

left part of the evil twins

Ik ook niet, ik prefereer namelijk de templatized versie en laat de compiler zelf uitzoeken hoe hij het makkelijkste de 2 gegeven objecten kan swappen :)

Professionele website nodig?


  • Emmeau
  • Registratie: Mei 2003
  • Niet online

Emmeau

All your UNIX are belong to us

Verwijderd schreef op 15 October 2003 @ 11:34:
overigens heeft een van onze gerespecteerde goeroes (weet niet zo meer even of het nou curry,oisyn of iemand anders was) al eens de meest effectieve swap in c gepost :
C:
1
2
3
4
5
6
void swap(int *a,int *b)
{
  *a = *a ^ *b;
  *b = *b ^ *a;
  *a = *a ^ *b;
}
of
a^=b^=a^=b^=a;
Wat een onzin dat dit de meest effectieve swap zou zijn. Denk je echt dat een beetje compiler de cpu zichzelf over de kop gaat laten jagen met XOR-en?
Kijk eens wat GCC doet op de volgende code:
C:
1
2
3
4
5
6
7
8
9
10
11
int main(int argc, char *argv[])
{       
   int a=1;
   int b=2; 

   a^=b^=a^=b;  
        
   (void)printf("a=%d b=%d\n", a,b);
        
   return 0;    
}
En laten we eens kijken wat GCC er van maakt in assembly zonder optimalizatie (gcc -S -O0)
gcc -S -O0
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
        .file   "swaptest.c"
        .section        .rodata
.LC0:
        .string "a=%d b=%d\n"
        .text
.globl main
        .type   main,@function
main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        andl    $-16, %esp
        movl    $0, %eax
        subl    %eax, %esp
        movl    $1, -4(%ebp)
        movl    $2, -8(%ebp)
        movl    -8(%ebp), %edx
        leal    -4(%ebp), %eax
        xorl    %edx, (%eax)
        movl    -4(%ebp), %edx
        leal    -8(%ebp), %eax
        xorl    %edx, (%eax)
        movl    -8(%ebp), %edx
        leal    -4(%ebp), %eax
        xorl    %edx, (%eax)
        subl    $4, %esp
        pushl   -8(%ebp)
        pushl   -4(%ebp)
        pushl   $.LC0
        call    printf
        addl    $16, %esp
        movl    $0, %eax
        leave
        ret
.Lfe1:
        .size   main,.Lfe1-main
        .ident  "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
gcc -S -O3
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
        .file   "swaptest.c"
        .section        .rodata.str1.1,"aMS",@progbits,1
.LC0:
        .string "a=%d b=%d\n"
        .text
        .p2align 2,,3
.globl main
        .type   main,@function
main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        andl    $-16, %esp
        pushl   %eax
        pushl   $1
        pushl   $2
        pushl   $.LC0
        call    printf
        xorl    %eax, %eax
        leave
        ret
.Lfe1:
        .size   main,.Lfe1-main
        .ident  "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"


Duidelijk is dat bij de geoptimaliseerde code, de X-ors al volledig weggeoptimaliseerd zijn. Eigenlijk worden dus alleen maar de 1, de 2 op de stack gepushed en een printf uitgevoerd.

Verder heb ik ook al eens gezien dat de xor swap door de compiler omgezet werd naar een versie waarbij een register gebruikt werd voor het swappen, aangezien dit beter was.

Conclusie: Schrijfwijze van een statement (tenzij je de orde van het algorithme wijzigt natuurlijk) leidt niet altijd tot betere, snellere, kleinere, of geoptimalizeerde code. Een groot deel wordt door de compiler al gedaan.

[ Voor 10% gewijzigd door Emmeau op 16-10-2003 16:19 ]

If you choose to criticise you choose your enemies


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 21-08 12:02

.oisyn

Moderator Devschuur®

Demotivational Speaker

Emmau: het is efficienter in de zin dat je geen temporary nodig hebt, maar minder efficient omdat je 3 instructies nodig hebt die onderling afhankelijk zijn, ipv 2. Maar dat terzijde, als je een test doet, dan moet je wel goed testen, want die van jou zegt natuurlijk helemaal niets.

In jouw geval kan de compiler namelijk aannames doen over wat er met de variabelen gebeurt, omdat het zich allemaal lokaal binnen main () afspeelt. Maak een functie swap () zoals Akhorahil definieert in een andere translation unit, en vergelijk daarvan de bijbehorende assembly.

Dus als je voortaan iets als 'onzin' afdoet kom dan wel gewoon met een goed bewijs

(overigens ben ik ook voor een std::swap, maar ik code dan ook C++ ;))

[ Voor 6% gewijzigd door .oisyn op 16-10-2003 16:17 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Emmeau
  • Registratie: Mei 2003
  • Niet online

Emmeau

All your UNIX are belong to us

Ok, ik geef toe dat dit een eenvoudig voorbeeld is, maar in ieder geval zal een beetje compiler toch waarschijnlijk doet wat hem het beste lijkt (bijv. swappen via register), omdat dit nu net een aantal clockcycli sneller is.

Ik zou het toch erg sneu vinden als een compiler 3 XOR instructies niet zou vervangen door een swap met tussenkomst van een register.

If you choose to criticise you choose your enemies


Verwijderd

@curry, oisyn. : leuk dat het in C++ met std::swap veel mooier is opgelost, maar het ging hier om ansi C, vandaar mijn voorbeeld

@emmeau : zoals je zelf al zegt is in jouw door GCC geoptimaliseerde versie de swap ZELF weggeoptimaliseert, en dat was dacht ik nou net niet de bedoeling (tenzij het TS om een even triviale implementatie als jouw voorbeeld hier te doen is).

Je hebt natuurlijk wel gelijk dat de compiler dit soort optimalisaties regelt, maar hecht niet TE veel waarde aan compiler optimalisaties, de C++ compiler die microsoft meeleverd die ik hier gebruik is bijvoorbeeld niet echt een optimalisatie wondertje...

edit : het gaat om de compiler uit Visual Studio 6 SP 5

[ Voor 5% gewijzigd door Verwijderd op 16-10-2003 21:55 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 17:14
Als je op de hoogte bent van compiler theorie, dan weet je hoe lastig het is om die 3 XORs te vervangen door een optimale swap. De enige reden dat een compiler het kan, is dat precies dat pattern ingeprogrameerd is in de optimizer (zgn. peephole optimalisation). Feitelijk herkent de optimizer dus de slechte implementatie met de 3 xors, en vervangt 'm door een register temporary of eventueel een XCHG

Ik denk dat de resulterende instructiesequence kleiner is dan een functieaanroep, en dat inlinen dus effectied geheugen spaart, en mede daardoor ook sneller is.

PS. ook in C90 mocht een compiler al inlinen, alleen kon je het niet verzoeken. Maar zolang de semantiek niet verandert is elke methode om een functie te implementeren toegestaan. Een static functie was bijvoorbeeld een makkelijke kandidaat.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein

Pagina: 1