C: String aus Funktion zurückgeben

Moin,

ich würde gerne einen String in einer Funktion erzeugen und diesen über return verwenden.

Wenn ich es richtig verstanden habe, dann:

  • ist ein String immer ein Char-Array, richtig?
  • kann eine Funktion keine Arrays zurückgeben, richtig?
  • muss ich einen Zeiger auf das erste Element des Arrays zurückgeben, richtig?

dennoch kriege ich das nicht zum Laufen.
Kann mir jemand ein „3-Zeilen Beispiel“ nennen, wo ein String in einer Funktion erzeugt wird und über

printf ("%s",funktion());

aufgerufen wird?

Die Funktion soll auch noch Argumente aufnehmen, aber das hat mit meinem Problem ja nix zu tun, oder?

thx
moe.

Nochmal Hi!

ich würde gerne einen String in einer Funktion erzeugen und
diesen über return verwenden.

Wenn ich es richtig verstanden habe, dann:

  • ist ein String immer ein Char-Array, richtig?

Ja.

  • kann eine Funktion keine Arrays zurückgeben, richtig?

Ja.

  • muss ich einen Zeiger auf das erste Element des Arrays
    zurückgeben, richtig?

Ja.

dennoch kriege ich das nicht zum Laufen.
Kann mir jemand ein „3-Zeilen Beispiel“ nennen, wo ein String
in einer Funktion erzeugt wird und über

char g\_strMyString[64];

char \*GruselFunc( void )
{
 strcpy( g\_strMyString, "Ob ich hier wohl reinpasse?" );
 return( g\_strMyString );
}

int main( void )
{
 printf ("%s",GruselFunc());

 return( 0 );
}

biddaschön, das sollte gehen, habs nicht probiert!

Gruß
Stefan

char g_strMyString[64];

muss ich mit einer globalen Variable arbeiten? Ich hätte sie gerne lokal.

char *GruselFunc( void )
{
strcpy( g_strMyString, „Ob ich hier wohl reinpasse?“ );
return( g_strMyString );
}

int main( void )
{
printf ("%s",GruselFunc());

return( 0 );
}

biddaschön, das sollte gehen, habs nicht probiert!

ich auch noch nicht, werde es aber demnächst tun.

thx
moe.

Hallo FM

char g_strMyString[64];

muss ich mit einer globalen Variable arbeiten? Ich hätte sie
gerne lokal.

Stefans Beispiel läss sich etwas abwandeln
(Du musst den Speicher selbst verwalten):

 #include 
 #include 
 #include 
 
 char \*generateString( void )
{
 char string[] = "Ob ich hier wohl reinpasse?";
 char \*tmp\_ptr = malloc( strlen(string)+1 );

 if(tmp\_ptr != 0) 
 strcpy(tmp\_ptr, string);

 return tmp\_ptr;
}

 void deleteString(char \*tmp\_ptr)
{
 free( tmp\_ptr );
}

 int main( void )
{
 char \*p\_string = generateString();
 
 if(p\_string != 0) 
 printf ("%s", p\_string);

 deleteString( p\_string );

 return 0;
}

Grüße

CMБ

Kann mir jemand ein „3-Zeilen Beispiel“ nennen, wo ein String
in einer Funktion erzeugt wird und über

printf ("%s",funktion());

aufgerufen wird?

In 3 Zeilen wird das schwierig. Ich komme auf:

#include 
#include 
#include 

const char\* test\_string = "Hello, World!";

char\* some\_func( char\*\* p ) {
 size\_t sl = strlen(test\_string);

 \*p = (char\*)malloc( sl+1 );

 if( NULL != \*p ) {
 strncpy( \*p, test\_string, sl+1 );
 }
 /\* und was sonst? \*/

 return \*p;
}

int main( void ) {
 char\* sptr;

 /\* Fehler falls malloc() fehlschlägt! \*/
 printf( "%s\n", some\_func( &sptr ) );

 free( sptr );
 exit( 0 );
}

