memset - Seite der Dunkelheit



    Nachdem ich den Artikel Die gefährlichste Funktion in der Welt von C / C ++ gelesen hatte , fand ich es nützlich, in das Böse im dunklen Keller der Memsets einzutauchen und einen Nachtrag zu verfassen, um das Wesentliche des Problems näher zu erläutern .

    In der Sprache C wird memset () überall mit vielen Überfüllungen verwendet. Auszug aus C ++ Referenz:
    void * memset (void * ptr, int value, size_t num);
    Speicherblock füllen
    Setzt die ersten num-Bytes des Speicherblocks, auf den ptr zeigt, auf den angegebenen Wert (interpretiert als vorzeichenloses Zeichen).
    Parameter
    ptr - Zeiger auf den zu füllenden Speicherblock.
    value - einzustellender Wert. Der Wert wird als int übergeben, aber die Funktion füllt den Speicherblock mit der vorzeichenlosen Zeichenkonvertierung dieses Werts.
    num - Anzahl der Bytes, die auf den Wert gesetzt werden sollen. size_t ist ein vorzeichenloser ganzzahliger Typ.
    Rückgabewert
    ptr wird zurückgegeben.

    Wie bereits mehrfach erwähnt, gibt es viele Rechen, auf die auch erfahrene Entwickler treten. Aus dem Andrey2008- Artikel, der in einer kurzen Zusammenfassung typischer Fehler beschrieben wird :

    Nr. 1. Wenn Sie versuchen, die Größe eines Arrays oder einer Struktur zu berechnen, verwenden Sie sizeof () nicht für Zeiger auf ein Array / eine Struktur. Sie erhalten statt der Größe des Arrays / der Struktur eine Zeigergröße von 4 oder 8 Byte.

    Nr. 2. Das dritte Argument von memset () akzeptiert die Anzahl der Bytes als Eingabe, nicht die Anzahl der Elemente, unabhängig vom Datentyp. Ich füge beispielsweise hinzu, dass der int-Typ je nach Architektur entweder 4 oder 8 Byte belegen kann. Verwenden Sie in diesem Fall sizeof (int).

    Nummer 3. Verwechseln Sie nicht die Argumente. Die richtige Reihenfolge ist ein Zeiger, ein Wert und eine Länge in Bytes.

    Nummer 4. Verwenden Sie memset nicht, wenn Sie mit Klassenobjekten arbeiten.

    Dies ist jedoch nur die Spitze des Eisbergs.

    Memset-Alternative


    memset ist eine einfache Funktion, bei der der Entwickler alle Funktionen der Computerarchitektur berücksichtigen muss und deren Verwendung gerechtfertigt sein sollte. Betrachten wir zunächst die Alternative = {0} . Anstelle von memset können Sie auf diese Weise beim Kompilieren ein Array oder einen String initialisieren. Dies sollte die Geschwindigkeit des Programms erhöhen, im Gegensatz zu memset (auch ZeroMemory), mit dem Daten zur Laufzeit initialisiert werden. Ich beschloss, es mir anzusehen.

    void doInitialize()
    {
       char p0[25] = {0} ;           // установит все 25 символов в 0
       char p1[25] = "" ;            // установит все 25 символов в 0
       wchar_t p2[25] = {0} ;        // установит 25 символов в 0
       wchar_t p3[25] = L"" ;        // установит все 25 символов в 0
       short        p4[62] = {0}     // установит 62 значения в 0
       int          p5[37] = {-1} ;  // установит значение первого элемента в -1
       unsigned int p6[10] = {89} ;  // установит значение первого элемента 89
    }
    

    C99 [$ 6.7.8 / 21]
    Befinden sich weniger Initialisierer in einer Liste in geschweiften Klammern als Elemente oder Elemente eines Aggregats oder weniger Zeichen in einem Zeichenfolgenliteral, das zum Initialisieren eines Arrays bekannter Größe verwendet wird, als Elemente im Array vorhanden sind, muss der Rest des Aggregats angegeben werden werden implizit wie Objekte mit statischer Speicherdauer initialisiert.

    Gleichzeitig behebt diese Initialisierung die Probleme Nr. 1, Nr. 2, Nr. 3 mit einer Verwechslung von Parametern und Puffergrößen. Das heißt, wir werden das zweite und dritte Argument an einigen Stellen nicht verwechseln, wir müssen die Größe nicht übertragen. Mal sehen, wie Compiler solchen Code transformieren. Ich kann nicht alle Compiler auf Anhieb überprüfen, da sich herausgestellt hat, dass gcc, das in android-ndk-r10c und gcc in Ubuntu 14.04 enthalten ist, zur Hand ist.

    gcc -v
    1) gcc Version 4.9 20140827 (Vorabversion) (GCC)
    2) gcc Version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)

    Schauen wir uns an, wie sich der Compiler auf einem solchen Code verhält:

    void empty_string(){
        int i;
        char p1[25] = {0};
        printf("\np1: ");
        for (i = 0; i < 25; i++)
            printf("%x,",p1[i]);
    }
    

    Ohne Optimierung (-O0) wird die Initialisierung des Arrays in einen solchen Assembler-Code kompiliert (wir durchsuchen die Binärdateien mit objdump):

    gcc -O0, ELF 32-Bit, ARM, EABI5
        83d8:       e3a03000        mov     r3, #0
        83dc:       e50b3024        str     r3, [fp, #-36]  ; 0x24
        83e0:       e24b3020        sub     r3, fp, #32
        83e4:       e3a02000        mov     r2, #0
        83e8:       e5832000        str     r2, [r3]
        83ec:       e2833004        add     r3, r3, #4
        83f0:       e3a02000        mov     r2, #0
        83f4:       e5832000        str     r2, [r3]
        83f8:       e2833004        add     r3, r3, #4
        83fc:       e3a02000        mov     r2, #0
        8400:       e5832000        str     r2, [r3]
        8404:       e2833004        add     r3, r3, #4
        8408:       e3a02000        mov     r2, #0
        840c:       e5832000        str     r2, [r3]
        8410:       e2833004        add     r3, r3, #4
        8414:       e3a02000        mov     r2, #0
        8418:       e5832000        str     r2, [r3]
        841c:       e2833004        add     r3, r3, #4
        8420:       e3a02000        mov     r2, #0
        8424:       e5c32000        strb    r2, [r3]
        8428:       e2833001        add     r3, r3, #1
    


    gcc -O0, ELF 64-Bit, x86-64
      400700:       48 c7 45 d0 00 00 00 00    movq   $0x0,-0x30(%rbp)
      400708:       48 c7 45 d8 00 00 00 00    movq   $0x0,-0x28(%rbp)
      400710:       48 c7 45 e0 00 00 00 00    movq   $0x0,-0x20(%rbp)
      400718:       c6 45 e8 00                movb   $0x0,-0x18(%rbp)
    


    Wie erwartet erhalten wir ohne Optimierung einen Laufzeitcode, der O (n) Prozessorzeit aufnimmt (wobei n die Länge des Puffers ist). Was der Compiler mit der Optimierung (-O3) machen wird, können wir unten sehen.

    gcc -O3, 32-Bit, ARM

    000083ac :
        83ac:       e59f002c        ldr     r0, [pc, #44]   ; 83e0 
        83b0:       e92d4038        push    {r3, r4, r5, lr}
        83b4:       e08f0000        add     r0, pc, r0
        83b8:       ebffffb2        bl      8288 
        83bc:       e59f5020        ldr     r5, [pc, #32]   ; 83e4 
        83c0:       e3a04019        mov     r4, #25
        83c4:       e08f5005        add     r5, pc, r5
        83c8:       e1a00005        mov     r0, r5
        83cc:       e3a01000        mov     r1, #0
        83d0:       ebffffac        bl      8288 
        83d4:       e2544001        subs    r4, r4, #1
        83d8:       1afffffa        bne     83c8 
        83dc:       e8bd8038        pop     {r3, r4, r5, pc}
    gcc -O3, 64-Bit, x86-64
    00000000004006d0 :
      4006d0:       53                      push   %rbx
      4006d1:       be a4 08 40 00          mov    $0x4008a4,%esi
      4006d6:       bf 01 00 00 00          mov    $0x1,%edi
      4006db:       31 c0                   xor    %eax,%eax
      4006dd:       bb 32 00 00 00          mov    $0x32,%ebx
      4006e2:       e8 d9 fd ff ff          callq  4004c0 <__printf_chk@plt>
      4006e7:       66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
      4006ee:       00 00 
      4006f0:       31 d2                   xor    %edx,%edx
      4006f2:       31 c0                   xor    %eax,%eax
      4006f4:       be aa 08 40 00          mov    $0x4008aa,%esi
      4006f9:       bf 01 00 00 00          mov    $0x1,%edi
      4006fe:       e8 bd fd ff ff          callq  4004c0 <__printf_chk@plt>
      400703:       83 eb 01                sub    $0x1,%ebx
      400706:       75 e8                   jne    4006f0 
      400708:       5b                      pop    %rbx
      400709:       c3                      retq   
    


    Wir sehen, dass der Code mit der Laufzeitnullung einfach verschwunden ist, wir haben die versprochene O (1) -Leistung erhalten. Lassen Sie uns herausfinden, woher printf seine Werte bezieht. Wir interessieren uns für dieses Stück:

    83bc:           ldr     r5, [pc, #32]
    83c0:           mov     r4, #25     ;// В r4 записываем количество циклов for, это наш счётчик цикла
    83c4:           add     r5, pc, r5  ;// В r5 записываем текст "%x," как константу, в памяти она хранится как 002c7825
    83c8:           mov     r0, r5      ;// r5 неизменно передаётся в r0 на каждой итерации цикла, это первый параметр printf()
    83cc:           mov     r1, #0      ;// записываем константу 0 (вместо фактического p1[i]) как второй параметр printf()
    83d0:           bl      8288 
    83d4:           subs    r4, r4, #1  ;// Отнимаем единицу в счётчике цикла
    83d8:           bne     83c8   ;// Если не дошли до 0, то переходим на начало цикла 83c8
    

    Das heißt, der Compiler hat das Array einfach verworfen und anstelle seiner Werte 0 als Konstante verwendet, die in der Kompilierungsphase festgelegt wurde. OK, aber was passiert, wenn wir Memset verwenden ? Schauen wir uns ein paar Teile von objdump an, zum Beispiel unter ARM:

    Ohne Optimierung -O0 :

        83d8:       e24b3024        sub     r3, fp, #36     ; 0x24
        83dc:       e1a00003        mov     r0, r3
        83e0:       e3a01000        mov     r1, #0
        83e4:       e3a02019        mov     r2, #25
        83e8:       ebffffa3        bl      827c 

    Mit der Optimierung -O3 :

        83c0:       e58d3004        str     r3, [sp, #4]
        83c4:       e58d3008        str     r3, [sp, #8]
        83c8:       e58d300c        str     r3, [sp, #12]
        83cc:       e58d3010        str     r3, [sp, #16]
        83d0:       e58d3014        str     r3, [sp, #20]
        83d4:       e58d3018        str     r3, [sp, #24]
        83d8:       e5cd301c        strb    r3, [sp, #28]
    

    x86-64
    Ohne Optimierung -O0:
      400816:       ba 19 00 00 00          mov    $0x19,%edx
      40081b:       be 00 00 00 00          mov    $0x0,%esi
      400820:       48 89 c7                mov    %rax,%rdi
      400823:       e8 a8 fc ff ff          callq  4004d0 

    Mit der Optimierung -O3:
      4007f4:       48 c7 04 24 00 00 00 00    movq   $0x0,(%rsp)
      4007fc:       48 c7 44 24 08 00 00 00    movq   $0x0,0x8(%rsp)
      400805:       48 c7 44 24 10 00 00 00    movq   $0x0,0x10(%rsp)
      40080e:       c6 44 24 18 00             movb   $0x0,0x18(%rsp)
    


    Das heißt, bei der Optimierung wird der Memset- Aufruf einfach durch Einfügen in die Zeile entfernt . In solchen Fällen funktioniert memset immer für O (n) -Zeit , aber die Initialisierung mit = {0} während der Optimierung funktioniert für eine Konstante, in unserem Fall ohne CPU-Zyklen, die unverschämt die Existenz des Arrays verwirft und alle seine Elemente ersetzt Nullen. Aber mal sehen, ob dies immer der Fall ist und was passiert, wenn wir nach der Initialisierung einen Wert ungleich Null schreiben? Die Testfunktion sieht folgendermaßen aus:

    void empty_string(){
        int i;
        char p1[25] = {0};
        p1[0] = 65;
        printf("\np1: ");
        for (i = 0; i < 25; i++)
            printf("%x,",p1[i]);
    }
    

    Nach dem Kompilieren erhalten wir einen bereits bekannten Codeblock:

        8404:       e3a02041        mov     r2, #65 ; 0x41
        8408:       e08f0000        add     r0, pc, r0
        840c:       e58d3004        str     r3, [sp, #4]
        8410:       e58d3008        str     r3, [sp, #8]
        8414:       e58d300c        str     r3, [sp, #12]
        8418:       e58d3010        str     r3, [sp, #16]
        841c:       e58d3014        str     r3, [sp, #20]
        8420:       e58d3018        str     r3, [sp, #24]
        8424:       e5cd301c        strb    r3, [sp, #28]
        8428:       e5cd2004        strb    r2, [sp, #4]
    

    x86-64
      4006f8:       48 c7 04 24 00 00 00    movq   $0x0,(%rsp)
      4006ff:       00 
      400700:       48 c7 44 24 08 00 00    movq   $0x0,0x8(%rsp)
      400707:       00 00 
      400709:       48 c7 44 24 10 00 00    movq   $0x0,0x10(%rsp)
      400710:       00 00 
      400712:       c6 44 24 18 00          movb   $0x0,0x18(%rsp)
      400717:       c6 04 24 41             movb   $0x41,(%rsp)
    


    Und es sieht so aus, als hätte der Compiler eine optimierte Version von memset in uns eingefügt . Und mal sehen, was passiert, wenn die Größe des Arrays deutlich zunimmt? Sprich nicht 25 Bytes, sondern 25 Kilobytes!

        83fc:       e24ddc61        sub     sp, sp, #24832  ; 0x6100
        8400:       e24dd0a8        sub     sp, sp, #168    ; 0xa8
        8404:       e3a01000        mov     r1, #0
        8408:       e59f2054        ldr     r2, [pc, #84]   ; 8464 
        840c:       e1a0000d        mov     r0, sp
        8410:       ebffff99        bl      827c 

    x86-64
      400720:       55                      push   %rbp
      400721:       ba a8 61 00 00          mov    $0x61a8,%edx
      400726:       31 f6                   xor    %esi,%esi
      400728:       53                      push   %rbx
      400729:       48 81 ec b8 61 00 00    sub    $0x61b8,%rsp
      400730:       48 89 e7                mov    %rsp,%rdi
      400733:       48 8d ac 24 a8 61 00    lea    0x61a8(%rsp),%rbp
      40073a:       00 
      40073b:       48 89 e3                mov    %rsp,%rbx
      40073e:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
      400745:       00 00 
      400747:       48 89 84 24 a8 61 00    mov    %rax,0x61a8(%rsp)
      40074e:       00 
      40074f:       31 c0                   xor    %eax,%eax
      400751:       e8 8a fd ff ff          callq  4004e0 
      400756:       be 54 09 40 00          mov    $0x400954,%esi
      40075b:       bf 01 00 00 00          mov    $0x1,%edi
      400760:       31 c0                   xor    %eax,%eax
      400762:       c6 04 24 41             movb   $0x41,(%rsp)
      400766:       e8 a5 fd ff ff          callq  400510 <__printf_chk@plt>
      40076b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
      400770:       0f be 13                movsbl (%rbx),%edx
      400773:       31 c0                   xor    %eax,%eax
      400775:       be 5a 09 40 00          mov    $0x40095a,%esi
      40077a:       bf 01 00 00 00          mov    $0x1,%edi
      40077f:       48 83 c3 01             add    $0x1,%rbx
      400783:       e8 88 fd ff ff          callq  400510 <__printf_chk@plt>
      400788:       48 39 eb                cmp    %rbp,%rbx
      40078b:       75 e3                   jne    400770 
      40078d:       48 8b 84 24 a8 61 00    mov    0x61a8(%rsp),%rax
      400794:       00 
      400795:       64 48 33 04 25 28 00    xor    %fs:0x28,%rax
      40079c:       00 00 
      40079e:       75 0a                   jne    4007aa 
      4007a0:       48 81 c4 b8 61 00 00    add    $0x61b8,%rsp
      4007a7:       5b                      pop    %rbx
      4007a8:       5d                      pop    %rbp
      4007a9:       c3                      retq
    


    Nein, so was!

    Line = {0} geht zur Seite der Dunkelheit, memset freut sich!

    Wir werden jedoch nicht vergessen, haben uns jedoch entschlossen, das Problem mit den Parametern zu lösen, und es wird uns nicht gelingen, die Argumente zu verwechseln.

    Zeileninitialisierung


    Es ist auch nicht überflüssig, die Option zum Initialisieren des Arrays = "" in Betracht zu ziehen . C verwendet nullterminierte Zeichenfolgen, dh das erste Zeichen mit einem Bytewert von 0x00 bedeutet das Ende der Zeichenfolge. Um eine Zeile zu initialisieren, ist es daher nicht sinnvoll, alle Elemente zu nullen. Es reicht aus, nur das erste zurückzusetzen. Hier sind einige Möglichkeiten zum Initialisieren einer leeren Zeichenfolge:

    void doInitializeCString()
    {
       char p0[25] = {0} ;           // установит все символы в 0
       char p1[25] = "" ;            // установит все символы в 0
       char p2[25] ;
       p2[0] = 0 ;                   // установит первый символ в 0
       char p3[25] ;
       memset(p3, 0, sizeof(p3)) ;   // установит 25 символов в 0
       char p4[25] ;
       strcpy(p4, "") ;              // установит первый символ в 0
       char *p5 = (char *) calloc(25, sizeof(char)) ;  // установит все символы в 0
    }
    

    Der zuverlässigste Weg, wie die Initialisierung funktioniert, besteht darin , objdump erneut zu analysieren. Ohne Optimierung sehen wir nichts Besonderes, alles ist dort ähnlich = {0} , wir werden es sofort mit der Option -O3 berücksichtigen. Also kompilieren wir unter ARM:
    Hier ist eine Funktion
    void empty_string(){
        int i;
        char p1[25] = "";
        printf("\np1: ");
        for (i = 0; i < 25; i++)
            printf("%x,",p1[i]);
    }
    


    Und plötzlich erhalten wir die Nullsetzung aller Elemente des Arrays.

        83c0:       e58d3004        str     r3, [sp, #4]
        83c4:       e58d3008        str     r3, [sp, #8]
        83c8:       e58d300c        str     r3, [sp, #12]
        83cc:       e58d3010        str     r3, [sp, #16]
        83d0:       e58d3014        str     r3, [sp, #20]
        83d4:       e58d3018        str     r3, [sp, #24]
        83d8:       e5cd301c        strb    r3, [sp, #28]
    

    x86-64
      400768:       48 c7 04 24 00 00 00 00    movq   $0x0,(%rsp)
      400770:       48 c7 44 24 08 00 00 00    movq   $0x0,0x8(%rsp)
      400779:       48 c7 44 24 10 00 00 00    movq   $0x0,0x10(%rsp)
      400782:       c6 44 24 18 00          movb   $0x0,0x18(%rsp)
    


    Na ja Warum alle nicht verwendeten Zeichen in einer nullterminierten Zeichenfolge aufheben?! Es reicht aus, ein einzelnes Byte zurückzusetzen. Hmm, und wenn es 25 Tausend Bytes gibt, was wird es tun? Und hier ist was:

        8474:       e24ddc61        sub     sp, sp, #24832  ; 0x6100
        8478:       e24dd0a8        sub     sp, sp, #168    ; 0xa8
        847c:       e3a0c000        mov     ip, #0
        8480:       e28d3f6a        add     r3, sp, #424    ; 0x1a8
        8484:       e1a0100c        mov     r1, ip
        8488:       e59f204c        ldr     r2, [pc, #76]   ; 84dc 
        848c:       e28d0004        add     r0, sp, #4
        8490:       e503c1a8        str     ip, [r3, #-424] ; 0x1a8
        8494:       ebffff78        bl      827c 

    x86-64
    00000000004007b0 :
      4007b0:       55                      push   %rbp
      4007b1:       ba a0 61 00 00          mov    $0x61a0,%edx
      4007b6:       31 f6                   xor    %esi,%esi
      4007b8:       53                      push   %rbx
      4007b9:       48 81 ec b8 61 00 00    sub    $0x61b8,%rsp
      4007c0:       48 8d 7c 24 08          lea    0x8(%rsp),%rdi
      4007c5:       48 8d ac 24 a8 61 00    lea    0x61a8(%rsp),%rbp
      4007cc:       00 
      4007cd:       48 c7 04 24 00 00 00    movq   $0x0,(%rsp)
      4007d4:       00 
      4007d5:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
      4007dc:       00 00 
      4007de:       48 89 84 24 a8 61 00    mov    %rax,0x61a8(%rsp)
      4007e5:       00 
      4007e6:       31 c0                   xor    %eax,%eax
      4007e8:       48 89 e3                mov    %rsp,%rbx
      4007eb:       e8 f0 fc ff ff          callq  4004e0 
      4007f0:       be 54 09 40 00          mov    $0x400954,%esi
      4007f5:       bf 01 00 00 00          mov    $0x1,%edi
      4007fa:       31 c0                   xor    %eax,%eax
      4007fc:       e8 0f fd ff ff          callq  400510 <__printf_chk@plt>
      400801:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)
      400808:       0f be 13                movsbl (%rbx),%edx
      40080b:       31 c0                   xor    %eax,%eax
      40080d:       be 5a 09 40 00          mov    $0x40095a,%esi
      400812:       bf 01 00 00 00          mov    $0x1,%edi
      400817:       48 83 c3 01             add    $0x1,%rbx
      40081b:       e8 f0 fc ff ff          callq  400510 <__printf_chk@plt>
      400820:       48 39 eb                cmp    %rbp,%rbx
      400823:       75 e3                   jne    400808 
      400825:       48 8b 84 24 a8 61 00    mov    0x61a8(%rsp),%rax
      40082c:       00 
      40082d:       64 48 33 04 25 28 00    xor    %fs:0x28,%rax
      400834:       00 00 
      400836:       75 0a                   jne    400842 
      400838:       48 81 c4 b8 61 00 00    add    $0x61b8,%rsp
      40083f:       5b                      pop    %rbx
      400840:       5d                      pop    %rbp
      400841:       c3                      retq
    


    Es sieht so aus, als würde uns ein dunkles Memset verfolgen. Wenn Sie immer noch gegen die Dunkelheit kämpfen möchten, sollten Sie erwähnen, welche anderen Fallen auf Sie warten.



    Memset initialisiert möglicherweise Zahlen mit falschen Werten


    Wenn Sie ein Array von Ganzzahlen mit Werten ungleich Null füllen möchten, überprüfen Sie die Daten zum Füllen von Bytes.

    void doInitializeToMistakenValues()
    {
       char           pChar[25] ;
       unsigned char  pUChar[25] ;
       short          pShort[25] ;
       unsigned short pUShort[25] ;
       int            pInt[25] ;
       unsigned int   pUInt[25] ;
       // Значения 2-байтовых и 4-байтовых элементов будут отличны от единицы
       memset(pChar,   1,  sizeof(pChar)) ;   // 1
       memset(pUChar,  1,  sizeof(pUChar)) ;  // 1
       memset(pShort,  1,  sizeof(pShort)) ;  // 257
       memset(pUShort, 1,  sizeof(pUShort)) ; // 257
       memset(pInt,    1,  sizeof(pInt)) ;    // 16843009
       memset(pUInt,   1,  sizeof(pUInt)) ;   // 16843009
       // Значения unsigned массивов заполнится байтами 0xFF
       memset(pChar,   -1, sizeof(pChar)) ;   // -1
       memset(pUChar,  -1, sizeof(pUChar)) ;  // 255
       memset(pShort,  -1, sizeof(pShort)) ;  // -1
       memset(pUShort, -1, sizeof(pUShort)) ; // 65535
       memset(pInt,    -1, sizeof(pInt)) ;    // -1
       memset(pUInt,   -1, sizeof(pUInt)) ;   // 4294967295
    }
    

    Mal sehen, wie es ausgeht. Angenommen, wir haben ein int-Array. Übergeben Sie den zweiten Parameter an die Einheit. Was passiert?

    Und hier ist was:

    0x01010101 - In hexadezimaler Schreibweise wird jedes Byte mit einer Einheit gefüllt, und der richtige Wert
    0x00000001 kann mit der Memset-Funktion nicht festgelegt werden. Aber eigentlich ist das kein Bug, sondern ein Feature.

    Das Unwissen über diese Funktionen führt zu unvorhersehbaren Fehlern.

    memset legt möglicherweise einen ungültigen Wert fest


    Wenn wir die Bytes -1 auf doppelte Elemente setzen, erhalten wir den Wert Not-A-Number (NaN), und nach nachfolgenden Berechnungen gibt jede Operation mit dem Wert NaN NaN zurück, wodurch die gesamte Berechnungskette unterbrochen wird.

    Ebenso ist das Setzen von -1 auf den Bool-Typ falsch und wird formal weder wahr noch falsch sein. Obwohl es sich in den meisten Fällen als wahr verhält. In den meisten Fällen ...

    Und schließlich funktioniert memset nur mit einfachen Datenstrukturen . Verwenden Sie memset niemals mit verwalteten Datenstrukturen. Diese Funktion ist nur für Operationen auf niedriger Ebene vorgesehen.


    Der Artikel verwendet Materialien Memset ist böse .

    Informieren Sie sich auch über Sicherheitslücken in der printf-
    Funktion .

    Jetzt auch beliebt: