# [C++] char* Out-Parameter



## chriss_2oo4 (20. Dezember 2007)

Hi,

ein Ähnliches Problem gibt es schon paar Threads weiter unten, hilft mir aber nicht wirklich weiter.

(-> Objekte wie CString oder String kann ich nicht verwenden )  

Ich möchte meiner Funktion (CheckValidty) einen Out-Parameter (pcErrorReport), also Call-by-Referenz, übergeben und diesen in meiner Funktion bearbeiten.

Die Funktion sieht so aus:

int CheckValidty( LPCSTR pcPath, LPSTR pcErrorReport )
{
  ...
  pcErrorReport = myCString.GetBuffer();
  ...
}

In der Main rufe ich die Funktion wie gefolgt auf:

int main()
{
  ...
  char * pcErrorReport;
  CheckValidty( "c:\\derPfad", pcErrorReport);
  ...
}

Wenn ich so vorgehe bekomm ich die Fehlermeldung, dass ich den Zeiger pcErrorReport nicht initialisiert hätte, aber wozu muss ich dass denn machen, ich möchte ihn ja nur auf eine Speicheradresse zeigen lassen, die bis dato noch garnicht beschrieben wurde?

Jetzt bin ich mir nicht ganz sicher, aber die methode geht doch wie gefolgt vor:

1. Erstellung eines Zeigers auf char
2. Zeiger zeigt auf die Adresse im Speicher, die von der Methode GetBuffer() im Speicher abgelegt wurde.
3. Funktion wird verlassen -> der, von der Funktion allokierte Speicher, wird wieder freigegeben -> ergo zeigt auch mein Zeiger auf eine leere Adresse

Des Weiteren hab ich noch nicht verstanden, wieso häufig zu einem Zeiger die Länge mit übergeben wird, denn der Zeiger zeigt doch immer auf die erste Adresse im char array und es ist doch eigentlich egal wie lang das Array werden soll?

So das waren jetzt viele Fragen, aber ich denke das es für einen C++ 'ler kein Problem ist mir diese Fragen zu beantworten -> bin mit java und c# aufgewachsen und deshalb ist mir c++ etwas suspekt

Lg Chriss


----------



## MCoder (20. Dezember 2007)

Hallo,

die Meldung zum nicht initialisierten Zeiger sollte eigentlich nur eine Warnung sein (C4700 bei VC++), so dass sich das Programm trotzdem erstellen lassen sollte.

Die Beschreibung deiner Methode ist soweit richtig. Nach dem Verlassen der Funktion verweist der Zeiger auf eine ungültige Adresse. Die Bezeichnung "leer" ist da nicht so glücklich gewählt.

Die Länge des Speicherbereiches zu einem übergebenen Zeiger ist wichtig, damit die Methode/Funktion weiss, welche Datenmenge sie darin speichern kann. Wird über das Ende hinausgeschrieben, entstehen die sogenannten Pufferüberläufe, die günstigstenfalls nur zum Programmabsturz führen u.U. aber ein Sicherheitloch entstehen lassen können. Es ist daher nicht egal, wie lang das Array werden soll. In C/C++ kann man nur den Speicher verwenden, den man vorher alloziiert hat.

Gruß
MCoder


----------



## chriss_2oo4 (20. Dezember 2007)

Hi,

also erstmal Danke für deine ausführliche Antwort.

VS2005 gibt keine Warnung oder Fehlermeldung aus, er gibt eine Meldung während der Laufzeit, also sobald er auf die nicht initialisierte Variable zugreift?



> Die Länge des Speicherbereiches zu einem übergebenen Zeiger ist wichtig, damit die Methode/Funktion weiss, welche Datenmenge sie darin speichern kann. Wird über das Ende hinausgeschrieben, entstehen die sogenannten Pufferüberläufe, die günstigstenfalls nur zum Programmabsturz führen u.U. aber ein Sicherheitloch entstehen lassen können. Es ist daher nicht egal, wie lang das Array werden soll. In C/C++ kann man nur den Speicher verwenden, den man vorher alloziiert hat.



Das versteh ich aber immer noch nicht, was denn nun nach dem Aufruf der Funktion im Speicher steht bestimmt doch die Funktion, die ich aufrufe und nicht der Aufrufer der Methode, also wenn ich den Wert Hallo übergebe und die Funktion Welt anähngt muss ich doch nicht der Methode sagen dass der String 9+1 Zeichen lang ist?

Lg Chriss


----------



## MCoder (20. Dezember 2007)

Es geht darum, das die Funktion/Methode, die an etwa den String "Hallo" den String "Welt" anhängt, auch den Speicherplatz zur Verfügung hat, um die Gesamtzahl der Zeichen aufzunehmen.
Falls man einen Speicherblock als Referenz übergibt, der dann z.B. das Ergebnis einer Stringverkettung erhalten soll, sollte man der ausführenden Funktion schon sagen, wieviel Speicherplatz sie "verbraten" kann. Falls nämlich das Ergebnis mehr Speicherplatz benötigen würde, als zur Verfügung steht, kann entsprechend darauf reagiert werden.

Gruß
MCoder


----------



