[C++] Frage zur Deklarierung einer unbekannent Zahl von Variablen mit einer Schleife

AMBiX ist ein Benchmark, kein Compiler :] ;). Die C Version ist das erste Release, momentan frickele ich an der D Version.

Eine kleine Datenbank mit Ergebnissen findest du zB. hier: http://www.i-hasser.net/benchmark/result/show4/show.php

Wie man sieht hat AMBiX auch seine Makel, der Celeron kommt zB. zu gut weg.



Die großen Zeitunterschiede sind allerdings zu auffällig - so groß dürften sie nicht sein, Pointerzugriffe sind normalerweise etwas langsamer.

Worauf bezieht sich das Ticks? Doch nicht auf CPU Takte per RDTSC, oder? Wenn doch ist der Vergleich hinfällig, weil es reicht wenn der Scheduler da einmal was dazwischen nimmt und die Zeitdauer geht gleich um tausende Takte nach oben.
 
hat sich scho auf CPU takte per rdtsc bezogen. aber da war nix mit scheduler oder so.
hab das edliche mal getestet. die werte waren +- 10% immmer die gleichen. waere schon ein toller zufall, wenn immer die gleiche stoerung vorhanden waere ;o)

das mit dem AMBiX hab ich wohl missverstanden. war schon etwas muede.

Nachtschatten
 
Original geschrieben von Nachtschatten
vorallem im geschwindigkeitsbereich macht es nen grossen unterschied.

hab einfach mal einen test mit und ohne pointer gemaht und dann gemessen.

aufgabe:
eine 10er liste vectoren erstellen
im ersten vector eine null eintragen.
die 10er liste auf eine 100er liste erweitern.
im ersten vector 1 eintragen
vectorliste leoschen.

mit reine vectoren: 12984 ticks
mit pointer und vector: 5592 ticks.

das bei 100 durchlaeufen und den mittelwert genommen.
mit den pointern also mehr als doppelt so schnell.
jedoch vom arbeitsaufwand war es mit vectoren besser.

Gib mal bitte an, was genau bei dir Zeiger bzw. keine waren. Dass Zeiger einen Geschwindigkeitsvorteil bedeuten stimmt nämlich so allgemein nicht. Wenn sie z.B. als Parameter genutzt werden, kann das durchaus richtig sein, insbesondere dann, wenn es um Objekte geht. Es ist ja schon ein Unterschied, ob ein 32bit-Wert übergeben wird, oder kiolbyte-weise Objekte kopiert werden. Wenn es aber um elementare Konstrukte geht, wie z.B. Array-Zugriffe, können Zeiger sogar einen erheblichen Geschwindigkeitsverlust bedeuten, weil eben Compiler sowas schlechter optimieren.
Und zu erstem Beispiel hier würde ich eh vorschlagen Referenzen statt Zeiger zu nehmen. Der Effekt ist der gleiche, nur kann dabei wesentlich weniger passieren. ;)
 
Naja, Referenzen sind allgemein recht unsauber.

Also solche Konstrukte wie 'void abc(int &a, float &b);' sollte man sich gänzlich sparen - man sieht nicht, ob die Werte dabei verändert werden.

Besser sind da Konstruktionen wie 'void abc(int* a, float* b);'.
Es gibt nix schöneres als nach einer Stunde debuggen festzustellen, dass ein Wert der irgendwo mal Parameter war von dieser Funktion verändert wurde - oder noch besser - man in dieser Funktion darauf nicht geachtet hat und sich der Wert demzufolge geändert hat.
 
Original geschrieben von i_hasser
Naja, Referenzen sind allgemein recht unsauber.

Also solche Konstrukte wie 'void abc(int &a, float &b);' sollte man sich gänzlich sparen - man sieht nicht, ob die Werte dabei verändert werden.

dafür gibt es const ;)

Referenzen haben halt den Vorteil dass man damit nicht wild im Speicher rumfurwerken kann, weil sie halt an eine Variable gebunden sind.
 
