Umgang mit Pointern



  • Hi,
    ich möchte gerne mit dynamischen Speicher arbeiten, und diesen uU in Unterfunktionen vergrößern, wäre der Umgang mit call by reference so korrekt?

    Wie würde man das in der Praxis verwenden, jedes mal Zeiger auf Zeiger? Oder gibt es da eine elegantere Methode?

    #include <stdio.h>
    #include <stdlib.h>
    
    int foo3(char **pp_ptr3)
    {
    	*(*pp_ptr3)='A';
    	*(*(pp_ptr3+1))='\0';
    	
    	
    	return 0;
    }
    
    int foo2(char **pp_ptr2)
    {
    	char *p_hold=NULL;
    	
    	if((p_hold=realloc(*pp_ptr2, 12))==NULL)
    	{
    		printf("fail2\n");
    		return -1;
    	}
    	*pp_ptr2=p_hold;
    	
    	foo3(&(*pp_ptr2));
    
    	return 0;
    }
    
    int foo1(char **pp_ptr1)
    {
    	printf("%p %p\n",&pp_ptr1, pp_ptr1);
    	foo2( &(*pp_ptr1) );
    	
    	return 0;
    }
    
    int main(void)
    {
    	char *p_ptr=NULL;
    	
    	if((p_ptr=malloc(10))==NULL)
    		printf("fail\n");
    	printf("%p %p\n",&p_ptr, p_ptr);	
    	foo1(&p_ptr);
    
    	printf("%p %p\n",&p_ptr, p_ptr);	
    	free(p_ptr);
    	return 0;
    }
    

  • |  Mod

    Funktioniert es denn? Wahrscheinlich bricht es doch mit einem Segfault ab. Also ist es offensichtlich nicht korrekt.

    Sieht das Programm für dich elegant aus? Es sind zwar nur zwei Sterne, aber vielleicht liest du dir trotzdem mal durch, was man unter einem über Drei-Sterne-Programmierer (three star programmer) versteht.

    Das Geheimnis, um den Überblick über die eigenen Programme zu behalten, ist Abstraktion. Anstatt wild mit rohen Zeigern zu jonglieren, und an jeder Stelle den Überblick behalten zu müssen, in welchem Zustand gerade welche Verschachtelungsebene von jedem Zeiger ist, macht man sich stattdessen eine Datenstruktur und zugehörige Funktionen, die nur die eine Aufgabe haben, dynamischen Speicher zu verwalten (anlegen, Größe ändern, kopieren, freigeben), und diese eine Aufgabe universell und korrekt erledigen. Und wieder andere Funktionen, die wieder nur genau eine Aufgabe haben, die dann gegebenenfalls deine Datenstruktur für dynamischen Speicher benutzen.

    Man könnte nun einwenden, dass doch bereits Zeiger, malloc, realloc, und free genau ein solcher Fall von einer Datenstruktur mit zugehörigen Funktionen sind. Und man hätte damit komplett recht. Die Frage wird dann eher, wieso du eigene Funktionen programmierst, die dann doch nichts anderes machen, als realloc aufzurufen? Wieso nicht direkt realloc aufrufen?

    Die Bemerkung über Abstraktion, um beim Programmieren den Überblick zu behalten, gilt natürlich trotzdem.



  • @_Neuling sagte in Umgang mit Pointern:

    Wie würde man das in der Praxis verwenden

    Mit "foo" und "pp_ptr" kann man sich nichts darunter vorstellen. Wenn du ein Beispiel hättest mit erkennbarer Semantik, dann könnte man eher was darüber sagen wie man "das" in der Praxis machen würde.



  • @_Neuling sagte in Umgang mit Pointern:

    char *p_ptr=NULL;
    
    if((p_ptr=malloc(10))==NULL)
    	printf("fail\n");
    

    Komisches Muster mit Zuweisung im if, das du da zweimal geschrieben hast.

    char *foo = malloc(42);
    if(!foo) {
        // exit / return / whatever.
    }
    


  • Ich habe den Teil eines großen Projektes und dachte zu viel Code und geschreibe schreckt eher ab.

    Im Grunde möchte ich üben einen Kommunikationsweg Daten austauschen(zur Zeit noch seriell oder ssh). Da die Datenmengen abundzu recht groß sein können(~4000Bytes), wollte ich dynamischen Speicher verwenden.

    char *p_buffer=NULL;
    	
    	if((p_buffer=malloc(1024+1))==NULL)
    		return 0;
    	
    	wr_cmd2("commando", &p_buffer);	
    	wr_cmd2("commando", &p_buffer);
    	
    	free(p_buffer);
    

    ich möchte also ich unterschiedlichen Funktionen write read Kommandos absetzen können.

    int wr_cmd2(char *cmd, char **recv_buffer){
    
    	
    	switch(COMM_METHOD)
    	{
    		case CM_RS232:
    					WriteReadComFixSize(cmd, *recv_buffer);	
    			break;
    		case CM_SSH:
    					GetAndSendInput(cmd);
    					GetAndSendInput("\n");
    					ReadAndHandleOutput_untilprompt(*recv_buffer);
    					break;
    		default: printf("Unknown communicationway!");
    	}
    
    	return 0;
    }
    

    ReadAndHandleOutput_untilprompt() ließt mit readfile aus einer pipe

    do
    {
    		printf("available: %i\n",i_BytesAvailable); 
    		if((i_BytesAvailable-i_BytesCount)<READ_BUF_SIZE+1) // have always enough memory for a readfile with full READ_BUF_SIZE(512)
    		{
    			printf("alloc! %i - %i < 512+1 = %i\n", i_BytesAvailable, i_BytesCount, i_BytesAvailable-i_BytesCount);
    			p_test=realloc(*lpBuffer, DEFAULT_BUF_SIZE+(READ_BUF_SIZE * ++i)+1);// +1 for stringnull at the end
    			if(p_test==NULL)
    			{
    				printf("can't allocate memory, abort reading!");
    				return -1;
    			}
    			printf("i=<%i> allocate memory= %i\n", i, DEFAULT_BUF_SIZE+(READ_BUF_SIZE * i)+1); 
    			*lpBuffer=p_test;
    			i_BytesAvailable=DEFAULT_BUF_SIZE+(READ_BUF_SIZE * i)+1;
    		}
    
    		if (!ReadFile(Redirect_Info.hOutputRead, *lpBuffer+i_BytesCount, READ_BUF_SIZE, &nBytesRead, NULL))
    	        {
    	    	          if (GetLastError() == ERROR_BROKEN_PIPE)
    	                 {
    	           	         printf("ERROR_BROKEN_PIPE");
    	           	         return -2;
    	                 }
    	                 else
    	                 {
    	           	         printf("error at ReadFile",);
    	           	         break;
    	                 }
    	        }
    	        //counting 
    	        i_BytesCount+=nBytesRead;
    	        printf("Bytescount: %i += nBytesRead %i\n", i_BytesCount, nBytesRead);
    		
    		*(*lpBuffer+i_BytesCount)=0;
    	        printf("strlen: %i\n",strlen(*lpBuffer));
    	    
    	}while(find_prompt(&(*lpBuffer), nBytesRead));
    

    Der Auszug ist sicherlich nicht ansatzweise korrekt, dem bin ich mir bewusst, es soll nur meine Herangehensweise darstellen.

    @swordfish: bei dem fail habe ich einen Breakpoint gesetzt, d.h. ich wäre nicht weiter gegangen im Fehlerfall.



  • Vielleicht möchtest Du einen Datentyp für Strings implementieren?

    typedef struct string_tag {
        char *data;
        size_t length;
        size_t size;
    } string_t;
    
    string_t string_create(void)
    {
        string_t string = { calloc(1, 1), 0, 1 }; // Leerer String, '\0' terminiert. length = 0, size = 1
        return string;
    }
    
    bool string_is_valid(string_t *string)
    {
        return string->data;
    }
    
    bool string_assign(string_t *dst, char const *src)
    {
        size_t new_length = strlen(src);
        if (new_length + 1 > dst->size) {
            char new_data = realloc(dst->data, new_length + 1);
            if(!new_data)
                return false;
            dst->data = new_data;
        }
        dst->size = new_length + 1;
        dst->length = new_length;
        strcpy(dst->data, src);
        return true;
    }
    
    // ...
    

    @_Neuling sagte in Umgang mit Pointern:

    @swordfish: bei dem fail habe ich einen Breakpoint gesetzt, d.h. ich wäre nicht weiter gegangen im Fehlerfall.

    Ja, ne, das Komische ist das Initialisieren mit NULL und die umständliche Zuweisung in der condition vom if.



  • Danke fürs Beispiel @swordfish, aber muss ich die Datenstruktur dann nicht genauso durchreichen mit Pointern, wie ich es im ersten Beitrag vor hatte?



  • Ja, weil du willst ja das Original verändern und nicht eine Kopie. Aber was ist das Problem dabei? Wenigstens hast Du so keine hässlichen "Doppelzeiger" mehr. Die Zauberworte heißen OOP und Kapselung.

    // edit: Ich hab' dir string_assign() oben als Bleistift implementiert. string_append() usw. ist dein Bier. string_destroy() nicht vergessen.



  • Ich vermute ich verstehe was du mir sagen möchtest, aber wenn ich die Datenstruktur in die 3.Unterfunktion befördern muss, um den prompt zu suchen, brauch ich doch genauso wieder Doppelzeiger, ich sehe noch nicht ganz den Vorteil? Vllt brauch ich ein Beispiel um es korrekt zu verstehen?



  • @_Neuling sagte in Umgang mit Pointern:

    [...] 3.Unterfunktion befördern muss, um den prompt zu suchen, [...]

    Ne, du, ich hab' keine Lust zu suchen. Code?



  • Letztes codebeispiel, letzte Zeile, find_prompt() gibt 0 zurück, wenn der prompt gefunden werden konnte.
    Edit: gut, in der letzten Funktion ist es nicht nötig by reference zu übergeben, da nicht verändert wird, dann sind in diesem Bsp zwei Unterfunktionen

    Wie gesagt, ich versuche mir eine gute Programmierstruktur anzueignen, benötige aber dabei etwas Unterstützung



  • @_Neuling sagte in Umgang mit Pointern:

    Letztes codebeispiel, letzte Zeile, find_prompt() gibt 0 zurück, wenn der prompt gefunden werden konnte.

    Ja, dann wirst Du wohl eine bool string_find(string_t const *haystack, char const *needle) schreiben müssen. strstr() ist dein Freund.

    @_Neuling sagte in Umgang mit Pointern:

    in der letzten Funktion ist es nicht nötig by reference zu übergeben, da nicht verändert wird, dann sind in diesem Bsp zwei Unterfunktionen

    Jein. Nötig nicht. Aber einen Pointer zu kopieren ist trotzdem billiger als die gesamte struct. Übergib per string_t const *.



  • @Swordfish sagte in Umgang mit Pointern:

    Vielleicht möchtest Du einen Datentyp für Strings implementieren?

    typedef struct string_tag {
        char *data;
        size_t length;
        size_t size;
    } string_t;
    

    Length und Size, WTF?
    Einer davon ist doch bestimmt überflüssig.



  • @RBS2 sagte in Umgang mit Pointern:

    Length und Size, WTF?
    Einer davon ist doch bestimmt überflüssig.

    Nur wenn der String immer nur soviel wächst wie absolut nötig. Ziemlich ineffizient.

    Selber grund warum ein std::vector<> eine size und eine capacity hat.



  • @Swordfish Okay, reservierter Speicher vs. tatsächliche Stringlänge. Alles klar. 🙂



  • @RBS2 sagte in Umgang mit Pointern:

    @Swordfish Okay, reservierter Speicher vs. tatsächliche Stringlänge. Alles klar. 🙂

    Schlauer/Braver Junge! ❤


Anmelden zum Antworten