Forum: PC-Programmierung Problem bei Pointer-/Parameterübergabe und malloc


von Mark Huber (Gast)


Lesenswert?

Hallo,

habe zwei kleine Funktionen geschrieben, die aus dem main aufgerufen 
werden:

int main(void)
{
    int *m, *n;
    double *arrayzeiger;
    einlesen (m, n, arrayzeiger);
    ausgeben (m, n, arrayzeiger);
    return (0);
}

void einlesen (int *m, int *n, double *arrayzeiger)
{
    int temp;
    char Dateiname[50];

    FILE *Datei;
    printf ("bitte Dateinamen eingeben\n");
    scanf ("%s", Dateiname);
    Datei = fopen (Dateiname, "r");
    if (Datei == NULL)
    {
    printf ("Datei nicht gefunden\n");
    exit (1);
    }
    fscanf (Datei, "%i", m);
    fscanf (Datei, "%i\n", n);
    ZV_c = (double*) malloc (n  sizeof (double*));
    for (temp= 0; temp< *n; temp++)
  {
    fscanf (Datei, "%lf\n", &arrayzeiger[temp]);
  }
    fclose (Datei);
}

void ausgeben (int *m, int *n, double *arrayzeiger)
{
  int temp;
  for (temp= 0; temp< *n; temp++)
  {
    printf ("nachher: = %lf\n", arrayzeiger[temp]);
  }
}

