C warum funktioniert das proramm nicht?

Tr3x

Lieutenant
Registriert
Feb. 2007
Beiträge
650
hallo,

hab mir gerade eine alte probeklausur angsehen, da ich bald eine klausur schreiben darf.
darunter war folgende aufgabe:

warum funktioniert das programm nicht:

Code:
#include <stdio.h>

char *copy(char s[6]) {
char t[6] ;
int i;
for (i=0; i< 6; i++)
t[i] = s[i];
return t;
}

int main(void) {
printf("%s\n", copy( "Hallo"));
return 0;
}

ich weiß bisher nur das es an zeile 7 (return t) happert....nur nich warum?
wenn mir jemand da bitte helfen könnte. danke
 
Zuletzt bearbeitet:
Schau dir mal die Gültigkeitsbereiche der Variablen an.
Und bitte pack Code in Zukunft in
Code:
-Tags.
 
hmmm... er verlangt mit %s einen string. aber der rückgabewert ist ein char?
 
Nein, Variablen sind immer nur innerhalb der { bzw } Klammern gültig. (siehe Wikipedia)

t wird innerhalb der Funktion deklariert, ist also auch nur dort gültig.
Die Funktion gibt aber einen Zeiger auf diese interne Variable zurück, an diesem Ort kann aber schon etwas ganz anderes stehen wenn in der main-Funktion darauf zugegriffen wird.

Ich bekomme da vom Compiler auch die entsprechende Warnung:
Code:
klaufg.c:8:3: warning: function returns address of local variable

Um das zu beheben gibt es verschiedene Möglichkeiten. Entweder man alloziert den Speicher in der main-Funktion und gibt der Funktion einen Pointer auf diesen Speicher mit, wo diese dann reinschreibt oder man verwendet eine globale Variable auf die alle Funktionen zugriff haben (nicht so schön, kommt aber auf den Anwendungsfall an)
 
momento...
char s[6] ist ja wird übergeben mit hallo. da es nicht im rumpf steht. reicht es doch aus return s zu machen. oder gehört zeile 3 mit char*.... auch als deklaration in der funktion?
 
char s[6] ist ein Parameter deiner Funktion copy.

Da der Parameter in dem Fall kopiert wird, wird er am Ende der Funktion wieder gelöscht, ebenso wie char t[6].

Wenn ich nicht falsch liege musst du hier neuen Speicher mit malloc allokieren und den Pointer zurückgeben.

Da du dynamischen Speicher auch wieder freigeben solltest, legst du das ganze in einer Variablen ab und löschst diese später wieder mit free.

So:

Code:
#include <stdlib.h>
#include <stdio.h>

char *copy(char s[6]) {
    char* t = (char*) malloc( sizeof( char ) * 6 ) ; // Wir holen uns Speicher für 6 * char
    int i;
    for (i=0; i< 6; i++)
    t[i] = s[i];
    return t;
}

int main(void) {
    
    char* word = copy("Hallo");
    
    printf("%s\n", word );

    free( word ); // Oben geholten Speicher wieder freigeben.
    
    return 0;
}
 
Zuletzt bearbeitet:
Klar kannst du s zurückgeben, dann hast du aber keine Kopie gemacht und die Funktion ist nutzlos.

Ich vergaß zu erwähnen, dass man natürlich auch per malloc in der Funktion dynamisch den Speicher reservieren kann. Dann muss man diesen allerdings außerhalb wieder freigeben, weshalb man das nicht machen sollte. Aber nun nochmal zurück zu dem Beispiel:

Code:
#include <stdio.h>

char *copy(char s[6]) {
  char t[6] ;
  int i;
  for (i=0; i< 6; i++)
    t[i] = s[i];
  return t;
}

int main(void) {
  printf("%s\n", copy( "Hallo"));
  return 0;
}
Das Programm beginnt in der main-Funktion, dort wird printf mit dem Argument copy("Hallo") aufgerufen. Das entspricht diesem Code:
Code:
char* input = "Hallo";
printf("%s\n", copy(input));
Dadurch dass du "Hallo" an copy übergibst, übergibst du also einen char-Pointer. Du hast jetzt im Speicher irgendwo dein "Hallo" stehen und übergibst einen Zeiger darauf (also nur die Adresse) an die copy-Funktion.