Mag ja sein, aber nix ist wie gesagt schlimmer als nach einer Stunde debuggen festzustellen, dass man irgendwo mal einen Wert per referenz übergeben hat - das kann man per Zeiger viel deutlicher kennzeichnen.

Beispiel:

Code:
void fa(int &a, int b, int &c);
void fb(int* a, int b, int* c);

[...]

// Aufruf:
int x, y, z;

fa(x,y,z);
fb(&x, y, &z);

Da sieht man sofort am & dass es sich nur um einen Zeiger handeln kann.
Außerdem macht das & auch nix anderes als den Zeiger zu übergeben, von daher... - macht man es dagegen so wie der dämliche Basic Compiler (werte auf den Stack legen und nach der Funktion wieder runternehmen) verschenkt man einen Haufen Performance.


Wenn es wirklich um Performance geht sollte man sich aber allgemein angewöhnen alles per Zeiger zu übergeben - ist bei Funktionsaufrufen einfach schneller, selbst bei zB. chars (die nachfolgenden Daten liegen dann nicht mehr an einer 4 Byte boundary, was Zugriffe darauf extrem verlangsamt - sowas kann man auch nicht wegoptimieren, weil dann die Sache nicht mehr portabel wird wie zB. auch Register Argument Passing).


Überhaupt gibt es für sowas schöne Beispiele. Mein Prime M3 Alg (siehe Progg Wettbewerb) braucht zB. für 2 Zugriffe chars. Nutzt man nun dafür auch chars ist das langsamer als mit normalen ints, weil heute eben keine CPU mehr ordentlich mit 8 Bit rechnen kann - wieso auch.


Es gibt nur 4 Sachen die man bei Performancekrittischen Sachen immer direkt übergeben kann - 32bit Integer, 64bit Integer, Floats und Doubles (und natürlich Pointer ;D).
 
Original geschrieben von i_hasser
Mag ja sein, aber nix ist wie gesagt schlimmer als nach einer Stunde debuggen festzustellen, dass man irgendwo mal einen Wert per referenz übergeben hat - das kann man per Zeiger viel deutlicher kennzeichnen.

Beispiel:

Code:
void fa(int &a, int b, int &c);
void fb(int* a, int b, int* c);

[...]

// Aufruf:
int x, y, z;

fa(x,y,z);
fb(&x, y, &z);

Da sieht man sofort am & dass es sich nur um einen Zeiger handeln kann.
Außerdem macht das & auch nix anderes als den Zeiger zu übergeben, von daher... - macht man es dagegen so wie der dämliche Basic Compiler (werte auf den Stack legen und nach der Funktion wieder runternehmen) verschenkt man einen Haufen Performance.

Du gehst hier nur von C aus, ich von C++. Da sind Referenzen extra für die Parameterübergabe eingeführt worden (in C gibt es keine Referenzen).


Wenn es wirklich um Performance geht sollte man sich aber allgemein angewöhnen alles per Zeiger zu übergeben - ist bei Funktionsaufrufen einfach schneller, selbst bei zB. chars (die nachfolgenden Daten liegen dann nicht mehr an einer 4 Byte boundary, was Zugriffe darauf extrem verlangsamt - sowas kann man auch nicht wegoptimieren, weil dann die Sache nicht mehr portabel wird wie zB. auch Register Argument Passing).

Bitte was? Also nach meinem Kenntnisstand werden Speicherzugriffe genau dann langsam, wenn die Daten nicht aligned sind. Deshalb werden die ja an den entsprechenden Adressen ausgerichtet. ???
 
Original geschrieben von PuckPoltergeist
Du gehst hier nur von C aus, ich von C++. Da sind Referenzen extra für die Parameterübergabe eingeführt worden (in C gibt es keine Referenzen).

Ach ja? Wie denn? Die Zeiger sind aber trotzdem schon ziemlich sauber, weil das nahtlos mit dem C++ OO zusammenpasst.



Bitte was? Also nach meinem Kenntnisstand werden Speicherzugriffe genau dann langsam, wenn die Daten nicht aligned sind. Deshalb werden die ja an den entsprechenden Adressen ausgerichtet. ???

Nix anderes hab ich gesagt. Der Compiler kann bei der Parameterübergabe aber zB. nix alignen, weil der Code dann inkompatibel wäre, genauso wie eben die Argumente in den Registern zu übergeben.
 
Original geschrieben von i_hasser
Ach ja? Wie denn? Die Zeiger sind aber trotzdem schon ziemlich sauber, weil das nahtlos mit dem C++ OO zusammenpasst.

In C++ kann man Sachen wie

Code:
int i;
int &x = i; 

oder 

void increment(int& aa) { aa++; }
void f()
{
  int x = 1;
  increment(x);
}
machen. Das geht in C nicht. Und wenn es stört, dass die Variablen verändert werden können (in diesem Fall logischerweise nicht), kann man sie halt als const deklarieren.

Vielleicht mal noch ein Zitat aus dem Stroustrup (wo das Beispiel da oben auch her ist):
Eine Referenz ist ein alternativer Name für ein Objekt. Die Hauptanwendung von Referenzen ist die Angabe von Argumenten und Rückgabewerten für Funktionen im allgemeinen und für überladene Operatoren im besonderen.


Nix anderes hab ich gesagt. Der Compiler kann bei der Parameterübergabe aber zB. nix alignen, weil der Code dann inkompatibel wäre, genauso wie eben die Argumente in den Registern zu übergeben.

Ok, dann habe ich dich falsch verstanden. Kam bei mir so an, dass durch das Alignment die Ausführung langsamer wird. Und wieso können bei der Parameterübergabe die Daten nicht aligned sein? ???
 
Original geschrieben von PuckPoltergeist
In C++ kann man Sachen wie

Code:
int i;
int &x = i; 

oder 

void increment(int& aa) { aa++; }
void f()
{
  int x = 1;
  increment(x);
}
machen. Das geht in C nicht. Und wenn es stört, dass die Variablen verändert werden können (in diesem Fall logischerweise nicht), kann man sie halt als const deklarieren.


Hmmm, das & hab ich ja schon angesprochen - wusste garnet, dass es das in C nicht gibt *buck*. Trotzdem sind Zeiger einfach besser, weil man schon am Funktionsaufruf sieht, dass es sich da um einen Zeiger handelt.

Wenn man nicht performancekrittisch programmiert sollte man sich natürlich überlegen wie man das am dümmsten mit einem System verbindet - wenn man nur Zeiger übergibt wird das auch unübersichtlich, weil man nie weis ob der Zeigerinhalt verändert wird oder nicht.

Ich habs mir angewöhnt alles was verändert wird per Zeiger zu übergeben und der Reihenfolge nach ans Ende der Parameter zu setzen. Und damit bin ich bisher auch gut gefahren ;).

Wie man sieht hab ich das & bisher gemieden wie die Pest, weil es imho einfach unsauber ist. Man sieht am Aufruf f(x) eben nicht, ob x jetzt verändert wird oder nicht. Die Deklaration will ich nicht erst ändern müssen um das auszuschließen, außerdem muss ich da erst wieder drauf gucken wie x genau deklariert wurde.

Ok, dann habe ich dich falsch verstanden. Kam bei mir so an, dass durch das Alignment die Ausführung langsamer wird. Und wieso können bei der Parameterübergabe die Daten nicht aligned sein? ???

Weil die Daten der Reihenfolge nach auf den Stack gepackt werden und dann die Funktion aufgerufen wird (bei C werden die letzten Parameter zuerst draufgelegt).

Ein char ist nunmal nur ein Byte groß, und entsprechend wird auch nur ein Byte auf den Stack gepackt. Der Rest ist dann wegen dem char nicht 4*n Bytes vom 1. Parameterbyte entfernt, sondern eben 4*n+1 Bytes weil das char keine 4 Byte lang ist.