Wie schon im Code steht bekommst du dabei einen Segfault, wenn das malloc() in some_func fehlschlägt (also wenn NULL rauskommt). Sowie du in C mit Heap-Speicher arbeitest solltest du jeden Rückgabewert auf Fehler untersuchen bevor du ihn weiter verwendest. Strings in C sind grade deshalb ein besonders hässliches Thema. Du solltest also die Funktion aufrufen und den Rückgabewert prüfen, zB so:

 char\* ret;
 ret = some\_func( &sptr );
 if( NULL != ret ) {
 printf( "%s\n", ret );
 }

Das wird natürlich einfacher, wenn du in der aufrufenden Funktion schon einen Puffer bereithältst, der dort komplett verwaltet wird. Dann musst du nur noch irgendwie vermeiden, dass da zu lange Zeichenketten reinkopiert werden.

muss ich mit einer globalen Variable arbeiten? Ich hätte sie
gerne lokal.

Hallo,

das ist ein Widerspruch in sich, unabhängig von der Sprache - alle lokalen Variablen hören auf zu existieren, wenn das Unterprogramm beendet wird. Das Ergebnis ist „undefined“, im besten Fall.

Gruss Reinhard

muss ich mit einer globalen Variable arbeiten? Ich hätte sie
gerne lokal.

Hallo,

das ist ein Widerspruch in sich, unabhängig von der Sprache -
alle lokalen Variablen hören auf zu existieren, wenn das
Unterprogramm beendet wird. Das Ergebnis ist „undefined“, im
besten Fall.

?? Versteh ich jetzt nicht ganz:

double funktion (void) {
double x = 10;
return x;
}

printf ("%lf",funktion());

funktioniert doch auch ohne globale Variable?! Oder reden wir aneinander vorbei?

thx
moe.

das ist ein Widerspruch in sich, unabhängig von der Sprache -
alle lokalen Variablen hören auf zu existieren, wenn das
Unterprogramm beendet wird. Das Ergebnis ist „undefined“, im
besten Fall.

?? Versteh ich jetzt nicht ganz:

double funktion (void) {
double x = 10;
return x;
}

printf ("%lf",funktion());

funktioniert doch auch ohne globale Variable?! Oder reden wir
aneinander vorbei?

Ein Bisschen. In deinem Beispiel funktioniert „Pass by Value“. Der Compiler weiß, wie er eine Variable vom Typ Double in Register oder im Speicher platzieren muss, damit die aufrufende Funktion sie als Rückgabewert verwenden kann. Dabei wird der Wert mehrmals kopiert:

  • Die Funktion reserviert sich nach dem Aufruf Platz auf dem Stack und initialisiert diesen mit dem Wert

  • Beim Rücksprung wird der Wert aus der Variable ausgelesen und in einem Register abgelegt

  • Die aufrufende Funktion kopiert den Inhalt des Registers in den Speicherbereich für eine Variable oder in deinem Fall auf den Parameter-Stack der printf-Funktion.

Wenn du Zeiger zurückgibst, dann wird ja nur eine Referenz auf einen Speicherbereich zurückgegeben. Für diesen Zeiger funktioniert der Mechanismus genauso, aber eben nicht für den Inhalt des Speichers auf den der Zeiger zeigt.

Ein Bisschen. In deinem Beispiel funktioniert „Pass by Value“.
Der Compiler weiß, wie er eine Variable vom Typ Double in
Register oder im Speicher platzieren muss, damit die
aufrufende Funktion sie als Rückgabewert verwenden kann. Dabei
wird der Wert mehrmals kopiert:

  • Die Funktion reserviert sich nach dem Aufruf Platz auf dem
    Stack und initialisiert diesen mit dem Wert

  • Beim Rücksprung wird der Wert aus der Variable ausgelesen
    und in einem Register abgelegt

  • Die aufrufende Funktion kopiert den Inhalt des Registers
    in den Speicherbereich für eine Variable oder in deinem Fall
    auf den Parameter-Stack der printf-Funktion.

