Array von Zeigern auf char - ich blick nicht mehr durch!



  • Hi(gh)!

    Ich bin jetzt im Goll/Grüner/Wiese auf Seite 221 angekommen, wo die Vorzüge von Zeiger-Arrays gegenüber fest dimensionierten zweidimensionalen Arrays erklärt werden... da ich vorgestern ein kleines Eingabe- und Sortierprogramm für Strings (Bubblesort mit strcpy) geschrieben hatte (und das auch funktionierte), wollte ich es jetzt auf diese Zeiger-Arrays mit variabler "zweiter Dimension" umstellen. Aber leider produzierte ich schon in der Eingabeschleife nach der Eingabe des zweiten Strings einen Speicherzugriffsfehler... warum? Hier ist der Code:

    /* Datei: stringsort.c */
    /* Bubblesort für C-Strings */
    /* Sortierkriterium: aufsteigend                                  */
    
    #include <stdio.h>
    #include <string.h>
    
    int main (void)
    {
      char* liste[5];
      char* ptr=liste;
      char puffer[30];
      unsigned short i=0, j;
     
      
      
      while (i < 5 && strcmp(puffer, "ENDE"))
      {
        printf("Name %d: ", i+1);
        scanf("%s", puffer);
        strcpy(liste[i++], puffer);
      }  
      
      
      ptr=liste;
      
      /*for (i = 19; i > 0; i--)
      {
        for (j=0; j<i; j++)
        {
          if (strcmp(liste[j+1], liste[j]) < 0)
          {
    	strcpy(puffer, liste[j]);
            strcpy(liste[j], liste[j+1]);
            strcpy(liste[j+1], puffer);
          }
        }
      } */
      
      for (i=0; i<5; i++)
      {
        printf("%s\n", liste[i]);
      }
        
      return 0;
    }  
    
    


  • Die Zeiger liste[0...4] zeigen irgendwohin in die Pampa. Du solltest schon Speicher besorgen und die jeweiligen Zeiger darauf zeigen lassen, bevor Du mit strcpy() irgendwas schreibst.

    scanf() nie ohne Feldgrößen für Zeichenketten verwenden.

    Variablen deklariert man seit C99 nicht mehr am Anfang eines Blocks sondern dort, wo sie gebraucht werden.

    Der richtige Datentyp für Speichergrößen ist (zum 100. Mal) size_t und nicht unsigned short.



  • @swordfish sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    Die Zeiger liste[0...4] zeigen irgendwohin in die Pampa. Du solltest schon Speicher besorgen und die jeweiligen Zeiger darauf zeigen lassen, bevor Du mit strcpy() irgendwas schreibst.

    Verstehe ich nicht! Das wird einen im "Goll/Grüner/Wiese" auch nicht erklärt... ich bin kurz davon, das Buch wegzuschmeißen!
    Ich habe hier außerdem auch noch "Programmieren in C" von Kernighan und Richie rumliegen... aber es ist die erste Ausgabe, also wohl erst recht von Anno Mief...

    scanf() nie ohne Feldgrößen für Zeichenketten verwenden.

    Was sind Feldgrößen?

    Variablen Deklariert man seit C99 nicht mehr am Anfang eines Blocks sondern dort, wo sie gebraucht werden.

    Meinetwegen...

    Der richtige Datentyp für Speichergrößen ist (zum 100. Mal) sizt_t und nicht unsigned short.

    dito



  • @yadgar sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    @swordfish sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    Die Zeiger liste[0...4] zeigen irgendwohin in die Pampa. Du solltest schon Speicher besorgen und die jeweiligen Zeiger darauf zeigen lassen, bevor Du mit strcpy() irgendwas schreibst.

    Verstehe ich nicht!

    char *foo[5];
    

    Worauf zeigt jetzt foo[0]?

    @yadgar sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    @swordfish sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    scanf() nie ohne Feldgrößen für Zeichenketten verwenden.

    Was sind Feldgrößen?

    "scanf()" and "fscanf()" format strings should specify a field width for the "%s" string placeholder

    Also in Deinem Fall:

    scanf("%29s", puffer);
    

    @yadgar sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    strcpy(puffer, liste[j]);
    strcpy(liste[j], liste[j+1]);
    strcpy(liste[j+1], puffer);
    

    Dieses herumkopieren wird nicht funktionieren, da liste[0...4] auf unterschiedlich große Speicherbereiche zeigen wird, und ist auch überflüssig: Es reicht die Zeiger zu tauschen:

    {
        char *tmp = liste[j];
        liste[j] = liste[j+1];
        liste[j+1] tmp;
    }
    


  • @swordfish sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    char *foo[5];

    Worauf zeight jetzt foo[0]?
    

    Auf einen Zeiger auf char, wobei dieses char noch unbestimmt ist - aber genau das soll doch mit strcpy() behoben werden! Wieso funktioniert das nicht?

    @yadgar sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    strcpy(puffer, liste[j]);
    strcpy(liste[j], liste[j+1]);
    strcpy(liste[j+1], puffer);
    

    Dieses herumkopieren wird nicht funktionieren, da liste[0...4] auf unterschiedlich große Speicherbereiche zeigen wird, und ist auch überflüssig: Es reicht die Zeiger zu tauschen:

    So weit bin ich noch gar nicht...



  • @yadgar sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    @swordfish sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    @yadgar sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    @swordfish sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    Die Zeiger liste[0...4] zeigen irgendwohin in die Pampa. Du solltest schon Speicher besorgen und die jeweiligen Zeiger darauf zeigen lassen, bevor Du mit strcpy() irgendwas schreibst.

    Verstehe ich nicht!

    char *foo[5];
    

    Worauf zeigt jetzt foo[0]?

    Auf einen Zeiger auf char, wobei dieses char noch unbestimmt ist

    foo[0] ist ein Zeiger auf char, es zeigt nicht auf einen Zeiger auf char.

    @yadgar sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    aber genau das soll doch mit strcpy() behoben werden! Wieso funktioniert das nicht?

    Was soll strcpy() daran ändern, daß einer seiner Operanden einen undefinierten Wert hat und vermutlich auf Speicher zeigt, der Deinem Programm nicht gehört?



  • Hi(gh)!

    @swordfish sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    Worauf zeigt jetzt foo[0]?

    Auf einen Zeiger auf char, wobei dieses char noch unbestimmt ist

    foo[0] ist ein Zeiger auf char, es zeigt nicht auf einen.

    Und wie weise ich foo[0] einen String zu? In meinem Kopf wuselt das alles konfus durcheinander...

    Was soll strcpy() daran ändern, daß einer seiner Operanden einen undefinierten Wert hat und vermutlich auf Speicher zeigt, der Deinem Programm nicht gehört?

    Ich bin anscheinend zu dumm, das zu verstehen...



  • @yadgar sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    Und wie weise ich foo[0] einen String zu?

    Speicher reserviert man in C mit malloc() und Konsorten.

    @yadgar sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    Ich bin anscheinend zu dumm, das zu verstehen...

    char *foo[5];
    

    sind 5 Zeiger auf char. Da Du ihnen keine Werte zugewiesen hast haben sie undefinierte Werte, die höchstwahrscheinlich keine gültigen Zeiger sind.



  • @swordfish sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    @yadgar sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    Und wie weise ich foo[0] einen String zu?

    Speicher reserviert man in C mit malloc() und Konsorten.

    Tja, da haben mich Goll/Grüner/Wiese mal wieder ins offene Messer ihrer miserablen, verquasten Didaktik laufen lassen - dynamische Speicherverwaltung kommt erst ab Seite 354!

    Vielleicht sollte ich mir wirklich stattdessen den Kernighan/Richie vornehmen... wieder mal ein Abend, wo ich hauptsächlich gelernt habe, wie wenig ich über C weiß! Da schreit in mir mal wieder alles nach der Euthanasiespritze...



  • Geschmacksmuster:

    #include <stdbool.h>
    #include <stddef.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    #define MAX_LIST_ITEMS 5
    #define MAX_STRING_LENGTH 30
    
    #define STRING(value) #value
    #define STRINGIFY(symbol) STRING(symbol)
    
    size_t read_strings(char **dst, size_t max_count, char const *prompt)
    {
    	size_t num_strings = 0;
    
    	for (; num_strings < max_count; ++num_strings) {
    		printf("%s %zu: ", prompt, num_strings + 1);
    		char buffer[MAX_STRING_LENGTH + 1] = "";
    		scanf("%"STRINGIFY(MAX_STRING_LENGTH)"s", buffer);
    
    		if (strcmp(buffer, "ENDE") == 0)
    			break;
    
    		dst[num_strings] = malloc(strlen(buffer) + 1);
    		strcpy(dst[num_strings], buffer);
    	}
    
    	return num_strings;
    }
    
    void swap_strings(char **a, char **b)
    {
    	char *tmp = *a;
    	*a = *b;
    	*b = tmp;
    }
    
    void qsort_strings(char **strings, size_t count)
    {
    	bool sorted;
    	do {
    		sorted = true;
    		for (size_t i = 0; i < count - 1; ++i) {
    			if (strcmp(strings[i], strings[i + 1]) > 0) {
    				swap_strings(&strings[i], &strings[i + 1]);
    				sorted = false;
    			}
    		}
    	} while (!sorted);
    }
    
    void print_strings(char **strings, size_t count)
    {
    	for (size_t i = 0; i < count; ++i)
    		puts(strings[i]);
    }
    
    void free_strings(char **strings, size_t count)
    {
    	for (size_t i = 0; i < count; ++i)
    		free(strings[i]);
    }
    
    int main(void)
    {
    	char *list[MAX_LIST_ITEMS];
    	size_t num_list_items = read_strings(list, MAX_LIST_ITEMS, "Name");	
    	
    	qsort_strings(list, num_list_items);
    	print_strings(list, num_list_items);
    	free_strings(list, num_list_items);
    }
    

    Plan-B für eine vaiable Anahl an Zeichenketten:

    /* ... */
    
    size_t read_strings(char ***dst, char const *prompt) /* Dafür bekommt man aber
    Schimpfnamen wie "Drei-Sterne-Programmierer" und wird gesteinigt, oder schlimmeres */
    {
    	size_t num_strings = 0;
    	*dst = NULL;
    
    	for (;;) {
    		printf("%s %zu: ", prompt, num_strings + 1);
    		char buffer[MAX_STRING_LENGTH + 1] = "";
    		scanf("%"STRINGIFY(MAX_STRING_LENGTH)"s", buffer);
    
    		if (strcmp(buffer, "ENDE") == 0)
    			break;
    
    		*dst = realloc(*dst, (num_strings + 1) * sizeof(char*));
    		(*dst)[num_strings] = malloc(strlen(buffer) + 1);
    		strcpy((*dst)[num_strings++], buffer);
    	}
    
    	return num_strings;
    }
    
    /* ... */
    
    void free_strings(char **strings, size_t count)
    {
    	for (size_t i = 0; i < count; ++i)
    		free(strings[i]);
    	free(strings);
    }
    
    int main(void)
    {
    	char **list;
    	size_t num_list_items = read_strings(&list, "Name");
    
    	/* ... */
    


  • @swordfish sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    @yadgar sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    Und wie weise ich foo[0] einen String zu?

    Speicher reserviert man in C mit malloc() und Konsorten.

    Du kannst die Zeiger selbstverständlich auch auf normale char-Array zeigen lassen.

    char* liste[5];
    char feld[5][30];
    
    for (int i = 0; i < 5; i++) liste[i] = feld[i];
    

    Dann kannst du liste sortieren, ohne das Originalfeld umzumodeln (du sparst das strcpy)

    Oder die zeigen auf Stringliterale. Die sind dann allerdings const, da dir deren Speicherbereich nicht gehört.

    char const *wochentage[] = {"Montag", "Dienstag", ... , "Sonntag"};
    (Edit nach Hinweis von Swordfish s.u.)

    Sinn macht das erst mit dynamischen Speicher (und vielen Daten)., aber irgendwo muss man mit dem erklären anfangen.

    Zum K&R: Erst in der zweiten Ausgabe wird ANSI-C (auch als C89 bekannt) beschrieben.
    Das kennst du ja schon. Daher wird dir der Syntax vom K&R C der ersten Ausgabe fremd vorkommen.
    Und, auch der K&R enthält Fehler. Die sind allerdings dokumentiert.



  • @dirkb sagte in Array von Zeigern auf char - ich blick nicht mehr durch!:

    Oder die zeigen auf Stringliterale. Die sind dann allerdings const, da dir deren Speicherbereich nicht gehört.

    Dann sollte Dein Beispiels-wochentage auch char const * sein ... und ein Array.



  • @TE
    Ich verstehe ehrlich gesagt auch nicht, warum es jetzt auf ein Mal C sein muss. Du hast schon was in C++ gemacht, bist auf Probleme gestossen, hast die Flinte in´s Korn geworfen und gedacht, dass das alles in C einfacher wird. Naja, und jetzt stehste wieder da. Bleib bei C++, dann hättest du die Probleme dieses Threads nicht.


Anmelden zum Antworten