realloc führt zu Fehler "invalid old size"



  • Hallo c-community,

    ich habe folgendes Problem:
    realloc führt bei mir bei der Verwendung zu unterschiedlichen Ergebnissen. Hier der erste funktionierende Fall:

    main.c

    #include <stdio.h>
    #include "../header/device.h"
    
    int main(int argc, char* argv[])
    {
    	tds_devices* Devices;
    	DEV_Create((tds_devices**)&Devices);
    	DEV_FindDeviceName((tds_devices**)&Devices);
    	DEV_Free((tds_devices**)&Devices);
    	return 0;
    }
    

    device.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <dirent.h>
    
    #define DEVICEPATH "/dev/v4l/by-id/"
    
    typedef struct
    {
    	int iDeviceCount;
    	char (*pcaSerialNumber)[9];
    	char (*pcaDeviceName)[128];
    }tds_devices;
    
    /*	Constructor and Destructor	*/
    void		DEV_Create(tds_devices** This);
    void		DEV_Free(tds_devices** This);
    
    /*	dev methodes	*/
    void DEV_FindDeviceName(tds_devices** This);
    void	DEV_errno_exit(const char* ErrorMessage);
    
    void		DEV_Create(tds_devices** This)
    {
    	if (NULL == (*This = malloc(sizeof(tds_devices)))) DEV_errno_exit("Allocation of tds_usbinfo failed\tdevice.c line 5");
    }
    
    void		DEV_Free(tds_devices** This)
    {
    	if ((*This)->pcaSerialNumber != NULL) free((*This)->pcaSerialNumber);
    	if ((*This)->pcaDeviceName != NULL) free((*This)->pcaDeviceName);
    	if ((*This) != NULL) free(*This);
    }
    
    void DEV_FindDeviceName(tds_devices** This)
    {
    	struct dirent *CurrentFile;
    	DIR* Directory;
    
    	(*This)->iDeviceCount = 0;
    	if (NULL == (Directory = opendir(DEVICEPATH))) USB_errno_exit("Find device failed due worng path\tdevice.c line 19");
    	while (NULL != (CurrentFile = readdir(Directory))) 
    	{
    		if ((strcmp((char*)CurrentFile->d_name, ".") != 0) && (strcmp((char*)CurrentFile->d_name, "..") != 0))
    		{
    			char (*tmpdeviceName)[128];
    			(*This)->iDeviceCount++;
    //[Fehler]
    			if (NULL == (tmpdeviceName = realloc((*This)->pcaDeviceName, (*This)->iDeviceCount * 128 * sizeof(char)))) USB_errno_exit("realloc failed\tdevice.c line 25");
    //[/Fehler]
    			(*This)->pcaDeviceName = tmpdeviceName;
    			strncpy((*This)->pcaDeviceName[(*This)->iDeviceCount - 1], CurrentFile->d_name, 128);
    			(*This)->pcaDeviceName[(*This)->iDeviceCount - 1][127] = '\0';
    			char* tmpString;		
    			char stringCutter[] = "_-";
    			char Letter[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    			tmpString = strtok((*This)->pcaDeviceName[(*This)->iDeviceCount - 1], stringCutter);
    			while (tmpString != NULL)
    			{
    				if (NULL == strpbrk(tmpString, Letter))
    				{
    					char (*tmpSerialNumber)[9];
    					if (NULL == (tmpSerialNumber = realloc((*This)->pcaSerialNumber, (*This)->iDeviceCount * 9 * sizeof(char)))) USB_errno_exit("realloc failed\tdevice.c line 37");
    					(*This)->pcaSerialNumber = tmpSerialNumber;
    					strncpy((*This)->pcaSerialNumber[(*This)->iDeviceCount - 1], tmpString, 8);
    					(*This)->pcaSerialNumber[(*This)->iDeviceCount - 1][8] = '\0';
    					printf("%s\n", (*This)->pcaSerialNumber[(*This)->iDeviceCount - 1]);
    				}	
    				tmpString = strtok(NULL, stringCutter);
    			}	
    		}	
    	}
    	closedir(Directory);
    }
    
    void	DEV_errno_exit(const char* ErrorMessage)
    {
    	fprintf(stderr, "DEV Error:\t%s error %d, %s\n", ErrorMessage, errno, strerror(errno));
    	exit(EXIT_FAILURE);
    }
    

    Die gleiche device.c rufe ich nun in einer anderen c-Datei auf und es tritt der oben genannte Fehler an der in device.c markeierten Stelle auf.

    camera.c (Auszug)

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <errno.h>
    #include "../../LibUVC/header/obj_camera.h"
    #include "../header/device.h"
    
    #define MAXDEVICE 25
    #define BUFFERSIZE 357120		// 744 * 480
    
    typedef struct
    {
    	int iID;
    	int iCameraCount;
    	int* iDelayInMSec;
    	tds_devices* pDevices;
    	tds_obj_camera** pOBJ_Camera;
    	tds_obj_buffer* PictureBuffer;
    }tds_obj_multiCamera;
    
    /*	Constructor and Destructor	*/
    void	MULTICAMERA_Create(tds_obj_multiCamera** This, int ID, int* DelayInMSec, int DelayInMSecCount);
    
    ...
    
    void	MULTICAMERA_Create(tds_obj_multiCamera** This, int ID, int* DelayInMSec, int DelayInMSecCount)
    {
    	char caWorkFolder[256];
    	int i;
    	if (NULL == (*This = malloc(sizeof(tds_obj_multiCamera)))) MULTICAMERA_errno_exit("Allocation of tds_obj_multiCamera failed\tobj_multiCamera.c line 5");
    
    	time_t t_time = time(NULL);
    	struct tm* stm_timeInfo = localtime(&t_time);
    
    	getcwd(caWorkFolder, 256);
    	sprintf(caWorkFolder, "%s/20%d_%d_%d",caWorkFolder, stm_timeInfo->tm_year-100, stm_timeInfo->tm_mon+1, stm_timeInfo->tm_mday);
    	mkdir(caWorkFolder, 0666);
    
    // Hier gleicher Aufruf
    	DEV_Create((tds_devices**)&(*This)->pDevices);
    	DEV_FindDeviceName((tds_devices**)&(*This)->pDevices);
    // Hier Aufrufende
    
    	MULTICAMERA_Set_CameraCount((*This), (*This)->pDevices->iDeviceCount);
    	if (NULL == ((*This)->pOBJ_Camera = malloc(sizeof(tds_obj_camera*) * MULTICAMERA_Get_CameraCount(*This)))) MULTICAMERA_errno_exit("Allocation of tds_obj_camera failed\tobj_multiCamera.c line 9");
    	MULTICAMERA_Set_ID((*This), ID);
    	MULTICAMERA_Set_DelayInMSec((*This), DelayInMSec);
    
    	for (i = 0; i < MULTICAMERA_Get_CameraCount(*This); i++)
    	{
    		char caWorkSubFolder[256];
    		int iTmpDelayInMSec;
    		sprintf(caWorkSubFolder, "%s/%s",caWorkFolder, (*This)->pDevices->pcaSerialNumber[i]);
    		if (DelayInMSecCount > i) iTmpDelayInMSec = DelayInMSec[i];
    		else iTmpDelayInMSec = DelayInMSec[DelayInMSecCount - 1];
    		CAMERA_Create((tds_obj_camera**)&(*This)->pOBJ_Camera[i], i, (*This)->pDevices->pcaSerialNumber[i], (*This)->pDevices->pcaDeviceName[i], iTmpDelayInMSec);
    	}
    }
    ...
    

    Kurz was habe ich schon kontrolliert (mit ddd), also der große Unterschied ist, dass anscheinend der Zeiger für (*This)->pcaDeviceName scheint sich bei der 2. Verwendung in der anderen c-Datei aus irgendeinem Grund, den ich nicht kenne zu ändern, obwohl ich die gleichen Aufrufe habe. Bevor jetzt die Frage kommt, wie er sich denn ändert, kann ich sagen, beim Aufruf in der Main bleibt der Zeiger (wie erwartet) NULL bis zum realloc. Beim Aufruf durch die camera.c-Datei ist anscheinend schon ein Pointer vergeben, was ich noch nicht verstehe, da ich ja eigentlich genau das gleiche mache.

    Hoffe jemand kann mir helfen.

    MfG
    mirrowwinger

    [Edit1] Also mit dem Debugger ergibt sich noch, dass durch die main.c beim malloc für die tds_devices die Zeiger für die dynamischen Variablen NULL gesetzt werden (vorher haben sie einen Wert - eventuell Trash). Beim Aufruf aus der camera.c-Datei sind diese dynamischen Variablen anscheinend von anfang an NULL aber nach dem malloc bekommen sie eine Adress (von irgendwo her) zugewiesen.[/Edit1]


  • Mod

    Unübersichtlicher Code; vollgepflastert mit dem Best of der sinnlosesten (oder gar schädlichen) Cargo Cult Techniken; Drei-Sterne Programmierung. Meine Diagnose: Undefiniertes Verhalten, vermutlich ausgelöst durch Schreiben in verbotene Bereiche.

    Nötige Verbesserungen, bevor man überhaupt anfangen kann, dir zu helfen:
    - Sobald du mehr einen Stern brauchst, machst du höchstwahrscheinlich etwas falsch. Nutze Abstraktionstechniken wie benutzerdefinierte Datentypen und typedefs!
    - Lies dir die Referenzen zu Funktionen der Standardbibliothek genau durch und verstehe sie!
    - Lerne Pointerkonvertierungsregeln und verstehe sie! Nutze sie!
    - Du musst von jedem einzelnen Zeichen in deinem Code wissen, was genau es bewirkt und wieso du es gesetzt hast. "Das habe ich bei anderen Leuten so gesehen" ist kein guter Grund. Verbinde dies mit dem Punkten über die Funktionen der Standardbibliothek und die Pointerregeln!
    - Es ist keine gute Idee, sich wie ein Möchtegernhacker auszudrücken. Unverständlicher/Schwer verständlicher Code wie

    while (NULL != (CurrentFile = readdir(Directory)))
    

    ist ein Anti-Qualitätszeichen. Drück das lesbar aus, auch wenn es dann zwei Zeilen länger ist! Fremde Leser und dein zukünftiges Selbst in zwei Wochen werden dir dankbar sein.
    - Unter Linux hast du mächtige Fehlersuchwerkzeuge wie die Warnungen und Debugmodi des GCC, sowie valgrind zur Verfügung. Mach dich mit ihrer Benutzung vertraut und nutze sie! Verhindere die Warnungen des Compilers nicht mit unsinnigen Casts! Der Compiler hat Recht, nicht du.
    - Nochmal wiederholt der wichtigste Punkt: ⚠ Du musst von jedem einzelnen Zeichen in deinem Code wissen, was genau es bewirkt und wieso du es gesetzt hast. ⚠ Du hast wahrscheinlich derzeit schon das Gefühl, dass du dies weißt. Überprüfe deine Annahmen! Wie du an obigem Verriss deines Codes merkst, sind viele davon falsch.



  • Konkret ist das Problem, dass malloc den Speicher, den es anfordert, nicht initialisiert. Wenn du später realloc aufrufst, ist (*This)->pcaDeviceName weder NULL noch ein Zeiger auf einen mit *alloc angefoderten Speicherbereich, so dass realloc nichts damit anfangen kann. Quick Fix wäre, calloc zu benutzen. Ob das alle Probleme beseitigt, kann ich dir aber nicht sagen.



  • @ SeppJ

    also danke erstmal für die allgemeinen Tips. Lerne Programmieren autodidaktisch und da ist es sicherlich auch immer problematisch von Anfang an auf allen Gebieten gut zu sein. Deine Kritik ist sicherlich auch berechtigt, könntest du deswegen auf folgende Punkte nochmal etwas spezifischer für mich eingehen, damit ich auch vertiefendes Material dazu suchen kann.

    Folgende Punkte nochmal etwas ausbauen (Vielen Dank):
    - Abstraktion
    - Pointerkonvertierungsregeln

    zu dem Compiler/Warnungen und Debugger:
    Ich nutze aktiv DDD. Deswegen habe ich ja auch bestimmte Hinweise in meinem Anfangsbeitrag mitteilen können (z.B. Verhalten der Zeiger bei malloc). Ich kompiliere immer folgender Maßen:

    gcc -ggdb ...

    Der Quellcode ist bis jetzt beim Kompilieren vollständig OHNE WARNUNGEN!!!. Hier weiß ich nicht, ob es eine Möglichkeit gibt die "Empfindlichkeit" des Compilers zu erhöhen.


  • Mod

    Wenn du noch neu in C bist, dann hast du dich mit deinem Problem völlig übernommen. Nimm dir die Zeit, erst einmal an einfachen Beispielen zu lernen! Das ist hinterher schneller, als jetzt zu versuchen, gleich das schwierige Problem zu lösen, wegen dem du mit C angefangen hast. Das wird sowieso nichts und du wirst am Ende bloß alles wegschmeißen müssen und frustriert sein. Ich wiederhole nochmal, weil es so wichtig ist: Du musst von jedem einzelnen Zeichen in deinem Code wissen, was genau es bewirkt und wieso du es gesetzt hast! Hand aufs Herz: Ist das wirklich der Fall oder hast du manches nur kopiert? Ich denke, wir beide kennen die Antwort.

    mirrowwinger schrieb:

    Folgende Punkte nochmal etwas ausbauen (Vielen Dank):
    - Abstraktion

    Gutes Lehrbuch lesen. Hier im Forum die Lösungen von Problemen ansehen. Lösungen der Standardbibliothek angucken. Lösungen guter (d.h. in der Regel verbreiteter) Bibliotheken angucken.
    Übertriebenes Idealbild: Keine zwei Zeilen im Code sehen sich auch nur ähnlich. Keine zwei Sternchen folgen jemals aufeinander.
    Mittel dazu sind structs, typedefs, Funktionen. Teilweise auch Zeiger und Funktionszeiger, wobei das ein etwas schwierigeres Kapitel ist, von dem du erst einmal die Finger lassen solltest.

    - Pointerkonvertierungsregeln

    Gutes Lehrbuch lesen. Kurzzusammenfassung: Zeiger sind von und nach void* implizit konvertierbar. Bei deinem Code kannst du einfach mal alle Casts rausmachen. Wenn dann der Compiler einen Fehler meldet, dann liegt das da dran, dass da ein Fehler ist, nicht da dran, dass dort ein Cast fehlt.

    Ich kompiliere immer folgender Maßen:

    gcc -ggdb ...

    Der Quellcode ist bis jetzt beim Kompilieren vollständig OHNE WARNUNGEN!!!. Hier weiß ich nicht, ob es eine Möglichkeit gibt die "Empfindlichkeit" des Compilers zu erhöhen.

    Füg mal die Optionen -Wall -Wextra hinzu. Starte dein Programm mal mit valgrind (ggf. vorher installieren) und den Optionen

    valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes dein_programm und gegebenenfalls dessen optionen
    

Anmelden zum Antworten