Wenn du Zeiger zurückgibst, dann wird ja nur eine Referenz auf
einen Speicherbereich zurückgegeben. Für diesen Zeiger
funktioniert der Mechanismus genauso, aber eben nicht für den
Inhalt des Speichers auf den der Zeiger zeigt.

Fazit: Ein String, der aus einer Funktion über einen Zeiger zurückgegeben werden soll, muss global definiert oder zumindest in der verwendenden „Überfunktion“ (z.B. main) definiert sein, weil das bei Zeigerrückgaben aus Funktionen immer so zu handhaben ist, richtig?

thx
moe.

Fazit: Ein String, der aus einer Funktion über einen Zeiger
zurückgegeben werden soll, muss global definiert oder
zumindest in der verwendenden „Überfunktion“ (z.B. main)
definiert sein, weil das bei Zeigerrückgaben aus Funktionen
immer so zu handhaben ist, richtig?

Nein, man kann auch, wie das hier schon gezeigt wurde, in einer Unterfunktion einen Speicherbereich allokieren und einen Zeiger auf diesen Bereich zurückgeben. So reservierter Speicher wird beim Verlassen einer Funktion nicht freigegeben, sondern muss irgendwo anders vom Programm per free() freigegeben werden. Das ist dann der Teil von C, der viele in den Wahnsinn treibt, weil so Memory Leaks und andere sehr unlustige Probleme entstehen :smile:

Ganz einfach:

char\* some\_func( void ) {
 char\* a = (char\*)malloc(256); /\* reserviert 256 Bytes Speicher \*/

 if( NULL != a ) {
 /\* zum Spass den Speicher noch mit dem Zeichen 'a' füllen \*/
 memset( a, (int)'a', 256 );
 }

 return a;
}

int main( void ) {
 char\* platz = some\_func();

 if( NULL != platz ) {
 /\* platz verweist jetzt auf den von some\_func reservierten
 \* Speicher.
 ... platz verwenden ...
 \*/
 } else {
 /\* es ist was schiefgegangen \*/
 }

 /\* free(NULL) ist harmlos... \*/
 free( platz );
}

Fazit: Ein String, der aus einer Funktion über einen Zeiger
zurückgegeben werden soll, muss global definiert oder
zumindest in der verwendenden „Überfunktion“ (z.B. main)
definiert sein, weil das bei Zeigerrückgaben aus Funktionen
immer so zu handhaben ist, richtig?

Hallo Moe,

wenn du das als Regel für dich nimmst, bist du auf der sicheren, überschaubaren Seite. In einem Unterprogramm Speicherplatz anzufordern, aber wo ganz anders wieder freizugeben, führt schnell in Teufels Küche. U.A. kannst du den Speicherplatz ja nur über den Pointer wieder freigeben: hast du den mal verändert, z.B. auf 0 gesetzt, dann gibt es auf den Speicher keinen Zugriff mehr - lost in time and lost in space.

Gruss Reinhard

Hallo FM

Wenn ich es richtig verstanden habe, dann:

  • ist ein String immer ein Char-Array, richtig?
  • kann eine Funktion keine Arrays zurückgeben, richtig?
  • muss ich einen Zeiger auf das erste Element des Arrays
    zurückgeben, richtig?

Weil es zu diesen Dingen im Thread
einige ‚Misconceptions‘ gab, wollte
ich nochmal eine andere Variante
in den Raum werfen.

So umgehen C-Programmierer z.B. die
Beschränkung, dass keine Arrays als
Arrays zurückgegeben werden können.

Man gibt die Arrays einfach in einer
Struktur zurück (falls es mal sein muss).

