Pointer als argument übergeben ?!



  • Warnt dein Compiler dich bei

    char *a = "mustersmann"; /* ARGH! */
    

    nicht, dass ein const-Qualifier weggecastet wird? Ein String-Literal ist eine Konstante; du kannst nicht darin herumschreiben. Einen Zeiger-auf-Variable darauf zu nehmen, ist für mich schon als solches ein Fehler, und durch einen solchen Zeiger in einer Konstante herumzuschreiben, erzeugt undefiniertes Verhalten. Du musst den String zunächst in Speicher legen, der dir auch gehört - wie etwa in ein Stack-Array der Form

    char a[] = "mustersmann";
    

    oder einen dynamisch angeforderten Speicherbereich ausreichender Länge:

    char *a = malloc(sizeof("mustersmann"));
    strcpy(a, "mustersmann");
    
    ...
    
    free(a);
    


  • char *a="mustersmann";
    

    Definiert einen Zeiger a und initialisiert ihn mit der Adresse eines Stringliterals. Stringliterale können vom Compiler in einem Nur-Lese-Speicher abgelegt werden, sodass ein Schreiben/Ändern darauf zu einem Fehler führt/führen kann.

    char a[]="mustersmann";
    

    Definiert einen auch schreibbaren Speicherbereich von mind. strlen("mustersmann")+1 Bytes und kopiert "mustersmann" + '\0' dort hinein. a ist eine Referenz auf den Beginn dieses Speicherbereiches, d.h. du kannst hier was ändern ohne dass ein Fehler wie oben auftritt.

    Prinzipiell kann man sagen, dass ein mit einem Stringliteral initialisierter Zeiger implizit const ist, also:

    char *a="mustersmann";
    

    bedeutet:

    const char *a="mustersmann";
    

    Wenn man gleich die const Variante wählt ist dies zunächst mal lesbarer, auch ist die Chance, vom Compiler eine Warnung bei Änderungen zu bekommen, größer.



  • Und hier noch eine Möglichkeit, wie du etwas eleganter den Buchstaben entfernen kannst:

    void strDelChar(char *s, int index)
    {
    	int i;
    
    	for (i = index; i < strlen(s); ++i)
    	{
    		char x = s[i];
    		s[i] = s[i + 1];
    		s[i + 1] = x;
    	}
    }
    


  • Alles klar, danke für die ausführlichen Erklärungen! Dann gewöhn ich mir das ab einen Zeiger zu definieren mit einer adresse eines stringliterals, denn da darf man nicht rumpfuschen =). danke nochmal

    Liebe Grüße
    🙂



  • Naja, wenn wir schon so weit sind:

    void strDelChar(char *s, int index) {
      char *p = s + index;
      memmove(p, p + 1, strlen(p));
    }
    

    Wenn man es wie gjghjghj von Hand machen will, wäre es sinnvoll, die Länge des Strings nicht in jedem Schleifendurchlauf neu zu nehmen - strlen ist O(n) mit der Länge des Strings, dementsprechend steigt die Komplexität des Algorithmus so unnötig auf O(n2).



  • ok. rein aus interesse nun. memmove(p, p + 1, strlen(p)); was macht das genau ?



  • Dave01 schrieb:

    ok. rein aus interesse nun. memmove(p, p + 1, strlen(p)); was macht das genau ?

    Zeit verschwenden?
    Im Gegensatz zu memcpy und strcpy dürfen sich bei memmove Quellgebiet und Zielgebiet überlappen, deswegen kann man es benutzen, kann aber nicht strcpy benutzen, deswegen der strlen-Aufruf.



  • Hmm...das kommt drauf an. Nach meinen Messungen hat die memmove-Variante vor Handarbeit bei längeren Strings einen Vorteil, wobei das natürlich compilerabhängig sein dürfte (ich benutze gcc 4.5.2). Folgendes:

    #include <stdio.h>
    #include <string.h>
    #include <time.h>
    
    void strDelChar(char *s, int index) {
      char *p = s + index;
      memmove(p, p + 1, strlen(p));
    }
    
    void strDelChar2(char *s, int index) {
      char *p = s + index;
      char *q = p + 1;
    
      while(*p++ = *q++);
    }
    
    #define ITERATIONS 100000000
    #define STRING "mustersmann"
    
    int main(void) {
      clock_t bench[3];
      int i;
    
      bench[0] = clock();
    
      for(i = 0; i < ITERATIONS; ++i) {
        char a[] = STRING;
        strDelChar(a, 6);
      }
    
      bench[1] = clock();
    
      for(i = 0; i < ITERATIONS; ++i) {
        char a[] = STRING;
        strDelChar2(a, 6);
      }
    
      bench[2] = clock();
    
      printf("%g\n%g\n",
             (double) (bench[1] - bench[0]) / CLOCKS_PER_SEC,
             (double) (bench[2] - bench[1]) / CLOCKS_PER_SEC);
    
      return 0;
    }
    

    gibt mit -O2 hier

    1.65
    0.83
    

    Das selbe mit

    #define STRING "mustersmannfooooooooooooooooooooooooooo"
    

    gibt

    2.84
    2.97
    

    und mit

    #define STRING "mustersmannfooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
    

    kriege ich

    5.38
    12.16
    

    . Wahrscheinlich könnte man die Tricks, die glibc für memmove (und womöglich auch strlen) intern benutzt, plattformabhängig nachbauen, aber ich traue mir nicht zu, das aus dem Stand effizienter hinzukriegen, und es wäre bei einer eventuellen Portierung zusätzlicher Aufwand.



  • seldon schrieb:

    Hmm...das kommt drauf an. Nach meinen Messungen hat die memmove-Variante vor Handarbeit bei längeren Strings einen Vorteil,

    Ja, klar. Der darf sich erlauben, pro Schleifendurchlauf mindestens 4 Bytes zu behandeln. Und strlen auch.



  • Ich muss zu meiner Schande gestehen, dass mir bis eben nicht klar war, wie strlen blockweise prüfen könnte; jetzt hab ich es mal im Code nachgeschlagen. In eglibc 2.11 verwendet strlen auf den am häufigsten vorkommenden Plattformen handoptimierten Assembler-Code; auf x86 läuft es auf repnz hinaus, unter x86-64 ist der Code vollgestopft mit SSE. Auf Plattformen, für die kein Assembler-Code vorhanden ist, benutzt strlen eine Heuristik, um Batzen auf einmal zu prüfen:

    unsigned long int longword, himagic, lomagic;
    
      /* ... */
    
      himagic = 0x80808080L;
      lomagic = 0x01010101L;
    
      /* ... ggf. auf 64 Bit erweitern ... */
    
      /* Instead of the traditional loop which tests each character,
         we will test a longword at a time.  The tricky part is testing
         if *any of the four* bytes in the longword in question are zero.  */
      for (;;)
        {
          longword = *longword_ptr++;
    
          if (((longword - lomagic) & ~longword & himagic) != 0)
            {
              /* Which of the bytes was the zero?  If none of them were, it was
                 a misfire; continue the search.  */
    

    Da wär ich nicht drauf gekommen.

    Interessant ist auch, dass memmove, wenn es kann, den Speicher nicht nur wort-, sondern gleich speicherseitenweise verschiebt. Wobei das für diesen speziellen Anwendungsfall vermutlich keine Auswirkungen hat.


Anmelden zum Antworten