Würde der Compiler das char einfach auf 4 Byte aufblähen müssten alle Funktionen die das char als Parameter bekommen neucompiliert werden, weil normalerweise ein char eben ein Byte ist und nicht 4.
 
Original geschrieben von i_hasser
Ich habs mir angewöhnt alles was verändert wird per Zeiger zu übergeben und der Reihenfolge nach ans Ende der Parameter zu setzen. Und damit bin ich bisher auch gut gefahren ;).

Das ist halt Ansichtssache. Und wie schon geschrieben, kann mit Referenzen weniger passieren.



Weil die Daten der Reihenfolge nach auf den Stack gepackt werden und dann die Funktion aufgerufen wird (bei C werden die letzten Parameter zuerst draufgelegt).

Ein char ist nunmal nur ein Byte groß, und entsprechend wird auch nur ein Byte auf den Stack gepackt. Der Rest ist dann wegen dem char nicht 4*n Bytes vom 1. Parameterbyte entfernt, sondern eben 4*n+1 Bytes weil das char keine 4 Byte lang ist.

Würde der Compiler das char einfach auf 4 Byte aufblähen müssten alle Funktionen die das char als Parameter bekommen neucompiliert werden, weil normalerweise ein char eben ein Byte ist und nicht 4.

Sache der ABI! Wenn da drinn steht, dass die Daten grundsätzlich aligned sind (ist bei x86_64 so für den gcc definiert, sofern ich mich jetzt nicht irre), dann muss man davon ausgehen. Grundsätzlich spricht da aus meiner Sicht nichts dagegen. Dass das dann von vorne bis hinten durchgehalten werden muss, versteht sich von selbst.
 
Original geschrieben von PuckPoltergeist
Das ist halt Ansichtssache. Und wie schon geschrieben, kann mit Referenzen weniger passieren.

Mit Pointern auch nicht wenn man den Pointer einmal per & von einem Wert ableitet.




Sache der ABI! Wenn da drinn steht, dass die Daten grundsätzlich aligned sind (ist bei x86_64 so für den gcc definiert, sofern ich mich jetzt nicht irre), dann muss man davon ausgehen. Grundsätzlich spricht da aus meiner Sicht nichts dagegen. Dass das dann von vorne bis hinten durchgehalten werden muss, versteht sich von selbst.

So ist es nunmal in der Praxis aber nicht! Alignment von irgendwelchen Funktionen und Schleifen macht den Code nicht inkompatibel, bei Alignment auf dem Stack schon. Und desswegen wird es in der Praxis nicht genutzt und GCC bietet afaik auch garkein Flag dafür an.
 
Original geschrieben von i_hasser
Mit Pointern auch nicht wenn man den Pointer einmal per & von einem Wert ableitet.

Zeiger kann man immer auf andere Speicherstellen zeigen lassen, oder liege ich da jetzt falsch?


So ist es nunmal in der Praxis aber nicht! Alignment von irgendwelchen Funktionen und Schleifen macht den Code nicht inkompatibel, bei Alignment auf dem Stack schon. Und desswegen wird es in der Praxis nicht genutzt und GCC bietet afaik auch garkein Flag dafür an.

gcc-manual:
-mpreferred-stack-boundary=num
Attempt to keep the stack boundary aligned to a 2 raised to num byte boundary. If -mpreferred-stack-boundary is not specified, the default is 4 (16 bytes or 128 bits), except when optimizing for code size (-Os), in which case the default is the minimum correct alignment (4 bytes for x86, and 8 bytes for x86-64).

On Pentium and PentiumPro, double and long double values should be aligned to an 8 byte boundary (see -malign-double) or suffer significant run time performance penalties. On Pentium III, the Streaming SIMD Extension (SSE) data type __m128 suffers similar penalties if it is not 16 byte aligned.


