Strings in Ganzzahlen umwandeln



  • Hallo,
    ich bin recht neu im Umgang mit C. Daher verstehe ich die ein oder andere Funktion nicht - in diesem Fall, frage ich mich, warum mein Code funktioniert.

    Die Idee war, dass ich 2 Argumente an meine Funktion übergebe (z.B. ./myFunc 5 8)und mir die Summe als Ganzzahl zurückgegebe. Ich habe es mit diesem Code versucht und es funktioniert. Aber warum?

    int main(int argc,char* argv[]){

    char* random;
    int z=0,y=0;

    z=strtol(argv[1],&random,10);
    y=strtol(argv[2],&random,10);
    printf("%d",z+y);

    return EXIT_SUCCESS;
    }

    Ich kenne lediglich die Syntax für die Funktion strtol.
    Wie funktioniert die Funktion?
    argv[1] zeigt auf die Adresse meiner Eingabe.
    argv[2] zeigt auf die Adresse meiner Eingabe.

    So wie ich das verstanden habe, zeigt &random auf die Adresse eines Pointers.
    Und die 10 gibt die Basis an, auf die ich mich beziehe. (z.B. 16=Hex)

    Ich habe woanders erfahren, dass auch y=strol(argv[2],NULL,10) ebenfalls möglich ist. Jetzt weiß ich gar nicht mehr weiter. Ich dachte das 2. Argument muss auf die Adresse eines Pointers zeigen. Hoffe jemand kann mir die Theorie dahinter erklären. Lg





  • strtol ist eine Standardfunktion. Und diese sind sehr gut dokumentiert. Z.B
    http://www.cplusplus.com/reference/cstdlib/strtol/?kw=strtoloder
    http://en.cppreference.com/w/c/string/byte/strtol

    Da steht dann auch, dass der zweite Parameter NULL sein kann, wenn man ihn nicht braucht.

    Der dient dazu, um zu schauen, wann/warum/wo die Funktion die Erkennung der Zahl abgebrochen hat.
    Damit kannst du Fehleingabe erkennen oder den String weiter parsen.



  • Hab mir das jetzt durchgelesen. Sind nur noch mehr Fragen entstanden. Was ich der Dokumentation aber entnehmen kann, ist, dass mein Ruckgabewert kein Int, sondern besser ein long int sein sollte. Außerdem, dass die Funktion alle Leerzeichen zwischen den Argumenten entfernt und der **char auf das erste Zeichen nach meinem ersten Argument zeigt und somit einen Endwert fürs Lesen des ersten Argument erzeugt. Sprich:
    ./myfun 123 45a

    1. entfernen aller Leerzeichen ./myfun12345a
    2. der **char setzt setzt seinen endmarker auf das ehemalige Leerzeichen ,somit auf 4. Und beginnt dann von 1 bis 3 zu lesen. Dies wiederholt er beim 2. Argument. Sieht aber das a als Fehler an und hört dann auf, bevor die Adresse nach dem a erreicht worden ist. Warum ich dass mit einem nicht initialisierten pointer machen kann und was NULL damit zu tun hat "wenn man es nicht verwendet" wird wohl ein Rätsel bleiben


  • Motion4Life schrieb:

    Warum ich dass mit einem nicht initialisierten pointer machen kann und was NULL damit zu tun hat "wenn man es nicht verwendet" wird wohl ein Rätsel bleiben

    Was davor in dem Pointer war ist egal. Es wird einfach überschrieben. strtol bekommt ja die Adresse des pointers (welche ein char** ist). Übergibst du stattdessen NULL oder 0, wird strtol das nicht als char** interpretieren, sondern als Information, dass es dir egal ist, bei welchem Zeichen strtol abbricht.

    Eine strtol-Variante:

    long int
    strtol(
        char *string,       /* String of ASCII digits, possibly
                     * preceded by white space.  For bases
                     * greater than 10, either lower- or
                     * upper-case digits may be used.
                     */
        char **endPtr,      /* Where to store address of terminating
                     * character, or NULL. */
        int base            /* Base for conversion.  Must be less
                     * than 37.  If 0, then the base is chosen
                     * from the leading characters of string:
                     * "0x" means hex, "0" means octal, anything
                     * else means decimal.
                     */
    )
    {
        register char *p;
        int result;
    
        /*
         * Skip any leading blanks.
         */
        p = string;
        while (isspace(*p)) {
        p += 1;
        }
    
        /*
         * Check for a sign.
         */
        if (*p == '-') {
        p += 1;
        result = -1*(strtoul(p, endPtr, base));
        } else {
        if (*p == '+') {
            p += 1;
        }
        result = strtoul(p, endPtr, base);
        }
        if ((result == 0) && (endPtr != 0) && (*endPtr == p)) {
        *endPtr = string;
        }
        return result;
    }
    

    Und das noch:

    /* 
     * strtoul.c --
     *
     *	Source code for the "strtoul" library procedure.
     *
     * Copyright (c) 1988 The Regents of the University of California.
     * Copyright (c) 1994 Sun Microsystems, Inc.
     *
     * See the file "license.terms" for information on usage and redistribution
     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
     *
     * RCS: @(#) $Id: strtoul.c,v 1.1.1.3 2003/03/06 00:09:04 landonf Exp $
     */
    
    #include "tclInt.h"
    #include "tclPort.h"
    
    /*
     * The table below is used to convert from ASCII digits to a
     * numerical equivalent.  It maps from '0' through 'z' to integers
     * (100 for non-digit characters).
     */
    
    static char cvtIn[] = {
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9,		/* '0' - '9' */
        100, 100, 100, 100, 100, 100, 100,		/* punctuation */
        10, 11, 12, 13, 14, 15, 16, 17, 18, 19,	/* 'A' - 'Z' */
        20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
        30, 31, 32, 33, 34, 35,
        100, 100, 100, 100, 100, 100,		/* punctuation */
        10, 11, 12, 13, 14, 15, 16, 17, 18, 19,	/* 'a' - 'z' */
        20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
        30, 31, 32, 33, 34, 35};
    
    /*
     *----------------------------------------------------------------------
     *
     * strtoul --
     *
     *	Convert an ASCII string into an integer.
     *
     * Results:
     *	The return value is the integer equivalent of string.  If endPtr
     *	is non-NULL, then *endPtr is filled in with the character
     *	after the last one that was part of the integer.  If string
     *	doesn't contain a valid integer value, then zero is returned
     *	and *endPtr is set to string.
     *
     * Side effects:
     *	None.
     *
     *----------------------------------------------------------------------
     */
    
    unsigned long int
    strtoul(string, endPtr, base)
        CONST char *string;		/* String of ASCII digits, possibly
    				 * preceded by white space.  For bases
    				 * greater than 10, either lower- or
    				 * upper-case digits may be used.
    				 */
        char **endPtr;		/* Where to store address of terminating
    				 * character, or NULL. */
        int base;			/* Base for conversion.  Must be less
    				 * than 37.  If 0, then the base is chosen
    				 * from the leading characters of string:
    				 * "0x" means hex, "0" means octal, anything
    				 * else means decimal.
    				 */
    {
        register CONST char *p;
        register unsigned long int result = 0;
        register unsigned digit;
        int anyDigits = 0;
        int negative=0;
        int overflow=0;
    
        /*
         * Skip any leading blanks.
         */
    
        p = string;
        while (isspace(UCHAR(*p))) {
    	p += 1;
        }
        if (*p == '-') {
            negative = 1;
            p += 1;
        } else {
            if (*p == '+') {
                p += 1;
            }
        }
    
        /*
         * If no base was provided, pick one from the leading characters
         * of the string.
         */
    
        if (base == 0)
        {
    	if (*p == '0') {
    	    p += 1;
    	    if ((*p == 'x') || (*p == 'X')) {
    		p += 1;
    		base = 16;
    	    } else {
    
    		/*
    		 * Must set anyDigits here, otherwise "0" produces a
    		 * "no digits" error.
    		 */
    
    		anyDigits = 1;
    		base = 8;
    	    }
    	}
    	else base = 10;
        } else if (base == 16) {
    
    	/*
    	 * Skip a leading "0x" from hex numbers.
    	 */
    
    	if ((p[0] == '0') && ((p[1] == 'x') || (p[1] == 'X'))) {
    	    p += 2;
    	}
        }
    
        /*
         * Sorry this code is so messy, but speed seems important.  Do
         * different things for base 8, 10, 16, and other.
         */
    
        if (base == 8) {
    	unsigned long maxres = ULONG_MAX >> 3;
    	for ( ; ; p += 1) {
    	    digit = *p - '0';
    	    if (digit > 7) {
    		break;
    	    }
    	    if (result > maxres) { overflow = 1; }
    	    result = (result << 3);
    	    if (digit > (ULONG_MAX - result)) { overflow = 1; }
    	    result += digit;
    	    anyDigits = 1;
    	}
        } else if (base == 10) {
    	unsigned long maxres = ULONG_MAX / 10;
    	for ( ; ; p += 1) {
    	    digit = *p - '0';
    	    if (digit > 9) {
    		break;
    	    }
    	    if (result > maxres) { overflow = 1; }
    	    result *= 10;
    	    if (digit > (ULONG_MAX - result)) { overflow = 1; }
    	    result += digit;
    	    anyDigits = 1;
    	}
        } else if (base == 16) {
    	unsigned long maxres = ULONG_MAX >> 4;
    	for ( ; ; p += 1) {
    	    digit = *p - '0';
    	    if (digit > ('z' - '0')) {
    		break;
    	    }
    	    digit = cvtIn[digit];
    	    if (digit > 15) {
    		break;
    	    }
    	    if (result > maxres) { overflow = 1; }
    	    result = (result << 4);
    	    if (digit > (ULONG_MAX - result)) { overflow = 1; }
    	    result += digit;
    	    anyDigits = 1;
    	}
        } else if ( base >= 2 && base <= 36 ) {
    	unsigned long maxres = ULONG_MAX / base;
    	for ( ; ; p += 1) {
    	    digit = *p - '0';
    	    if (digit > ('z' - '0')) {
    		break;
    	    }
    	    digit = cvtIn[digit];
    	    if (digit >= ( (unsigned) base )) {
    		break;
    	    }
    	    if (result > maxres) { overflow = 1; }
    	    result *= base;
    	    if (digit > (ULONG_MAX - result)) { overflow = 1; }
    	    result += digit;
    	    anyDigits = 1;
    	}
        }
    
        /*
         * See if there were any digits at all.
         */
    
        if (!anyDigits) {
    	p = string;
        }
    
        if (endPtr != 0) {
    	/* unsafe, but required by the strtoul prototype */
    	*endPtr = (char *) p;
        }
    
        if (overflow) {
    	errno = ERANGE;
    	return ULONG_MAX;
        } 
        if (negative) {
    	return -result;
        }
        return result;
    }
    


  • Motion4Life schrieb:

    Was ich der Dokumentation aber entnehmen kann, ist, dass mein Ruckgabewert kein Int, sondern besser ein long int sein sollte.

    Wenn der Zahlenwert klein genug ist, kannst du diesem auch ein int zuweisen.

    Motion4Life schrieb:

    Außerdem, dass die Funktion alle Leerzeichen zwischen den Argumenten entfernt

    Nein, sie werden nicht entfernt sondern einfach überlesen.

    Motion4Life schrieb:

    1. entfernen aller Leerzeichen ./myfun12345a

    Nein, weil
    a) die Leerzeichen nicht entfernt werden
    b) du den String gar nicht als Ganzes bekommst sondern schon augeteilt. argv[1] enthält "123", argv[2] dann "45a"

    Die Leerzeichen (und die Aufteilung hat in diesem Fall schon die Shell/Betriebsystem gemacht. (oder der Startupcode (wird vor main ausgeführt))

    Motion4Life schrieb:

    1. der **char setzt setzt seinen endmarker auf das ehemalige Leerzeichen ,somit auf 4. Und beginnt dann von 1 bis 3 zu lesen.

    enmarker zeigt dann auf das '\0' (Stringende)

    Motion4Life schrieb:

    Dies wiederholt er beim 2. Argument. Sieht aber das a als Fehler an und hört dann auf, bevor die Adresse nach dem a erreicht worden ist.

    Ja

    Motion4Life schrieb:

    Warum ich dass mit einem nicht initialisierten pointer machen kann

    weil in der Funktion nie lesend auf enmarker zugegriffen wird

    Motion4Life schrieb:

    und was NULL damit zu tun hat "wenn man es nicht verwendet" wird wohl ein Rätsel bleiben

    NULL ist die Kennzeichnung für ungültiger Zeiger.

    Wenn endmarker ungleich NULL ist, wird in *endmarker die Adresse vom Abbruchzeichen geschrieben.

    Kommst du denn mit dem Doppelzeiger klar?



  • Ich habe heute mal versucht, eure Erklärungen anzunehmen und die Vorgehenweise selber zu programmieren. Natürlich wird dieser Quellcode mein char nicht wie strol prüfen, aber so im Allgemeinen werden doch die Argumente eingelesen oder?

    Ich lese 2 Werte ein. Das sind Strings. (Test1 und Test2)
    Durch man printf habe ich erfahren, dass printf nichts Anderes macht als nach dem /0 Terminator zu suchen und dann zu stoppen. Das entspricht so ungegefähr der Vorgehensweise, wenn ich statt einen endpointer (der die Adressen meiner 0 Terminatoren speichert) auf NULL setze. Wenn ich jetzt noch ein Abfrage hinbekommen würde, ob der String literale != meiner Zahlen enthält, entspräche das doch ungefähr so dem Prinzip, oder nicht?

    int main(void){

    char* a[]={"Test1","Test2"};

    printf("%s",a[1]);
    }

    PS: Warum sollte man einen Endpointer benutzen, wenn man den Wert einfach auf NULL zeigen lassen kann? Ich kann mir gerade nicht vorstellen, warum ich die Adressen meiner Null-Terminator brauchen könnte.



  • Motion4Life schrieb:

    PS: Warum sollte man einen Endpointer benutzen, wenn man den Wert einfach auf NULL zeigen lassen kann? Ich kann mir gerade nicht vorstellen, warum ich die Adressen meiner Null-Terminator brauchen könnte.

    Vorstellbar sind ja solche Zusammenhänge:
    char *csv = "123, 456, 789, ...
    Wenn du nun, strtol() mit csv als Argument aufrufst, wird es beim Komma mit dem Parsen stoppen. Nun guckst du einfach nach, ob *endptr == ',' oder == 0 ist und kannst in Abhängigkeit davon entscheiden, ob danach noch mehr Werte kommen oder nicht.



  • Motion4Life schrieb:

    Durch man printf habe ich erfahren, dass printf nichts Anderes macht als nach dem /0 Terminator zu suchen und dann zu stoppen.

    Das steht wohl so in jedem noch so schlechten Buch/Tutorial über C.

    Motion4Life schrieb:

    Wenn ich jetzt noch ein Abfrage hinbekommen würde, ob der String literale != meiner Zahlen enthält, entspräche das doch ungefähr so dem Prinzip, oder nicht?

    Verstehst du diesen (deinen) Satz heute immer noch?

    Motion4Life schrieb:

    PS: Warum sollte man einen Endpointer benutzen, wenn man den Wert einfach auf NULL zeigen lassen kann? Ich kann mir gerade nicht vorstellen, warum ich die Adressen meiner Null-Terminator brauchen könnte.

    Zum einen zeigt endptr nicht unbedingt auf '\0'.
    Er zeigt auf das erste Zeichen, dass nicht mehr zur Zahl passt.
    In deinem Beispiel "./myfun 123 45a" wird er beim zweiten Parameter auf das 'a' zeigen.

    Jetzt liegt es an dem Syntax der Eingabe, ob das 'a' dort richtig oder falsch ist. Entsprechend kannst du reagieren.

    Du kannst auch den String ab der Stelle weiter untersuchen.

    i.A. benutzt man strtol um Nutzereingaben (menschenlesbarer Zahlen) umzuwandeln. Und Nutzer hast du nicht im Griff. Die machen was sie wollen. Es passieren Fehler.

    Warum braucht man sonst noch die Adresse vom Terminator:
    strcat kopiert dorthin den zweiten String.



  • Hier habt ihr ein strtol ohne *endptr:

    long myStrtol (char *str)
    {
        long x = 0;
        int sign = *str == '-' ? str++, -1 : 1;
        while (*str >='0' && *str <= '9')
            x = 10 * x + *str++ - '0';
        return x*sign;
    }
    


  • Andromeda schrieb:

    Hier habt ihr ein strtol ohne *endptr:

    Ob das sowas wie atol ist?

    http://www.cplusplus.com/reference/cstdlib/atol/

    Die hat aber einige Nachteile bei der Fehlererkennung gegenüber strtol .


Anmelden zum Antworten