ubuntuusers.de

[C] Funktion soll String zurückgeben

Status: Gelöst | Ubuntu-Version: Ubuntu 7.10 (Gutsy Gibbon)
Antworten |

Norbert987

Anmeldungsdatum:
27. Dezember 2006

Beiträge: 71

Hallo,

ich habe gestern mit C begonnen zu Programmieren und muss sagen, der Anfang war überraschend einfach\^^ Aber dann hing ich Stunden an einer Funktion. Ich weiß mitlerweile, dass es an dem Rückgabepointer liegt, habe den Fehler aber nicht lösen können. Irgendwann hab ich dann (fast) alles Mögliche versucht, aber habs net hinbekommen. Es wäre wirklich toll, wenn mir jemand Helfen könnte ☹

Nun zur Sache: Ich habe eine Funktion, die den übergebenen Parameter reinigen soll, so dass alle klein Buchstaben zu Großbuchstaben werden und dann nur noch Großbuchstaben und Zahlen zurückgegeben werden. Mein Ansatz ist:

char* clean(char s[100])
{
	char *res = (char *) malloc(100);
	char test[100];
	char h[10] = "X";
	int i;
	for (i = 0; i<strlen(s); i++)
	{
		s[i] = toupper(s[i]);
		if (((s[i]>='A') && (s[i]<= 'Z')) ||
		    ((s[i]>='0') && (s[i]<= '9')))
		{
			strncpy(h,&s[i],1);
			strcat(test,h);
		}
	}

//	strcpy(res, test);
	return(res);
}

Der Aufruf lautet wie folgt:

printf(strcat("\nCodierung: ", clean(text)));

Text ist ein Char[100] und wird mittels scanf("%s", text); gefüllt. Beim Compilieren bekomme ich den "Fehler: hello.c:58: Fehler: inkompatible Typen in Zuweisung" welcher sich auf den Aufruf von clean bezieht." Zuvor hatte ich es ohne Malloc und anderen Variationen.

Vielen Dank im Vorraus, Stone

Dee Team-Icon

Avatar von Dee

Anmeldungsdatum:
9. Februar 2006

Beiträge: 20095

Wohnort: Schwabenländle

Zu wenig Input. Es wäre hilfreich, wenn man sehen würde, was Zeile 58 ist und Dein gesamtes Programm, also nicht nur die Auszüge.

Was ich bisher schon mal programmiertechnisch sehe. Du allokierst Speicher in clean() für hundert Zeichen, gibst den aber nirgends mehr frei. Sowas ist gar nicht gut. Vor allem, wenn Du das nicht einmal, sondern tausendmal machst. Wieso allokierst Du überhaupt neuen Speicher und machst keine Veränderung in Deinem s direkt? Wenn der Programmierer (also Du) eine Kopie will, dann sollte er sie draußen selbst anlegen.

Naja, jedenfalls ist Dein Fehler nicht clean, sondern strcat. Syntax ist

char *strcat(char *kett1, const char *kett2)

Und "\nCodierung: " ist nun mal const. Wohin soll er kett2 auch kopieren, Du hast ja gar kein Speicherplatz reserviert.

Gruß, Dee

Norbert987

(Themenstarter)

Anmeldungsdatum:
27. Dezember 2006

Beiträge: 71

@Dee: Vielen Dank für deine schnelle Antwort.
Sorry, dachte es wäre klar, dass Zeile 58 der Aufruf von clean ist. Der komplette Code ist:

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

char* clean(char s[100])
{
	char *res = (char *) malloc(100);
	char test[100];
	char h[10] = "X";
	int i;
	for (i = 0; i<strlen(s); i++)
	{
		s[i] = toupper(s[i]);
		if (((s[i]>='A') && (s[i]<= 'Z')) ||
		    ((s[i]>='0') && (s[i]<= '9')))
		{
			strncpy(h,&s[i],1);
			strcat(test,h);
		}
	}

//	strcpy(res, test);
	return(res);
	free(s); //Freigabe in diesem Sinne?
}

int main(void)
{
	char codewort[100];
	char text[100];
	char entschluesseln[2];
	char crypt[100];

// Dateneingabe
	printf("Codewort: ");
	scanf("%s", codewort);

	printf("Text: ");
	scanf("%s", text);

	printf("Text verschlüsselen (1) oder entschlüsselen (2)? ");
	scanf("%s", entschluesseln);

// ver- oder entschlüsseln (1)(2) bis Auswahl richtig
	while ((entschluesseln[0] != '1') && (entschluesseln[0] != '2') && (strlen(entschluesseln) == 1))
	{
		printf("Ihre Eingabe war Fehlerhaft. Soll dieser Text verschlüsselt (1) oder entschlüsselt (2) werden? ");
		scanf("%s", entschluesseln);
	}

	crypt = clean(text);

// eigentliche ver- entschlüsselung
	printf(entschluesseln);
        if (entschluesseln[0] == '1')
        {
                printf(strcat("\nCodierung: ", clean(codewort)));
                printf(strcat("\nCodewort:  ", clean(text)));
                printf("\n");
//...hier fehlt noch einiges
        } else {
		printf(clean(codewort));
                printf(strcat("\nChiffre:  ", clean(codewort)));
                printf(strcat("\nCodewort: ", clean(text)));
                printf("\n");
//...hier fehlt noch einiges
        }
	return (0);
} 

Die Fehlermeldung (durch eine erneute Formatierung, damit es besser zu lesen ist):

tobias@tobias-desktop:~/Desktop$ gcc hello.c -Wall
hello.c: In Funktion »main«:
hello.c:51: Fehler: inkompatible Typen in Zuweisung