To ensure proper alignment of this values on the stack, the stack boundary must be as aligned as that required by any value stored on the stack. Further, every function must be generated such that it keeps the stack aligned. Thus calling a function compiled with a higher preferred stack boundary from a function compiled with a lower preferred stack boundary will most likely misalign the stack. It is recommended that libraries that use callbacks always use the default setting.


This extra alignment does consume extra stack space, and generally increases code size. Code that is sensitive to stack space usage, such as embedded systems and operating system kernels, may want to reduce the preferred alignment to -mpreferred-stack-boundary=2.

Das Zeug ist aligned, sogar auf i386.
 
Original geschrieben von PuckPoltergeist
Zeiger kann man immer auf andere Speicherstellen zeigen lassen, oder liege ich da jetzt falsch?

Jep, aber ein

'int* a=&b;' wird eben nur auf b zeigen ;).


gcc-manual:


Das Zeug ist aligned, sogar auf i386.

Die Stack Boundary ist was völlig anderes :]. Das betrifft den Stackbereich für lokale Vars, den kann man frei ändern.
 
Original geschrieben von i_hasser
Jep, aber ein

'int* a=&b;' wird eben nur auf b zeigen ;).

Und ein a = &c zeigt halt auf c, sofern man das irgendwo im Code mit unterbringt. Das geht mit Referenzen nicht, mit Zeigern schon.


Die Stack Boundary ist was völlig anderes :]. Das betrifft den Stackbereich für lokale Vars, den kann man frei ändern.

Stimmt, die Adressen von übergebenen Parametern sind nämlich immer ein Vielfaches von vier bei i386. Insofern sind die offenbar wirkich nicht beeinflußbar, sondern immer aligned. :P
 
Original geschrieben von PuckPoltergeist
Und ein a = &c zeigt halt auf c, sofern man das irgendwo im Code mit unterbringt. Das geht mit Referenzen nicht, mit Zeigern schon.

Wenn ich einer Funktion die als Parameter einen Zeiger erwartet ein &a gebe ist das also eindeutig ;).



Stimmt, die Adressen von übergebenen Parametern sind nämlich immer ein Vielfaches von vier bei i386. Insofern sind die offenbar wirkich nicht beeinflußbar, sondern immer aligned. :P

Nein, so wird es aber von den OSs normalerweise gehandhabt. Auf der anderen Seite zeigt aber gerade die Auswerung bei variabler Parameterzahl genau das Gegenteil.

Normalerweise legt sich eine Funktion ausgehend vom ESP einen neuen lokalen Stackbereich an, der bei EBP beginnt (der Stäck wächst rücktwärts, nicht vergessen).

Bei EBP liegt dadurch normalerweise die Rückkehradresse für das letztendliche RET. EBP+4 zeigt auf den ersten Parameter der übergeben wurde, EBP+8 auf den 2. usw.

Liegt nun zuallererst ein char als Parameter vor und danach zB. ein int (in der Reihenfolge auf dem Stack, je nach Calling Convention ist die Reihenfolge beim Funktionsprototyp umgekehrt), liegt das char bei EBP+4 und der int bei EBP+5.

Aber es gibt eine einfache Methode das herauszufinden, die ich gleich mal anwenden werde ;).
 
Original geschrieben von i_hasser
Wenn ich einer Funktion die als Parameter einen Zeiger erwartet ein &a gebe ist das also eindeutig ;).

Ich habe das Gefühl, wir reden hier schon wieder aneinander vorbei. Was ich meine ist z.B. statt

&a = ....
a = ....

Damit änderst du nicht den Wert der Variable, sondern den Zeiger. Sowas kann auch sehr fix gehen, und kann mit Referenzen halt nicht passieren. Ich weiß, das ist kein Beinbruch, aber halt eine zusätzliche Fehlerquelle.


Nein, so wird es aber von den OSs normalerweise gehandhabt. Auf der anderen Seite zeigt aber gerade die Auswerung bei variabler Parameterzahl genau das Gegenteil.