## chriss_2oo4 (21. Dezember 2007)

Hi,

ich hab jetzt nicht den vollen Durchblick beim Speichermanagement, aber wenn ich einen Standarddatentyp deklariere (den ich ohnehin nicht mit new auf 'm Heap ablegen kann), dann wird dieser im Stack abgelegt -> desshalb gebe ich eine Grenze (maximale Größe) an, da es evtl möglich ist dass ich im relativ kleinem Speicherbereich des Stacks einen Teil (oder auch komplett) einer anderen Variable überschreibe?
Aber um das 100%ig richtig zu machen brauch ich ja den kompletten Überblick über den gesamten Stack, ich meine es könnte ja auch jemand anders was im Stack ablegen?

Lg Chriss


----------



## MCoder (21. Dezember 2007)

chriss_2oo4 hat gesagt.:


> aber wenn ich einen Standarddatentyp deklariere (den ich ohnehin nicht mit new auf 'm Heap ablegen kann)


Ich weiss nicht, was du unter Standardtypen verstehst, aber es können jegliche Arten von Variablen im Heap abgelegt werden.


chriss_2oo4 hat gesagt.:


> da es evtl möglich ist dass ich im relativ kleinem Speicherbereich des Stacks einen Teil (oder auch komplett) einer anderen Variable überschreibe?


Nicht nur andere Variablen sondern auch die Rücksprungadresse zur aufrufenden Funktion. Auf diesem Weg kann fremder Code eingeschleusst werden.


chriss_2oo4 hat gesagt.:


> Aber um das 100%ig richtig zu machen brauch ich ja den kompletten Überblick über den gesamten Stack, ich meine es könnte ja auch jemand anders was im Stack ablegen?


Interessant ist nur der Stack der aufgerufenen Funktion. Andere Prozesse kommen da normalerweise nicht ran. Eine Übersicht über den Stack brauchst du eigentlich nicht; du musst nur darauf achten, dass du nicht mehr Speicher verwendest, als du reserviert hast.

Gruß
MCoder


----------



## chriss_2oo4 (7. Januar 2008)

Hi,

nochmals danke für die Antworten, meine Antwort kommt etwas spät... war die ganzen Ferien nicht am PC.

Aus der Antwort auf meinen ersten Post geht ja hervor, dass der Zeiger pcErrorReport auf eine ungültige (nicht leere;-)) Adresse zeigt, weil der Speicherbereich nach dem Verlassen der Funktion wieder freigegeben wird:



> int CheckValidty( LPCSTR pcPath, LPSTR pcErrorReport )
> {
> ...
> pcErrorReport = myCString.GetBuffer();
> ...



Aber wie löst man das am elegantesten dass der Speicherbereich nicht freigegeben wird und somit der Zeiger auf eine gültige Adresse zeigt?

Die Variable myCString global zu deklarieren ist doch auch nicht die beste Möglichkeit?

Lg Chriss


----------



## MCoder (7. Januar 2008)

> Aber wie löst man das am elegantesten dass der Speicherbereich nicht freigegeben wird und somit der Zeiger auf eine gültige Adresse zeigt?


Warum gibtst du nicht einfach das CString-Objekt zurück:

```
CString CheckValidty(LPCSTR pcPath)
{
    // ...

    return myCString;
}

int main()
{
    // ...

    char *pcErrorReport = CheckValidty("c:\\derPfad").GetBuffer();

    // ...
}
```
Gruß
MCoder


----------



## chriss_2oo4 (7. Januar 2008)

Hi,

das wäre eine gute Lösung, die Aufgabenstellung setzt jedoch o. g. Vorgehensweise voraus. Außerdem möchte man doch immer gerne was neues dazulernen ;-)

Ich kenne einige Funktionen (WinAPI) wo das genauso gemacht wird, wobei dort zusätzlich eine Länge mit angegeben wird (z. B. GetPrivateProfileString )

Lg Chriss


----------



## MCoder (7. Januar 2008)

chriss_2oo4 hat gesagt.:


> Hi,
> Ich kenne einige Funktionen (WinAPI) wo das genauso gemacht wird, wobei dort zusätzlich eine Länge mit angegeben wird (z. B. GetPrivateProfileString )
> Lg Chriss


Bei diesen Funktionen muss der Speicher aber bereits außerhalb der Funktion alloziiert werden und dann wird ein Zeiger auf diesen Speicherbereich und seine Länge übergeben. Wenn du das ähnlich machen willst, müsste das etwa so aussehen:

```
int CheckValidty(LPCSTR pcPath, LPTSTR pcErrorReport, int nSize)
{
    // ...
 
    if( myCString.GetLength() >= nSize )
    {
        return 0; // Fehler, Speicher zu klein
    } 
 
    lstrcpy(pcErrorReport, myCString);
    return 1;
}
 
int main()
{
    // ...
 
    char pcErrorReport[1000];
    int nRet = CheckValidty("c:\\derPfad", pcErrorReport, sizeof(pcErrorReport));
 
    // ...
}
```
Gruß
MCoder


----------



## chriss_2oo4 (8. Januar 2008)

Genau das is es!

Vielen Dank MCoder!

Lg Chriss


----------