Die einlesen Funktion holt sich aus der Datei die Größe des Arrays und 
belegt anschließend mittels malloc Speicher fürs das Array.
Das Problem ist nur, dass mir die Funktion nicht den neuen Zeiger von 
malloc zurückgibt, der also nur lokal existiert :=(
Deshalb habe ich den Aufruf der Funktion auch schon mal zu 
&(*arrayzeiger) verändert, womit ich ja eigentlich die Adresse des 
Zeigers in die Funktion bekommen sollte. Aber das klappt auch nicht.
Wie macht man das richtig?

Gruß,

Mark

P.S. Wenn ich den Speicher mit malloc schon vorher im main reserviere 
geht alles gut (da der Zeiger ja auch nicht verändert wird).

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Es ist sowieso Zufall, dass das Programm funktioniert. Du speicherst in 
der Zeile "fscanf (Datei, "%lf\n", &arrayzeiger[temp]);" wegen der 
uninitialisierten Variable arrayzeiger irgendwo im Speicher.

Einlesen könnte den allozierten Speicher als Pointer an Main 
zurückgeben. Und am Ende von main() (oder bei neuem Aufruf von 
einlesen()) kannst du den per free() auch wieder freigeben.
1
void * einlesen (int *m, int *n, double *arrayzeiger) // <==
2
{
3
    int temp;
4
    char Dateiname[50];
5
    FILE *Datei;
6
7
    printf ("bitte Dateinamen eingeben\n");
8
    scanf ("%s", Dateiname);
9
    Datei = fopen (Dateiname, "r");
10
11
    if (Datei == NULL)
12
    {
13
       printf ("Datei nicht gefunden\n");
14
       exit (1);
15
    }
16
17
    fscanf (Datei, "%i", m);
18
    fscanf (Datei, "%i\n", n);
19
20
    if (arrayzeiger != NULL) // <==
21
    {
22
       free(arrayzeiger);
23
    }
24
25
    arrayzeiger = (double *) malloc (*n * sizeof (double*)); // <==
26
27
    if (arrayzeiger == NULL) // <==
28
    {
29
       printf ("Speicherproblem\n");
30
       fclose (Datei);
31
       exit (1);
32
    }
33
34
    for (temp = 0; temp < *n; temp++)
35
    {
36
       fscanf (Datei, "%lf\n", &arrayzeiger[temp] /* oder arrayzeiger+temp */); // <==
37
    }
38
39
    fclose (Datei);
40
41
    return ((void *)arrayzeiger); // <==
42
}
43
44
int main(void)
45
{
46
    int *m, *n;
47
    double *arrayzeiger = NULL; // <==
48
49
    arrayzeiger = (double *) einlesen (m, n, arrayzeiger);
50
    ausgeben (m, n, arrayzeiger);
51
    free((void *)arrayzeiger); // <==
52
53
    return (0);
54
}

  

von Arc N. (arc)


Lesenswert?

Eigentlich dürfte noch weniger funktionieren...
da die Zeiger alle uninitialisiert sind d.h. wenn einlesen in main 
aufgerufen wird, bekommt einlesen irgendetwas nur keine Zeiger auf 
benutzbare Variablen in denen die neuen Werte gespeichert werden 
könnten.
Soll arrayzeiger nach dem Aufruf auf den angeforderten Speicher zeigen, 
muss einlesen entweder den neuen Zeiger zurückliefern (double* 
einlesen(int* m, int* n) oder man übergibt einen Zeiger auf einen 
Zeiger.
1
// aus
2
    int *m, *n;
3
    double *arrayzeiger;
4
    einlesen(m, n, arrayzeiger)
5
6
// müsste entweder
7
    int m, n;
8
    double *arrayzeiger;
9
    einlesen(&m, &n, &arrayzeiger);
10
// oder 
11
    int mv, nv;
12
    int *m = &mv, *n = &nv;
13
    einlesen(m, n, &arrayzeiger)
14
// werden

  

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Stimmt. Typ. Fall von Problemfixierung bei mir.

von Mark Huber (Gast)


Lesenswert?

Hallo,

vielen Dank für die schnelle Rückmeldung. Nun bleibt noch ein Problem: 
später sollte die Funktion mehrere Arrays einlesen, d.h. ich müstte auch 
mehrere Zeiger zurückgeben. Aber wenn ich erst mal weiß wie ich die 
Zeiger korrekt in die Funktion ein/übergeben bekomme, dann probliere ich 
da erst mal weiter und melde mich evtl. noch mal.

Danke,

Mark

von Mark Huber (Gast)


Lesenswert?

Hallo,

habe das ganze nun zu
1
void einlesen (int *m, int *n, double **arrayzeiger)
2
{
3
    int temp;
4
    char Dateiname[50];
5
6
    FILE *Datei;
7
    printf ("bitte Dateinamen eingeben\n");
8
    scanf ("%s", Dateiname);
9
    Datei = fopen (Dateiname, "r");
10
    if (Datei == NULL)
11
    {
12
    printf ("Datei nicht gefunden\n");
13
    exit (1);
14
    }
15
    fscanf (Datei, "%i", m);
16
    fscanf (Datei, "%i\n", n);
17
    if (*arrayzeiger != NULL)
18
    {
19
    free (*arrayzeiger);   // <== evtl. ist hier auch ein Fehler
20
    }
21
    *arrayzeiger = (double*) malloc (*n * sizeof (double*));
22
    if (*arrayzeiger == NULL)
23
    {
24
       printf ("Speicherproblem\n");
25
       fclose (Datei);
26
       exit (1);
27
    }
28
    for (temp = 0; temp < *n; temp++)
29
  {
30
    fscanf (Datei, "%lf\n", &(*arrayzeiger)[temp]);
31
  }
32
    fclose (Datei);
33
}
34
35
void ausgeben (int m, int n, double **arrayzeiger)
36
  int temp;
37
  for (temp= 0; temp< *n; temp++)
38
  {
39
    printf ("nachher: = %lf\n", arrayzeiger[temp]);
40
  }
41
42
int main(void)
43
{
44
    int m, n;
45
    double *arrayzeiger = NULL;
46
    einlesen (&m, &n, &arrayzeiger);
47
    ausgeben (m, n, &arrayzeiger);
48
    //free (*ZV_c);   // <== schlägt immer fehl
49
    return (0);
50
}

geändert. Nun läuft es (mal wieder), aber die free Aufrufe scheitern 
(bzw. der letzte auf jeden Fall, bei dem in der einlesen Funktion bin 
ich mir unsicher). Insofern nehme ich stark an, dass da mmer noch was 
nicht stimmt. Warum der letzte free Aufruf scheitert wundert mich, das 
das main ja jetzt den in arrayzeiger die Position/Adresse des 
eigentlichen Arrays hat.
Werde da wohl noch mal ne Nacht drüber schlafen müssen... Muss gestehen, 
so richtig klar ist mir das leider noch nicht.

Mark

von Arc N. (arc)


Lesenswert?

Das free in einlesen ist soweit richtig, dafür das free in main nicht.
Die Funktion ausgeben dürfte auch nur falsche Werte ausgeben, ausser 
arrayzeiger[temp] wäre ein Tippfehler und sollte *arrayzeiger[temp] 
sein.
arrayzeiger[temp] wäre nur dann richtig, wenn auch nur ein einfacher 
Zeiger übergeben werden würde. So wie es jetzt da steht würde der tempte 
(nicht vorhandene Zeiger auf einen double Zeiger) ausgewertet.

Zur Verdeutlichung:
1
 
2
    double* array1 = (double *)malloc(sizeof(double) * 4);
3
    double* array2 = (double *)malloc(sizeof(double) * 4);
4
    double array3[4] = { 1.0, 2.0, 3.0, 4.0 };
5
6
    double* ptr = array1;
7
    double* ptr2[3] = { array1, array2, array3 };
8
9
    double** dptr1 = &ptr;
10
    double** dptr2 = &ptr2[0];
11
12
    array1[0] = 1.1; array1[1] = 2.1; array1[2] = 3.1; array1[3] = 4.1; 
13
    array2[0] = 1.2; array2[1] = 2.2; array2[2] = 3.2; array1[3] = 4.2; 
14
15
    // was geben die folgenden printfs aus?
16
    printf("%p", dptr[0]);
17
    printf("%f", *dptr[0]);
18
    printf("%f", **dptr);
19
    printf("%p", dptr2[1]);
20
    printf("%f", *dptr2[1]);
21
    printf("%f", **dptr2);

von Rolf Magnus (Gast)


Lesenswert?

> double* array1 = (double *)malloc(sizeof(double) * 4);

Den Rückgabewert von malloc sollte man nicht casten. Der Cast ist 
unnötig und verdeckt potentielle Fehler.

von Arc N. (arc)


Lesenswert?

Rolf Magnus wrote:
>> double* array1 = (double *)malloc(sizeof(double) * 4);
>
> Den Rückgabewert von malloc sollte man nicht casten. Der Cast ist
> unnötig und verdeckt potentielle Fehler.

In C++ ist dieser Cast zwingend nötig. In C nicht...

von Rolf Magnus (Gast)


Lesenswert?

In C++ verwendet man eh gleich den Operator new. In C sollte man wie 
gesagt den Cast besser weglassen.

von Mark Huber (Gast)


Lesenswert?

Hallo,

habe nun alles soweit ohne Fehler am laufen, nur das letzte free 
bereitet mir nach wie vor noch Kopfzerbrechen. Meiner Meinung (und dem 
C-Buch Beispiel) nach sollte ein

    [...]
    ausgeben (m, n, &arrayzeiger;
    if (ZV_c != NULL)
    {
    free (arrayzeiger);
    }
    return (0);
}

richtig sein. Das fliegt mir aber immer noch um die Ohren. Da ich 
gelesen habe, dass nach Terminierung des Programms der Speicher eh 
wieder freigegebn wird, hat das jetzt Zeit bis morgen. Werde da noch mal 
ein paar Dinge ausprobieren. Ist zwar mit Sicherheit kein sauberer 
Programmierstil, aber das zählt ja zum Glück auch nicht...
Bis hierhin: vielen Dank für eure Hilfe, habe wieder was dazugelernt und 
steige da (so langsam) durch.

Mark

von Peter M. (Firma: Sorry-No) (edison24)


Lesenswert?

muss das nicht "free(*arryzeiger)" heißen....aslo mit "*"?

von Simon K. (simon) Benutzerseite


Lesenswert?

Nö.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.