In der copy-Funktion wird jetzt ein neues Array angelegt (t), dass aber nur innerhalb dieser Gültigkeit hat (ein Array ist nichts anderes als ein Pointer, der auf das erste Element zeigt).
Dann werden 6 bytes (chars) beginnend bei s in den Speicher beginnend bei t kopiert. Genauso könnte man schreiben:
Code:
for (i = 0; i < 6; i++)
  *(t+i) = *(s+i);
Was du dann aber zurückgibst ist ein Zeiger, der auf das lokal, in der copy-Funktion, deklarierte t zeigt, also nur eine Adresse. Wenn jetzt allerdings in der main-Funktion darauf zugegriffen wird gehört dieser Speicher garnicht mehr deinem Programm.

Mal ein Beispiel wie es funktionieren würde:
Code:
#include <stdio.h>
#include <stdlib.h>

/* kopiert eine vorgegebene Zahl bytes (chars) */
void copy(const char* source, char* target, int count)
{
  int i;
  for (i = 0; i < count; i++)
    target[i] = source[i];
}

/* kopiert bis das stringende erreicht ist (0) */
void strcopy(const char* source, char* target)
{
  int i = 0;
  while (source[i] != 0)
  {
    target[i] = source[i];
    i++;
  }
[COLOR=Red]  target[i] = '\0'; /* korrektur von asdfman */[/COLOR]
}

int main(int argc, char** argv)
{
  char* str1 = "Hello";
  char* str2 = "World";

  /* statische speicherallozierung */
  char target1[6];
  copy(str1, target1, 6);
  printf("str1: %s\ntarget1: %s\n", str1, target1);

  /* dynamische speicherallozierung */
  char* target2 = malloc(6);
  strcopy(str2, target2);
  printf("str2: %s\ntarget2: %s\n", str2, target2);
  free(target2);

  return 0;
}

/edit: Korrektur eingefügt, siehe asdfman unten.
 
Zuletzt bearbeitet: (main-return)
Dein strcpy() versagt, wenn source und target überlappen.
€: Ich muss mich korrigieren. Dein strcpy() versagt immer.
 
Zuletzt bearbeitet:
Ich bestätige hiermit Whiz-zarD.

€:
Code:
#include <stdio.h>
#include <stdlib.h>

void strcopy(const char* source, char* target)
{
  int i = 0;
  while (source[i] != 0)
  {
    target[i] = source[i];
    i++;
  }
  target[i] = '\0';     /* Kleiner Bug gefixt von asdfman */
}


int main(void) {
    char t[256];

    strcopy("Hallo!", t);
    printf(" '%s'\n", t);

    /* Grosser Bug demonstriert hier. */
    strcopy(t, t + 2);
    printf(" '%s'\n", t);
    return EXIT_SUCCESS;
}

Habe dein strcpy() ein bisschen gefixt, so dass es manchmal funktioniert.
Aber wie man offensichtlich sieht, versagt es in bestimmten Fällen gnadenlos.

Ich empfehle also nicht, das so zu machen.
 
Zuletzt bearbeitet:
Ok man sollte es wenigstens testen bevor man's postet...

Das normale strcpy zu verwenden ist ja in diesem Fall eher weniger hilfreich, da es nur um ein Beispiel ging.
Dass es mit überlappenden Quell- und Zielstrings nicht klappt finde ich für das Beispiel nicht allzu schlimm, war auch nicht wirklich mein Ziel eine praktisch einsetzbare Funktion zu schreiben, dafür gibt's ja das eingebaute strcpy.

Außerdem ist der ein oder andere Segfault ja auch mal was nettes.

/edit: gerade mal bei Wikipedia geschaut, die dort angegebene implementierung hat das gleiche Problem: http://en.wikipedia.org/wiki/Strcpy
Die standard-strcpy scheint es aber nicht zu haben, eventuell wird vorher die Länge des Strings bestimmt (wäre aber langsamer)?
 
Zuletzt bearbeitet:
Backslash schrieb:
Die standard-strcpy scheint es aber nicht zu haben, eventuell wird vorher die Länge des Strings bestimmt (wäre aber langsamer)?

Wen interessiert es, wenn eine Funktion um so viel langsamer ist, sodass es erst bei 100.000 Durchläufen (oder mehr) überhaupt messbar ist?
Lieber benutz ich eine Funktion, die etwas langsamer ist, aber dafür zuverlässig seinen Dienst verrichtet, als eine selbst gefrickelte Funktion, die in bestimmten Fällen Fehler ausspuckt, weil nicht an alle Fehlerquellen gedacht wurde.

