Verwirrung mit String-Literalen



  • um mein häufchen noch drauf zu legen, falls richtig in erinnerung

    //schreibgeschützt
    char *s = "teststring";
    
    //nicht schreibgeschützt
    char s[] = "teststring";
    


  • Danke für das Beispiel no_code

    // Zeiger auf statischen Speicher, den man nicht verändern sollte, deswegen besser const deklarieren, damit der compiler helfen kann
    const char *s = "teststring";
    
    // char array (z.B. Zeiger auf den Stack) mit dem Inhalt "teststring", den man verändern darf.
    char s[] = "teststring";
    

    const vs. nicht const
    Das Problem ist der compiler organisiert String literale recht effizient.
    wird z.B. "teststring" mehrfach verwendet,
    so legt er "teststring" normalerweise nur einmal in den statischen Speicher.

    Verändert man nun den Inhalt von "teststring" führt das in der folge zu UB.
    Der statische Speicher kann z.B. READONLY deklariert sein,
    andere Codestellen rechnen NICHT mit dieser 'magischen' Veränderung, ...

    Hoffe das hilft beim Verständnis,
    Gruß Frank.



  • Okay, ehrlich gesagt habe ich wirklich keine Ahnung von Stringliteralen und daher dann scheinbar falsche Vorstellungen.

    Meine Funktion würde so aussehen:

    void exception_test (void *pointer, char *error_message){
    
    	int i;
    
    	if (pointer == NULL){
    		printf(error_message);
    		scanf("%i", &i); /* falls Aufruf nicht über Konsole */
    		exit(EXIT_FAILURE);
    	}
    
    	return;
    }
    

    Ich würde jetzt eben bei malloc immer den gerade zugewiesenen Pointer und eine "Kein Speicher"-Fehlermeldung (als Literal) mitgeben. Außerdem würde ich sie auch für andere Fehlermeldungen benutzen und dann eben gleich mit NULL als Pointer aufrufen.

    Ein Aufruf sähe dann zB so aus:

    int *a;
    
    a= malloc(123*sizeof(int));
    exception_test(a, "Nicht genug Speicher vorhanden\");
    

    Wäre das korrekt funktionierender Code?



  • Anstatt in der Funktion den Pointer zu prüfen und bei den vielen anderen möglichen Fehlern dafür NULL zu übergeben, um die Prüfung fehlschlagen zu lassen, wäre es doch sinnvoller, es so zu schreiben:

    a=malloc(123*sizeof(int));
    if(!a) {
      ErrorExit("Out of memory");  //Name mal geändert, "test" passt nicht mehr
    }
    

    Und damit sich die Fehlertexte nicht im Source wiederholen (Redundanz jeglicher Art immer nach Möglichkeit vermeiden!), könntest du Fehlernummern definieren, die du stattdessen an deine Fehlerfunktion übergibst.

    #define ERR_NO_ERR 0
    #define ERR_OUT_OF_MEM 1
    #define ERR_STRANGE_BEHAVIOUR 2
    //...
    
    a=malloc(123*sizeof(int));
    if(!a) {
      ErrorExit(ERR_OUT_OF_MEM);
    }
    
    void ErrorExit(int ErrorNumber) {
      switch(ErrorNumber) {
        case 0:
          printf("No Error");
          break;
        case 1:
          printf("Out of memory");
          break;
        case 0:
          printf("Strange behaviour! Please redesign your programm.");
          break;
      }
      exit(EXIT_FAILURE);
    }
    

    Nur ein kleiner Gedankenanstoß...



  • Ich wollte bei den Out-of-memory Fehlern immernoch dazu angeben, in welcher Funktion es den Fehler gab, daher muss ich eben immernoch den Funktionsnamen als Parameter mitgeben. Oder gibt es irgendeine Möglichkeit, den Funktionsnamen der aktuellen Funktion "automatisch" mitzugeben?



  • plizzz schrieb:

    Ich wollte bei den Out-of-memory Fehlern immernoch dazu angeben, in welcher Funktion es den Fehler gab, daher muss ich eben immernoch den Funktionsnamen als Parameter mitgeben. Oder gibt es irgendeine Möglichkeit, den Funktionsnamen der aktuellen Funktion "automatisch" mitzugeben?

    Schau dir mal __FILE__, __LINE__ usw. an (ist aber glaube ich Compiler-spezifisch, bei MS gibt's die).



  • plizzz schrieb:

    Ich wollte bei den Out-of-memory Fehlern immernoch dazu angeben, in welcher Funktion es den Fehler gab, daher muss ich eben immernoch den Funktionsnamen als Parameter mitgeben. Oder gibt es irgendeine Möglichkeit, den Funktionsnamen der aktuellen Funktion "automatisch" mitzugeben?

    __func__ ist der Funktionsname. __FILE__ und __LINE__ sind Datei bzw. Zeile. Mit man: perror gibt es übrigens eine Funktion, mit der man Fehlermeldungen ausgeben kann (die auch man: errno überprüft und einem genau sagt, was schief gegangen ist)

    @_matze
    Nein, das ist Standard C.



  • Kann ich auch Expressions in String-Literale einbauen? Ich habe vorher eben sofort über printf eine Fehlermeldung ausgegeben und konnte da einen Laufindex mit einbauen, den er ausgegeben hat, also

    printf("ERROR bei Objekt %i", i);
    

    Nun habe ich so eine error_exit Funktion, der ich einen String übergebe, den sie dann ausgibt. Ist es möglich, in den String irgendwie diesen Wert von i einzubauen?



  • plizzz schrieb:

    Kann ich auch Expressions in String-Literale einbauen? Ich habe vorher eben sofort über printf eine Fehlermeldung ausgegeben und konnte da einen Laufindex mit einbauen, den er ausgegeben hat, also

    printf("ERROR bei Objekt %i", i);
    

    Nun habe ich so eine error_exit Funktion, der ich einen String übergebe, den sie dann ausgibt. Ist es möglich, in den String irgendwie diesen Wert von i einzubauen?

    Das hat nichts mit dem Stringliteral zu tun! Das %i mit dem Wert von i zu ersetzen ist das was man: printf macht! Nicht der Stringliteral! Wenn du so etwas in deiner Funktion machen willst, dann könntest du zB man: vsnprintf nehmen (wenn du die ganzen Formatierungssachen nicht selbst schreiben willst).



  • Japp, das wusste ich ja, dass das zur printf-Funktion gehört. Ich habe mich nur gefragt, ob es eben möglich ist, dies äquivalent umzusetzen. Ich werde jetzt aber erstmal die ganzen netten Links durhclesen, die hier gepostet wurden.

    Danke schonmal!



  • Ich hab folgeden Makro in meinen Bibliotheken:

    #define myperror(...) \
    do { \
        const char *file = strrchr(__FILE__, '/');\
        if(file) file++; else file = __FILE__;\
        fprintf(stderr, "%s:%d @ '%s': ", file, __LINE__, __func__); \
        fprintf(stderr, __VA_ARGS__); \
        fflush(stderr); \
    } while(0)
    

    und verwende es so:

    void *x = malloc(82);
    if(x == NULL)
      myperror("strange world......\n");
    else
      free(x);
    
    myperror("even with %s value\n", "a");
    

    Ausgabe wäre (im Fall eines Fehlers)

    file.c:20 @ 'foo': strange world.......
    file.c:24 @ 'foo': even with a value
    

    btw: du kannst mit man: strerror(3) eine "String-Version" von errno bekommen.



  • _matze schrieb:

    void ErrorExit(int ErrorNumber) {
      switch(ErrorNumber) {
        ...
        default:
          printf("Strange behaviour! Please redesign your programm.");
          break;
      }
      exit(EXIT_FAILURE);
    }
    

    Nur ein kleiner Gedankenanstoß...

    😃


Anmelden zum Antworten