Normalerweise legt sich eine Funktion ausgehend vom ESP einen neuen lokalen Stackbereich an, der bei EBP beginnt (der Stäck wächst rücktwärts, nicht vergessen).

Bei EBP liegt dadurch normalerweise die Rückkehradresse für das letztendliche RET. EBP+4 zeigt auf den ersten Parameter der übergeben wurde, EBP+8 auf den 2. usw.

Liegt nun zuallererst ein char als Parameter vor und danach zB. ein int (in der Reihenfolge auf dem Stack, je nach Calling Convention ist die Reihenfolge beim Funktionsprototyp umgekehrt), liegt das char bei EBP+4 und der int bei EBP+5.

Aber es gibt eine einfache Methode das herauszufinden, die ich gleich mal anwenden werde ;).

Ja, die gibt es. Was meinst du, wieso ich so sehr auf meinem Standpunkt beharre? ;)

in C:
Code:
void fkt(char a, int b, char c)
{
        int i;
        char j,k;

        j = a + a;

        i = b + b;

        k = c * c;

        printf("%d %d %d\n", j,i,k);
}

int main()
{
        int x=100;
        char y=11;

        fkt(y,x,y);

        return 0;
}
gcc -m32 -S -o boundary.S boundary.c
Code:
        .file   "boundary.c"
        .section        .rodata
.LC0:
        .string "%d %d %d\n"
        .text
.globl fkt
        .type   fkt, @function
fkt:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $40, %esp
        movl    8(%ebp), %eax
        movl    16(%ebp), %edx
        movb    %al, -1(%ebp)
        movb    %dl, -2(%ebp)
        movzbl  -1(%ebp), %eax
        addb    -1(%ebp), %al
        movb    %al, -9(%ebp)
        movl    12(%ebp), %eax
        addl    12(%ebp), %eax
        movl    %eax, -8(%ebp)
        movzbl  -2(%ebp), %eax
        mulb    -2(%ebp)
        movb    %al, -10(%ebp)
        movsbl  -10(%ebp),%eax
        movl    %eax, 12(%esp)
        movl    -8(%ebp), %eax
        movl    %eax, 8(%esp)
        movsbl  -9(%ebp),%eax
        movl    %eax, 4(%esp)
        movl    $.LC0, (%esp)
        call    printf
        leave
        ret
        .size   fkt, .-fkt
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $24, %esp
        andl    $-16, %esp
        movl    $0, %eax
        addl    $15, %eax
        addl    $15, %eax
        shrl    $4, %eax
        sall    $4, %eax
        subl    %eax, %esp
        movl    $100, -4(%ebp)
        movb    $11, -5(%ebp)
        movsbl  -5(%ebp),%eax
        movl    %eax, 8(%esp)
        movl    -4(%ebp), %eax
        movl    %eax, 4(%esp)
        movsbl  -5(%ebp),%eax
        movl    %eax, (%esp)
        call    fkt
        movl    $0, %eax
        leave
        ret
        .size   main, .-main
        .section        .note.GNU-stack,"",@progbits
        .ident  "GCC: (GNU) 3.4.3  (Gentoo Linux 3.4.3, ssp-3.4.3-0, pie-8.7.6.6)"
gcc -O2 -m32 -S -o boundary.S2 boundary.c
Code:
        .file   "boundary.c"
        .section        .rodata.str1.1,"aMS",@progbits,1
.LC0:
        .string "%d %d %d\n"
        .text
        .p2align 4,,15
.globl fkt
        .type   fkt, @function