Ich kenn zwar die Implementierung von strcpy() nicht, aber ich kann mir vorstellen, dass dort memmove() aufgerufen wird. Bei der Funktion wird eine Anzahl von Bytes kopiert. Darüber hinaus garantiert diese Funktion, dass die korrekte Anzahl an Bytes kopiert wird. Also eine sichere Variante von memcpy(). Und all diese Funktionen sind sogar in der String.h implementiert.
 
strcpy ist nicht langsamer sondern viel schneller.

Und wie gesagt ging es hier garnicht darum sondern eigentlich um den Gültigkeitsbereich von Variablen, und da das Kopieren nun mal das Beispiel war habe ich etwas entsprechendes gepostet. Dass das nicht gerade praktisch eingesetzt werden sollte ist ja wohl selbstverständlich.
 
So könnte es auch gehen jedenfals unter Visual Studio 2010.
Es ist aber nicht unbedingt die sauberste Lösung, da der angeforderte Speicher in der
Function "copy" erst außerhalb wieder Freigegeben wird.

PHP:
char*	copy (char s[6]) 
{
	int i;
	char* t = new char[6] ;			        // Speicher im Heap erstellen, bleibt nach verlassen der Function erhalten
	if (t != NULL)					        // wenns geklappt hat
	{ 
		memset ( t, 0x00, 6 );		        // jedes Element des array's auf null setzen
		for (i=0; i< 6; i++)	t[i] = s[i];	    // Inhalt umkopieren
		return &t[0];				        // rückgabe, adresse vom ersten Element
	}
	return NULL;
}



int _tmain(int argc, _TCHAR* argv[])
{

	char* tString = copy( "Hallo");
	
	if (tString != NULL)
	{
		printf("%s\n", tString);
		delete [] tString;                 //angeforderten Speicher aus "copy" wieder freigeben
		tString = NULL;                    //Zeiger auf 0 setzen
	}
	
        // nachfolgentder Code dient nur dazu, das Konsolenfenster erst nach Eingabe von "0" zu beenden
      
        int nRetCode = 1;

	while(nRetCode != 0)
	{
		std::cout << "\nzum beenden(0)"<< std::endl;
		std::cin >> nRetCode;
	}
	
	return nRetCode;
}

Anmerkung:

Die Funktion "copy" ist eigentlich ein Paradebeispiel dafür, wie man es nie machen sollte.
den ersten Grund hatte ich weiter oben schon beschrieben. Als nächstes muß man noch beachten,
daß man nur "strings" mit einer länge von 5 Buchstaben kopieren kann.
Sind sie länger, werden zwar 6 Buchstaben kopiert, am ende fehlt abern die terminierende "0".
Sind sie kürzer wird auf Speicherbereiche zugegriffen, die nicht mehr zu "string" gehören

nachfolgender Code zum kopieren von strings mit unterschiedlicher länge
PHP:
char*	copy (const char* s) 
{
	int i, tlenght = 0;
	while (s[tlenght] != NULL ) tlenght++;			// länge von string zählen
    if (tlenght == 0 ) return NULL;                   // raus  hier bei leeren string
	tlenght++;										// einen dazu addieren für die null am Ende

	char* t = new char[tlenght] ;					 // Speicher im Heap erstellen, bleibt nach verlassen der Function erhalten
	if (t != NULL)									// wenns geklappt hat
	{ 
		memset ( t, 0x00, tlenght );				  // jedes Element des array's auf null setzen
		memcpy( t, s, tlenght);                       // Inhalt umkopieren
		return &t[0];								 // rückgabe, adresse vom ersten Element
	}
	return NULL;
}
 
Zuletzt bearbeitet: (weitere Erläuterungen eingefügt)
Wo gibts eine idiotensichere Anleitung wo man diese Sprache lernen kann? Ich hab soweit 0,0 Ahnung davon.
 
@toomanyhumans: Wenn du etwas idiotensicheres haben willst, dann lass die Finger von C/C++. Wer sich hier nicht wirklich 100% auskennt wird immer grobe Fehler machen. Da helfen auch Anleitungen nichts.

Wir sind aber schon wieder weit weg vom eigentlichen Thema.
 
Zuletzt bearbeitet:
hm k, ich wollte schon lange mal eine Programmiersprache lernen...Empfehlungen?
 
Zurück
Oben