Wieso allokierst Du überhaupt neuen Speicher und machst keine Veränderung in Deinem s direkt?

Hört sich gut an, kannst du mir sagen wie? 🙄

Naja, jedenfalls ist Dein Fehler nicht clean, sondern strcat. Syntax ist

char *strcat(char *kett1, const char *kett2)


Und "\nCodierung: " ist nun mal const. Wohin soll er kett2 auch kopieren, Du hast ja gar kein Speicherplatz reserviert.

Naja, hab bis jetzt in Delphi und Java programmiert und da konnte direkt mit dem Rückgabewert gearbeitet werden. Oder muss ich das erst in einer neuen Variablen zusammensetzen lassen um es dann ausgeben zu können?

Dee Team-Icon

Avatar von Dee

Anmeldungsdatum:
9. Februar 2006

Beiträge: 20095

Wohnort: Schwabenländle

Oder muss ich das erst in einer neuen Variablen zusammensetzen lassen um es dann ausgeben zu können?

Jupp. Wie gesagt ist ein String "..." immer ein const-Objekt. Und da kann strcat eben nix dranhängen. Die String-Funktionalität in C ist sehr ... äh, einfach gehalten.

hello.c:51: Fehler: inkompatible Typen in Zuweisung

Auch das ist klar. crypt ist vom Typ char test[100], Du gibst in clean aber einen Pointer zurück. Also mach aus crypt einen Pointer. Dann kannst Du den allokierten Speicher in clean() wenigstens auch wieder freigeben.

Hört sich gut an, kannst du mir sagen wie?

Also allgemein finde ich String-Handling mit Arrays umständlich. Benutze doch besser nur Pointer. Dann hast Du den richtigen Ansatz, aber setzt ihn falsch um. Ich würde einen Pointer (oder integer-Zähler) laufen lassen, der angibt, an welche Stelle Du grade im String s schreiben willst. So kannst Du leicht Elemente überspringen.

In Deinem Code ist da aber einiges noch falsch. Zum einen gibst Du res zurück, vergisst aber test reinzukopieren. Zum anderen muss das "free" vor return erfolgen und auch nur für malloc-allokierte Sachen. Das heißt nicht s, sondern wenn schon res. Zusätzlich solltest Du in einer Funktion niemals (oder zumindest nicht, wenn es sich verhindenr läßt) Objekte freigeben, die von außen kommen. Das führt meist nur zu Seg Faults.

Gruß, Dee

Marc_BlackJack_Rintsch Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

Anmeldungsdatum:
16. Juni 2006

Beiträge: 4677

Wohnort: Berlin

crypt hat den Typ char[100] und die Funktion gibt einen Zeiger auf eine Zeichenkette (char*) zurück. Diese beiden Typen passen nicht zusammen. Ein (etwas hinkender) Vergleich: Versuch mal in Java einer Variablen vom Typ char[] eine Zeichenkette zuzuweisen. Das geht auch nicht.

Die Freigabe nach dem return funktioniert natürlich nicht. Der Code wird ja nie ausgeführt. Du musst Dir Gedanken darüber machen wann Du den angeforderten Speicher nicht mehr benötigst und ihn dann freigeben.

In der Parameterliste einer Funktion ein Array bestimmter Länge stehen zu haben ist in C auch recht ungewöhnlich. clean() würde man eher einen Zeiger auf eine Zeichenkette übergeben. Wenn die Funktion diese Zeichenkette dann direkt verändern/säubern soll, kann man das so machen:

void cleanx(char *s)
{
    char *t = s;
    while (*s) {
        if (isalnum(*s)) *t++ = toupper(*s);
        s++;
    }
    *t = '\0';
} 

Dein Quelltext verwendet übrigens Nicht-Standard-Kommentare. Das heisst der neueste C-Standard kennt zwar mittlerweile // als Kommentar aber ich würde noch bei /*…*/ bleiben, damit der Code portabler bleibt. Wenn man dem Compiler die Optionen -Wall -ansi -pedantic mitgibt, bekommt man mehr Warnungen und Fehlerhinweise beim übersetzen.

Das Einlesen von Zeichenketten mit scanf() ohne Längenbegrenzung ist gefährlich. Wenn mehr eingegeben wird, dann wird einfach Speicher überschrieben, was immer da auch steht.

Zum strcat(): Auch in C kann man mit den Rückgabewerten direkt arbeiten, das ist hier nicht das Problem. Die Funktion kopiert Bytes von der zweiten Zeichenkette hinter die erste Zeichenkette. Dazu muss da aber 1. Platz vorgesehen sein und 2. muss in den Speicher geschrieben werden dürfen. Wenn man eine literale Zeichenkette angibt, dann wird da aber 1. kein Platz dahinter freigelassen, warum auch und wieviel sollte das denn sein?; und 2. wird die üblicherweise in einem schreibgeschützten Speicherbereich abgelegt, das heisst selbst ein Schreibzugriff innerhalb der Zeichenkette führt zu einem Programmabbruch (segfault).

Wenn Du die Zeichenkette wirklich "zusammenbauen" willst, dann müsstest Du dafür selber Speicher vorsehen und beide dort hinkopieren. Aber Du willst sie ja nur ausgeben. Dafür ist das erste Argument von printf() nicht einfach eine Zeichenkette, sondern wird als Formatierungsanweisung für die weiteren Argumente benutzt:

                printf("\nCodierung: %s", codewort);
                printf("\nCodewort:  %s", text);

Norbert987

(Themenstarter)

Anmeldungsdatum:
27. Dezember 2006

Beiträge: 71

@Dee, Marc 'BlackJack': 1000 Dank. Ich werd es später mal versuchen ☺

EDIT: super, jetzt läufts ☺

Antworten |