fkt:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $24, %esp
        movzbl  16(%ebp), %eax
        movzbl  8(%ebp), %ecx
        movl    12(%ebp), %edx
        movl    $.LC0, (%esp)
        imull   %eax, %eax
        addl    %ecx, %ecx
        addl    %edx, %edx
        movsbl  %cl,%ecx
        movl    %edx, 8(%esp)
        movl    %ecx, 4(%esp)
        movsbl  %al,%eax
        movl    %eax, 12(%esp)
        call    printf
        leave
        ret
        .size   fkt, .-fkt
        .p2align 4,,15
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $24, %esp
        andl    $-16, %esp
        subl    $16, %esp
        movl    $11, 8(%esp)
        movl    $100, 4(%esp)
        movl    $11, (%esp)
        call    fkt
        leave
        xorl    %eax, %eax
        ret
        .size   main, .-main
        .section        .note.GNU-stack,"",@progbits
        .ident  "GCC: (GNU) 3.4.3  (Gentoo Linux 3.4.3, ssp-3.4.3-0, pie-8.7.6.6)"

Für mich sehen die Daten schon aligned aus. :P
 
Code:
00000000 <tst>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 04                sub    $0x4,%esp
   6:   0f be 45 0c             movsbl 0xc(%ebp),%eax
   a:   03 45 08                add    0x8(%ebp),%eax
   d:   03 45 10                add    0x10(%ebp),%eax
  10:   89 45 fc                mov    %eax,0xfffffffc(%ebp)
  13:   c9                      leave
  14:   c3                      ret
  15:   8d 74 26 00             lea    0x0(%esi,1),%esi
  19:   8d bc 27 00 00 00 00    lea    0x0(%edi,1),%edi

00000020 <main>:
  20:   55                      push   %ebp
  21:   89 e5                   mov    %esp,%ebp
  23:   83 ec 18                sub    $0x18,%esp
  26:   83 e4 f0                and    $0xfffffff0,%esp
  29:   c7 44 24 08 03 00 00    movl   $0x3,0x8(%esp,1)
  30:   00
  31:   c7 44 24 04 02 00 00    movl   $0x2,0x4(%esp,1)
  38:   00
  39:   c7 04 24 01 00 00 00    movl   $0x1,(%esp,1)
  40:   e8 fc ff ff ff          call   41 <main+0x21>
  45:   c9                      leave
  46:   c3                      ret

per gcc -O2 -march=athlon-xp bei

Code:
void tst(int a, char b, int c) 
{
	volatile int i;
	i=a+b+c;
	return;
}

int main()
{
	tst(1,2,3);
}

Hier sieht die Sache mit der Calling Convention etwas anders aus, weil im lokalen Bereich noch Variablen deklariert werden (wenn man mit NASM ein ELF zusammentackert ist der 1. Parameter bei EBP+4, das PUSH EBP verschiebt das hier alles um 4).

Aber er aligned alles an einer 4 Byte Boundary, anscheinend ist das Teil der i386 C Calling Conventions. Ist bei Windoofs nicht anders (ist vorhin erst eingefallen, hab mit NASM mal eine Windoofs DLL geschrieben).

Bei Windoofs wird dann aber afaik alles als Zeiger übergeben was nicht n*4 Byte groß ist. So genau hab ich mich damit damals aber auch nicht beschäftigt, da ging es mir um andere Sachen.
 
Wie gesagt, bei Windoofs wird dann normalerweise alles als Zeiger übergeben was nicht 4n Byte groß ist.

Zu der Fehlerquellensache: Dann nehmen wir doch am besten alle einfach BASIC, wenn man damit ein Prog ohne Zuhilfename von DLL/API Funktionen zum Abschmieren bekommt ist man wirklich gut :P.
 
Original geschrieben von i_hasser
Zu der Fehlerquellensache: Dann nehmen wir doch am besten alle einfach BASIC, wenn man damit ein Prog ohne Zuhilfename von DLL/API Funktionen zum Abschmieren bekommt ist man wirklich gut :P.

