Warum faengt das Array bei 0 an?



  • wxSkip schrieb:

    1. C++/CLI gibt es schon.

    Hat C++/CLI wirklich einen GC? Ich ging davon aus, dass Blubb^ bla=gcnew Blubb ... eine Kurzform für etwa shared_ptr<Blubb> bla=shared_ptr<Blubb>(new Blubb...) ist.



  • Athar schrieb:

    wxSkip schrieb:

    1. C++/CLI gibt es schon.

    Hat C++/CLI wirklich einen GC? Ich ging davon aus, dass Blubb^ bla=gcnew Blubb ... eine Kurzform für etwa shared_ptr<Blubb> bla=shared_ptr<Blubb>(new Blubb...) ist.

    Ich weiß nicht genau, wie das gemacht wird, lediglich, dass der Speicher automatisch verwaltet wird. Über shared_ptr bin ich auch unzureichend informiert.



  • Wie soll das mit nem GC gehen? Wenn ich irgendeine alte Lib habe, die den Speicher schon selber aufräumt und plötzlich macht es de GC nochmal?



  • 9tiere schrieb:

    Wie soll das mit nem GC gehen? Wenn ich irgendeine alte Lib habe, die den Speicher schon selber aufräumt und plötzlich macht es de GC nochmal?

    Wenn sich free() bzw. delete und delete[] merken, was sie gelöscht haben, weiß es ja der GC und der löscht es dann nicht mehr.



  • Athar schrieb:

    wxSkip schrieb:

    1. C++/CLI gibt es schon.

    Hat C++/CLI wirklich einen GC? Ich ging davon aus, dass Blubb^ bla=gcnew Blubb ... eine Kurzform für etwa shared_ptr<Blubb> bla=shared_ptr<Blubb>(new Blubb...) ist.

    Nein, es handelt sich wirklich um einen GC und die bla befindet sich im Managed Heap.



  • Nexus schrieb:

    Wobei man dann wieder gegen den Standard Krieg führen muss ("man darf nur PODs im Speicher verschieben"). Oder wäre das was für Move-Semantik in C++0x?

    Ich gehe nicht von Verschieben aus. Und ich gehe davon aus, daß das Verschieben in Sachen Cache-Zeugs nix bringt. Das Senken der internen Fragmentierung ist mir recht egal, da ich nichtmal bei **** einen Rechner mit weniger als 4G RAM finde. Mein bester Freund dahingehend heißt Buddy und der andere ist Alexandrescu.

    Nexus schrieb:

    Oder hast du es unportabel hingekriegt?

    Natürlich unportabel. C++ kennt keine Threads. Ein threadlokaler void* reicht. Das bieten alle. Aber wie ihn kriegen? (Im Sonderausnahemfall meiner HTTP-Server-Planung war es "portabel" insoweit, daß jedes angestrebte BS dem neuen Thread Stackspeicher gibt und ich erstmal ein char[65536] anlege und daraus schöpfe. Wenn alle, dann Error-Response). Ich denke zur Zeit an eibnen Threadlokalen void*, der auf einen nur für diesen Thread per memory mapping allokierten 64k-Bereich zeigt, dessen Seiten erst bei Zugriff physikalisch hinterlegt werden. Linux und Win können das mit fast gleichen Befehlen und einfach und übersichtlich ist es auch. Den Zeiger auf den Speicherbereich, den ich zu Threadstart reserviere, kann ich entweder immer runterrreichen, oder im threadlokalen globalen Speicher ablegen. Das ist Jacke wie Hose.
    Viel schlimmer ist, daß ich gerade kein Kreuz-Bit finde und deswegen einen Rechner nicht aufschrauben kann. Ich will morgen früh nicht den Hausmeister um einen passenden Schraubendreher anbetteln müssen.



  • volkard schrieb:

    Das Senken der internen Fragmentierung ist mir recht egal, da ich nichtmal bei **** einen Rechner mit weniger als 4G RAM finde. Mein bester Freund dahingehend heißt Buddy und der andere ist Alexandrescu.

    Buddy-System und Small-Object-Allocator? Ich müsste sowas endlich mal richtig anwenden. Bisher waren meine Projekte zu wenig ressourcenfessend, als dass new und delete nicht gereicht hätten. Meistens liegt das Performanceproblem auch woanders, da ich viel mit Grafik programmiere.

    volkard schrieb:

    Im Sonderausnahemfall meiner HTTP-Server-Planung war es "portabel" insoweit, daß jedes angestrebte BS dem neuen Thread Stackspeicher gibt und ich erstmal ein char[65536] anlege und daraus schöpfe. Wenn alle, dann Error-Response

    Ich nehme an, damit deckst du wahrscheinlich schon viele kleine Anforderungen ab... Klingt gut. Kann man eigentlich Stackspeicher gut für Placement New benutzen (wegen möglichen Alignmentproblemen, aber keine Ahnung wie praxisrelevant die tatsächlich sind)? Und Overflows abzufangen ist nicht mühsam?



  • Nexus schrieb:

    Ich nehme an, damit deckst du wahrscheinlich schon viele kleine Anforderungen ab... Klingt gut. Kann man eigentlich Stackspeicher gut für Placement New benutzen (wegen möglichen Alignmentproblemen, aber keine Ahnung wie praxisrelevant die tatsächlich sind)?

    Klar. Man nehme den

    void* alloc(size_t size){
       size=aufrundenAufAllesAlignment(size);//je nach dem, bei mir 8
       char* result=threadLocalTop+size;
       threadLocalTop+=size;
       if(threadLocalTop>threadLocalMax)//nonstandard, aber klappt faktisch immer
          throw Bla
       return result;
       //Keine Verkettung
    }
    void free(void* hihihi_HEHEHE_hohoho){
       //Hihi, bei Threadende geht's eh weg. 
    }
    

    [cpp]Und Overflows abzufangen ist nicht mühsam?[/quote]
    Ja, das ist doof, weil es ein if mehr kostet. Ich habe gar nicht daran gedacht, das auch noch einzusparen und der MMU aufzudrücken. Doch, habe ich. Aber nee, das ist es nicht wert. Mit if geht win/linux gleich und benutzt "Standardmittel", die jedes BS hat.



  • volkard schrieb:

    Könnte vielleicht ein paar Locks vermeiden und bei 12 Kernen überlegen viele Leute schon viel rum, Locks zu vermeiden als Standardrundumallheilmittel.

    Eventuell stehe ich gerade auf dem Schlauch, aber wo brauch man Locks bei new und delete?

    EDIT: Meinst du wenn z. B. shared_pointer in einem Objekt sind?
    Okay... Dann ist es klar. Oder auch anders?



  • volkard schrieb:

    Ich hatte mal einen Simulator gebaut, wo verflixt viele Objekte sich freispeicherallkokierte Nachrichten geschickt hatten, und bei 10Mio Nachrichten pro Sekunde war new/delete schon ein Happen.

    Was hast du für ein Netzwerk, damit new und delete die großen Performancebremsen sind?



  • ??????????? schrieb:

    volkard schrieb:

    Ich hatte mal einen Simulator gebaut, wo verflixt viele Objekte sich freispeicherallkokierte Nachrichten geschickt hatten, und bei 10Mio Nachrichten pro Sekunde war new/delete schon ein Happen.

    Was hast du für ein Netzwerk, damit new und delete die großen Performancebremsen sind?

    Verzögerte Funktionsaufrufe, indem Aufwachzeitpunkt, Funktionszeiger und zur Funktion passende Parameter in ein Nachricht-Objekt gestopft werden, das von Basisnachricht erbt. Wegen Polymorphie liegt new recht nahe. Die Nachrichtenzeiger werden in einer Prioitätswarteschlage gehalten. Der Aufrufer ruft nicht mehr die Funktion auf, sondern baut eine Nachricht und wirft sie auf den großen Haufen. Und es gibt keine Akteure, die sowas kompliziertes wie Wegfindung, Grafik oder pseudointelligentes Verhalten brauchen. Die aufgerufenen Funktionen sind alle recht billig.



  • CSpille schrieb:

    volkard schrieb:

    Könnte vielleicht ein paar Locks vermeiden und bei 12 Kernen überlegen viele Leute schon viel rum, Locks zu vermeiden als Standardrundumallheilmittel.

    Eventuell stehe ich gerade auf dem Schlauch, aber wo brauch man Locks bei new und delete?

    Gehts auch ohne? Ich will den Speicherblock doch wieder in die verkettete Liste hängen oder Baum, Heap, oder mit Nachbarn verschmelzen oder nur den freispeicherzähler inkrementieren, ohne daß ein anderer Thread auch an meinen Nachbarn rumfummelt oder gleichzeitig Zähler ändert.



  • seldon schrieb:

    Es ist für Ringstrukturen ausgesprochen praktisch:

    array[raw_index % array_size]
    

    Das (bzw. ähnliches) dürfte beispielsweise in Hashtables oft zu sehen sein.

    Außerdem wäre es mit Blick auf Zeigerarithmetik ziemlich verwirrend, das erste Element mit Index 1 anzusprechen.

    Naja, wohl eher ein Restklassenring, oder ? 😃

    $\mathbb{Z}_n$

    Das das Array bei null anfängt ist doch ganz klar, wenn man sich die übersetzung anguckt:

    type & array.operator[] (int i)
    {
    // prüfe index
    ....
    // gebe referenz zurück
    return *(array_ptr+i); //der Compiler übersetzt hier zu return start_addr+i*sizeof(type)
    };
    

    Der Index i=0 ist also der faktorielle Abstand des "ersten" Array Inhaltes zu der Startposition im Speicher....

    Und das
    11

    als "normaler" startindex genommen wird ist keine konvention, sondern weil es das multiplikative neutrale element (des Körpers) der Reelen Zahlen ist,...

    greetz



  • volkard schrieb:

    CSpille schrieb:

    volkard schrieb:

    Könnte vielleicht ein paar Locks vermeiden und bei 12 Kernen überlegen viele Leute schon viel rum, Locks zu vermeiden als Standardrundumallheilmittel.

    Eventuell stehe ich gerade auf dem Schlauch, aber wo brauch man Locks bei new und delete?

    Gehts auch ohne? Ich will den Speicherblock doch wieder in die verkettete Liste hängen oder Baum, Heap, oder mit Nachbarn verschmelzen oder nur den freispeicherzähler inkrementieren, ohne daß ein anderer Thread auch an meinen Nachbarn rumfummelt oder gleichzeitig Zähler ändert.

    mkay...
    Damit verschiebst du den Lock ja eigentlich nur...

    Also bei gleichzeitigem Arbeiten auf einer Datenstruktur, auf der Objekte gelöscht werden können...

    thx,
    volkard



  • zeusosc schrieb:

    Das das Array bei null anfängt ist doch ganz klar, wenn man sich die übersetzung anguckt:

    type & array.operator[] (int i)
    {
    // prüfe index
    ....
    // gebe referenz zurück
    return *(array_ptr+i); //der Compiler übersetzt hier zu return start_addr+i*sizeof(type)
    };
    

    Du verwechselst hier Ursache und Wirkung. Die Frage war, warum die "Übersetzung" genau so und nicht anders aussieht. Sie könnte auch so aussehen, womit Arrays 1-basierend wären:

    array[i] == *(array+i-1)
    

    (Und btw: der Index wird nicht geprüft)

    zeusosc schrieb:

    Und das
    11

    als "normaler" startindex genommen wird ist keine konvention, sondern weil es das multiplikative neutrale element (des Körpers) der Reelen Zahlen ist,...

    Verstehe ich jetzt ehrlich gesagt nicht. Du meinst der Mensch fängt gerne bei 1 mit zählen an, weil a*1=a?
    Außerdem ist 0 das neutrale Element der Addition und Addition passt viel besser zum Zählen als Multiplikation.



  • Das war bei einigen früheren Compilern (erste FORTRAN-Compiler) anders, da fingen die Arrays tatsächlich bei 1 an. Das schuf aber manchmal Probleme für die Programmierung. Deshalb der heute allgmein übliche Standard mit dem Anfangsindex = Null. Ist einfach besser. Bei C-Strings macht das noch mehr Sinn wegen des abschliessenden Nullzeichens.



  • berniebutt schrieb:

    Bei C-Strings macht das noch mehr Sinn wegen des abschliessenden Nullzeichens.

    Nö.



  • 'array[i] == *(array+i-1)' Hat das Problem das es nicht korrekt ist, wenn man annimmt das 'array[i] == *(array+i)' korrekt ist, oder?

    Damit es korrekt ist muesste es doch 'array[i+1] == *(array+i-1)' heissen, wobei sich dann die frage Stellt warum man die 1 nicht wegkuerzt.

    Wenn man jetzt die Basis 0 um eins nach rechts verschiebt um die 1 als Basis zu haben, heisst es wieder 'array[i] == *(array+i)'. Oder man hat eine gemischte Basis, dann koennte auch 'array[i] == *(array+i-1)' korrekt sein.

    Jedoch weiss ich nicht wie man mit gemischter Basis rechnet, vielleicht kann ja hier ein Mathematiker mal Licht in den Sinn und Unsinn reinbringen.

    Oder bringe ich hier was durcheinander?

    Gruss
    Baracke



  • Baracke_out schrieb:

    'array[i] == *(array+i-1)' Hat das Problem das es nicht korrekt ist, wenn man annimmt das 'array[i] == *(array+i)' korrekt ist, oder?

    Natürlich kann nicht x == y und x == y-1 gleichzeitig wahr sein.

    Baracke_out schrieb:

    Oder bringe ich hier was durcheinander?

    Ja. Warum soll array[i+1] das Gleiche sein wie *(array+i-1) ? Wenn schon +1 .

    Aber ich sehe nicht, was es hier überhaupt zu diskutieren gibt. Dass array[i] dem Wert *(array+i) entspricht, ist das absolut Naheliegendste. Ein +1 benötigte wieder eine Addition mehr und würde für Verwirrung sorgen. Es wäre nicht einfacher, da man zwei verschiedene Konzepte lernen müsste (direkte Adressierung ist nicht mehr analog zur Verwendung eines Index). Was hätte man für einen Gewinn?



  • Nexus schrieb:

    Was hätte man für einen Gewinn?

    Der "Gewinn" ist vor allem für Anfänger. Wenn man 3 Äpfel vor sich liegen hat, sagt man intuitiv "1. Apfel, 2. Apfel, 3. Apfel" und nicht "0. Apfel, 1. Apfel, 2. Apfel". Gerade für Anfänger ist die 0-Basierung deshalb zuerst unlogisch (die interne Adressierung über Pointeraddition ist ja zu dem Zeitpunkt vollkommen irrelevant).
    Allerdings stimme ich dir zu, dass man sich daran schnell gewöhnt und in vielen Fällen die 0-Basierung schlussendlich logischer und konsistenter ist.


Anmelden zum Antworten