String-Länge



  • DirkB schrieb:

    Es kann aber ein Ergebnis von strncpy sein. 🙂

    Klar. Aber da muss doch dann der Programmierer drauf achten, dass sein String ein '\0' bekommt - meine Meinung.



  • R231SL schrieb:

    Wie könnte ich das zu meiner gewünschten Funktion umbasteln?

    So:

    #include <stdio.h>
    #include <string.h>
    
    #define BUFLEN 10
    
    int string_len(const char s[], int buf_len)
    {
        int i;
        for (i=0; i<buf_len; ++i)
        {
            if (s[i] == '\0') return i;
        }
        return -1; /* Fehler */
    }
    
    int main( void )
    {
        char string[BUFLEN] = "Das ist ein Teststring";
        size_t strlen_laenge;
        int string_len_laenge;
    
        strlen_laenge = strlen(string);
        printf("Der String \"%s\" hat %d Zeichen\n",string, strlen_laenge);
    
        string_len_laenge = string_len (string,BUFLEN);
        if (string_len_laenge < 0)
        {
            puts("Der String ist gar kein String");
        }
        else
        {
            printf("Der String \"%s\" hat %d Zeichen\n",string, string_len_laenge);
        }
    
        return 0;
    }
    

    Ich denke, damit ist auch Deine Frage nach buf_len beantwortet.

    viele grüße
    ralph



  • rkhb schrieb:

    #define BUFLEN 10
    
        char string[BUFLEN] = "Das ist ein Teststring";
    

    Undefiniertes Verhalten.

    rkhb schrieb:

    size_t strlen_laenge;
    
        printf("Der String \"%s\" hat %d Zeichen\n",string, strlen_laenge);
    

    Undefiniertes Verhalten.



  • Wutz schrieb:

    ...
    Undefiniertes Verhalten.
    ...
    Undefiniertes Verhalten.

    Aha. Und? Außerdem stimmt es nicht:

    1. "An array of character type may be initialized by a character string literal, optionally enclosed in braces. Successive characters of the character string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array." (ISO/IEC 8999:TC3, S.126 Rn 14).

    Das heißt: Das Array wird angelegt und sukzessive soweit gefüllt, wie Platz ist. Deswegen geben GCC und MSVC auch "nur" eine Warnung heraus.

    1. %s: "If no l length modifier is present, the argument shall be a pointer to the initial element of an array of character type." (aaO. S. 279)

    %d: "The int argument is converted to signed decimal in the style [−]dddd." (aaO. S. 277). Nun gut, dem C99-Standard zuliebe hätte ich %zd schreiben müssen, aber das hätte der MSVC im C-Modus nicht geschluckt.

    viele grüße
    ralph


  • Mod

    Du kannst nicht beides haben. Entweder schneidet deine Zuweisung nicht das Nullzeichen ab oder alle deine Operationen darauf sind undefiniert. Das erstere ist dir tatsächlich vom C-Standard so garantiert, das bedeutet der Rest des Programms ist voller undefinierten Verhaltens.



  • char szString[] = "Das ist ein String";
    

    So deklariert hat der String genau die Länge der angegeben Zeichen
    plus noch einen Null-Terminierer.

    Man muss sich also nicht darum bemühen, die Länge des Strings bei
    der Deklaration zu wissen und hat keine Probleme damit, dass der
    String länger als die Array Größe ist. 🙂



  • SeppJ schrieb:

    Du kannst nicht beides haben. Entweder schneidet deine Zuweisung nicht das Nullzeichen ab oder alle deine Operationen darauf sind undefiniert. Das erstere ist dir tatsächlich vom C-Standard so garantiert, das bedeutet der Rest des Programms ist voller undefinierten Verhaltens.

    Warum sollte ich beides haben wollen? Ich wollte dem OP nur schnell vor Augen führen, wann ein Bound-Checking nützen kann, denn danach hatte er gefragt. Die Logik Deiner Ausführungen erschließt sich mir nicht. Wenn etwas vom C-Standard garantiert ist, dann bedeutet das doch, dass dieser Teil definiert ist, und der Rest des Programms muss deswegen nicht voller undefinierten Verhaltens sein.

    Dass printf bei %s einen "pointer to the initial element of an array of character type" erwartet, heißt doch eindeutig, dass es sich bei dem Array nicht um einen richtigen C-String handeln muss, bzw. dass auf C-String geprüft wird. Es wird Speicher gelesen und ausgegeben - bis zur ersten Null, und die kann weit hinter den Grenzen des Arrays liegen. Das Verhalten ist zwar unerwünscht, aber nicht undefiniert.

    viele grüße
    ralph



  • @chara
    Es ist hier aber beabsichtigt, dass da kein Platz für eine Terminierung ist. 😉



  • int string_len(const char s[], int buf_len)
    {
        if ((!s) || (!buf_len))
            return -1;
    
        int i;
        for (i = 0; i < buf_len; i++) {
            if (s[i] == '\0')
                return i;
        }   
    
        return -1;
    }
    

    So würde ich es machen.

    Ist eine Terminierung innerhalb des Strings vorhanden, dann wird die Anzahl der Zeichen bis dahin zurückgegeben. Ist keine Vorhanden, dann wird -1 zurückgegeben, weil irgendwann die als Argument übergebene Grenze erreicht wurde. Außerdem werden noch die beiden Funktionsargumente überprüft (Null-Zeiger und 0-Größe).

    So wäre doch die Aufgabenstellung des Threaderstellers gelöst oder nicht?!



  • chara schrieb:

    Außerdem werden noch die beiden Funktionsargumente überprüft (Null-Zeiger und 0-Größe).

    Die 0-Größe bei buf_len wird auch in der for-Schleife abgefangen.
    Und bis auf den NULL-Zeifer-Test ist es doch identisch mit der Version von rkhb.

    (Ich hätte beim NULL-Zeiger -2 zurück gegeben)



  • chara schrieb:

    So würde ich es machen.

    Und ich so 😉

    int string_len( const char * buf, int buf_len ) {
    	int i;
    	for( i = 0; *buf && buf_len; buf_len--, i++, buf++ );
    	return buf_len ? i : -1;
    }
    

  • Mod

    rkhb schrieb:

    Dass printf bei %s einen "pointer to the initial element of an array of character type" erwartet, heißt doch eindeutig, dass es sich bei dem Array nicht um einen richtigen C-String handeln muss, bzw. dass auf C-String geprüft wird. Es wird Speicher gelesen und ausgegeben - bis zur ersten Null, und die kann weit hinter den Grenzen des Arrays liegen. Das Verhalten ist zwar unerwünscht, aber nicht undefiniert.

    Doch, das ist undefiniert ohne Ende, auf wer weiß was für Speicher zuzugreifen.



  • rkhb schrieb:

    Aha. Und? Außerdem stimmt es nicht:

    Du hast keine Ahnung, wovon du redest.

    rkhb schrieb:

    Das heißt: Das Array wird angelegt und sukzessive soweit gefüllt, wie Platz ist. Deswegen geben GCC und MSVC auch "nur" eine Warnung heraus.

    Standardgemäß müssen Compiler nur bei constraint-Verletzungen und Syntaxfehler melden, d.h. also nicht bei UB; von "nur Warnungen statt Fehler" auf definiertes Verhalten zu schließen ist schlichtweg Unsinn, zumal der Standard solcherlei Abstufungen der Meldungen überhaupt nicht vorsieht.
    Außerdem hast du nicht weitergelesen im (Standard)Draft, da heißt es nämlich noch " Characters from the array are written up to (but not including) a terminating null character ", und das offenbart ganz eindeutig deine String-Definition bei anschließender Verwendung in printf als UB. Wenn man schon liest und zitiert, dann vollständig und nicht sinnentstellend, aber das kannst du auch nicht.

    rkhb schrieb:

    %d: "The int argument is converted to signed decimal in the style [−]dddd." (aaO. S. 277).

    %d ist für size_t UB, da hast du auch schon wieder falsch gelesen. Die default argument promotion wandelt auf signed int, size_t ist aber immer als unsigned integral type definiert, die passen also nie zusammen:
    " If a conversion specification is invalid, the behavior in undefined ".

    rkhb schrieb:

    Nun gut, dem C99-Standard zuliebe hätte ich %zd schreiben müssen, aber das hätte der MSVC im C-Modus nicht geschluckt.

    Für C99 heißt es für size_t %zu und nicht falsch wie bei dir %zd, was für signed Typen der gleichen Größe wie size_t definiert ist und nicht für size_t selbst.

    rkhb schrieb:

    Ich wollte dem OP nur schnell

    Ja das ist die Krux der Google-Generation, nur eben schnell mal suchen, und wenn die Top 3 Antworten nicht sofort die Lösungen liefern, heißt es gleich: "im Internet steht nichts, also kann es auch nicht sein..."



  • Danke für die Hilfe!

    Ich habe jetzt versucht Ralphs code bisschen zu modifizieren damit ich selbst den string eingeben kann.

    #include <stdio.h> 
    #include <string.h> 
    
    #define BUFLEN 100 
    #define NO_STRING -1
    
    int string_len(const char s[], int buf_len) 
    { 
        int i; 
        for (i=0; i<buf_len; ++i) 
        { 
            if (s[i] == '\0') return i; 
        } 
        return NO_STRING;  
    } 
    
    int main( void ) 
    { 
        char string[BUFLEN]; 
        size_t strlen_laenge; 
        int string_len_laenge; 
    
        printf("String eingeben:");
        fgets(string,100,stdin);
    
        string_len_laenge = string_len (string,BUFLEN); 
        if (string_len_laenge < 0) 
        { 
            puts("Der String ist gar kein String"); 
        } 
        else 
        { 
            printf("Der String \"%s\" hat %d Zeichen\n",string, string_len_laenge); 
        } 
    
        return 0; 
    }
    

    Nun zeigt er mir aber leider eine Stelle zu viel an. Wird jetzt etwa die null am Ende mitgezählt?



  • R231SL schrieb:

    Nun zeigt er mir aber leider eine Stelle zu viel an. Wird jetzt etwa die null am Ende mitgezählt?

    Nein, die Null wird nicht mit gezählt, aber das '\n' von der Entertaste.
    Das kannst du auch daran erkennen, das die Ausgabe " hat ... Zeichen" in einer neuen Zeile steht.
    Steht auch so in der Referenz zu fgets.

    Und ersetze die 100 bei fgets durch dein BUFLEN. 🙂


Anmelden zum Antworten