Siehe z.B.:

 #include 
 #include 
 
 typedef struct { char inhalt[256]; } CharI0;

 CharI0 GetString(void)
{
 CharI0 s;
 strcpy(s.inhalt, "Ob ich hier wohl reinpasse?");
 return s;
}


 int main( void )
{
 CharI0 string; // char-Array in Struktur

 string = GetString(); // Grüße

CMБ

Abend!

So umgehen C-Programmierer z.B. die
Beschränkung, dass keine Arrays als
Arrays zurückgegeben werden können.

Man gibt die Arrays einfach in einer
Struktur zurück (falls es mal sein muss).

Ja auf irgendwelchen Gigahertz Boliden kann man sich so was leisten,
irgendwie müssen die ganzen Hertze ja verballert werden, aber auf
einem kleinen schnuckeligen Rechner wirst du dafür mit mehreren
Monaten Assembler Programmierung bestraft. :smile:

Windows zeigt uns immer wieder, wie man es schafft viele Her(t)zen
zu verblasen ohne wirklich sicherer oder besser zu werden.

Gruß
Stefan

Hallo Stefan

Man gibt die Arrays einfach in einer
Struktur zurück (falls es mal sein muss).

Ja auf irgendwelchen Gigahertz Boliden kann man sich so was
leisten, irgendwie müssen die ganzen Hertze
ja verballert werden, aber auf einem kleinen
schnuckeligen Rechner wirst du dafür mit
mehreren Monaten Assembler Programmierung bestraft. :smile:

Auch wenn man das Denken könnte, das stimmt nicht.

Wenn Du Arrays oder Blocks auf beschriebene Weise
‚direkt‘ Aus Funktionen zurückgibst, so ist das
für kleine Arrays (~ bis 4,096) eventuell
sogar schneller als die (normale) Rückgabe über Zeiger.

Und kürzer + prägnanter noch dazu.

Erst bei größeren Arrays ~ > 20,000 macht
sich die zusätzliche (implizite) Kopiererei
bemerkbar, dann fällt die direkte Methode
zurück.

bei mir: (P3-750, gcc/4 -O3)
kleine Arrays (20x20,direkt mit Struktur ist schneller):

 Array size: 400 bytes ...
 Direct: 390000 clocks, Indirect: 400000 clocks

größere Arrays (200x200,indirekt mit Zeigern ist schneller, wenn auch kaum):

 Array size: 40000 bytes ...
 Direct: 36520000 clocks, Indirect: 33960000 clocks

… nur zur Information.

(Quellen im Anhang)

Grüße

CMБ

 #include 
 #include 
 #include 
 #include 
 #include 
 
 #define SIZE 20
 typedef struct { char elem[SIZE][SIZE]; } ArrI0; // wrapped in struct
 
/\* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \*/

 ArrI0 DirectArrayReturn(void) 
{
 int x,y;
 ArrI0 feld;

 for(y=0; y

Hallo CMБ

Auch wenn man das Denken könnte, das stimmt nicht.

Doch bin ich immer noch von überzeugt.
Zusätzliche Arbeit (kopieren von Daten) kostet zusätzlich
Zeit, da gibt es nix dran zu nörgeln. :smile:
Durch das Füllen des Array holst du natürlich bei kleinen
Größen das gesamte Array in den 1 Level-Cache und es fällt nicht
weiter auf (zumindest bei so Intel x86 Dingern).

bei mir: (P3-750, gcc/4 -O3)

Ich sagte doch Gigahertz Bolide. :smile:
Ich hab hier mehr so 60MHz Altera-NIOS-Softcore-CPUs,
dafür kann ich mir immerhin eigene CPU Befehle bauen.

(Quellen im Anhang)

Ich fürchte du benchst in dem einen Fall nur die
Performance von malloc() und free(). Ist schon ein bisser’l
ungerecht einmal malloc und einmal Stack.

Gruß
Stefan

PS: Kann im Moment leider nichts ausprobieren, nächste Woche erst
wieder.

Danke
.