Ob man es zum abschmieren bekommt weiß ich nicht. Inkorrektes Verhalten kann man mit nem Sack voll goto's auf jeden Fall sehr schnell provozieren. ;D
Ich will dir deine Zeiger ja gar nicht ausreden, aber Referenzen sind durchaus sehr sinnvoll. Sonst hätte man sie bei C++ ja nicht neu eingeführt. Sie sind einfach zu gebrauchen, schnell in der Ausführung, sehr bequem und für den Compiler auch sehr einfach zu optimieren. 8)
 
Original geschrieben von PuckPoltergeist
Ob man es zum abschmieren bekommt weiß ich nicht. Inkorrektes Verhalten kann man mit nem Sack voll goto's auf jeden Fall sehr schnell provozieren. ;D
Ich will dir deine Zeiger ja gar nicht ausreden, aber Referenzen sind durchaus sehr sinnvoll. Sonst hätte man sie bei C++ ja nicht neu eingeführt. Sie sind einfach zu gebrauchen, schnell in der Ausführung, sehr bequem und für den Compiler auch sehr einfach zu optimieren. 8)

Und der Progger ärgert sich schwarz, wenn er durch eine Referenz einen hässlichen Fehler reingebastelt hat, dem er nach mehreren Stunden debuggen auf die Schliche gekommen ist ;).

Ich habs so auch in einigen Büchern gefunden, also ich bin mit meiner Meinung nicht allein.
 
Original geschrieben von i_hasser
Und der Progger ärgert sich schwarz, wenn er durch eine Referenz einen hässlichen Fehler reingebastelt hat, dem er nach mehreren Stunden debuggen auf die Schliche gekommen ist ;).

Ich habs so auch in einigen Büchern gefunden, also ich bin mit meiner Meinung nicht allein.

Naja, ich sehe jetzt erstmal nicht, was man da für Fehler reinbauen kann, die man mit Parameterübergabe bzw. Zeigerübergabe nicht genauso hinbekommt. Eine Referenz ist ja nix anderes als ein anderer Name für eine Variable. Der Unterschied bei der Parameterübergabe ist nur, dass das Objekt (im weitesten Sinne, also auch elementare Datentypen) nicht kopiert wird. Wenn man auf jeden Fall verhindern will, dass das ursprüngliche Objekt in der Funktion verändert wird, übergibt man halt eine const-Referenz. Da kann dann eigentlich gar nix mehr passieren.
 
Ich mag pointer - auch in C++. Ginge folg. mit Referenzen?

Code:
PluginExtTree **node;
PluginExtTree *tnode = this;
	
node = &tnode;
	
while (*node) {
	if (extension.Cmp((*node)->extension) > 0)
		node = &((*node)->right_node);
	else
		node = &((*node)->left_node);
}
	
*node = new PluginExtTree(ext, plugin);

Ist ein Teil einer Art insert Operation im Binärbaum.
 
Original geschrieben von PuckPoltergeist
Naja, ich sehe jetzt erstmal nicht, was man da für Fehler reinbauen kann, die man mit Parameterübergabe bzw. Zeigerübergabe nicht genauso hinbekommt. Eine Referenz ist ja nix anderes als ein anderer Name für eine Variable. Der Unterschied bei der Parameterübergabe ist nur, dass das Objekt (im weitesten Sinne, also auch elementare Datentypen) nicht kopiert wird. Wenn man auf jeden Fall verhindern will, dass das ursprüngliche Objekt in der Funktion verändert wird, übergibt man halt eine const-Referenz. Da kann dann eigentlich gar nix mehr passieren.

Das hab ich doch schon geschrieben! :]

Glaub es oder glaub es nicht, es ist nunmal einfach so.
 
Original geschrieben von i_hasser
Das hab ich doch schon geschrieben! :]

Glaub es oder glaub es nicht, es ist nunmal einfach so.

Ja, man ändert einfach unbemerkt den übergebenen Wert. Kann man ganz einfach unterbinden, und kann mit Zeigern genauso passieren. Dass das bei dir weniger der Fall ist, weil du bei Zeigern explizit davon ausgehst, dass sich da was ändert, ist eine andere Sache.
 
Zurück
Oben Unten