Ich versuche, eine C-Zeichenfolge von einer Funktion zurückzugeben, aber es funktioniert nicht. Hier ist mein Code.
char myFunction()
{
return "My String";
}
In main
nenne ich es so:
int main()
{
printf("%s", myFunction());
}
Ich habe auch einige andere Möglichkeiten ausprobiert myFunction
, aber sie funktionieren nicht. Beispielsweise:
char myFunction()
{
char array[] = "my string";
return array;
}
Hinweis: Ich darf keine Zeiger verwenden!
Wenig Hintergrund zu diesem Problem:
Es gibt eine Funktion, die herausfindet, welcher Monat es ist. Wenn es beispielsweise 1 ist, wird Januar usw. zurückgegeben.
Wenn es gedruckt werden soll, geht es folgendermaßen vor : printf("Month: %s",calculateMonth(month));
. Das Problem ist nun, wie diese Zeichenfolge von der calculateMonth
Funktion zurückgegeben werden kann.
return 0
wird standardmäßig nur in C99 (und C ++) impliziert, nicht jedoch in C90.Antworten:
Ihre Funktionssignatur muss sein:
Hintergrund:
Es ist so grundlegend für C & C ++, aber es sollte wenig mehr Diskussion angebracht sein.
In C (und auch in C ++) ist eine Zeichenfolge nur ein Array von Bytes, die mit einem Null-Byte abgeschlossen sind. Daher wird der Begriff "Zeichenfolge-Null" verwendet, um diese bestimmte Zeichenfolge darzustellen. Es gibt andere Arten von Zeichenfolgen, aber in C (& C ++) wird diese Variante von der Sprache selbst von Natur aus verstanden. Andere Sprachen (Java, Pascal usw.) verwenden andere Methoden, um "meine Zeichenfolge" zu verstehen.
Wenn Sie jemals die Windows-API (in C ++) verwenden, werden regelmäßig Funktionsparameter wie "LPCSTR lpszName" angezeigt. Der 'sz'-Teil repräsentiert diesen Begriff von' String-Null ': ein Array von Bytes mit einem Null-Terminator (/ Null).
Klärung:
Für dieses "Intro" verwende ich die Wörter "Bytes" und "Zeichen" synonym, da es einfacher ist, auf diese Weise zu lernen. Beachten Sie, dass es andere Methoden gibt ( Breitzeichen- und Multi-Byte-Zeichensysteme ( MBCs )), die zur Bewältigung internationaler Zeichen verwendet werden. UTF-8 ist ein Beispiel für ein mbcs. Um das Intro zu verbessern, überspringe ich das alles leise.
Erinnerung:
Dies bedeutet, dass eine Zeichenfolge wie "meine Zeichenfolge" tatsächlich 9 + 1 (= 10!) Bytes verwendet. Dies ist wichtig zu wissen, wenn Sie endlich dazu kommen, Zeichenfolgen dynamisch zuzuweisen.
Ohne diese 'Null beenden' haben Sie also keine Zeichenfolge. Sie haben eine Reihe von Zeichen (auch als Puffer bezeichnet) im Speicher.
Langlebigkeit der Daten:
Die Verwendung der Funktion folgendermaßen:
... wird Sie im Allgemeinen mit zufälligen unbehandelten Ausnahmen / Segmentfehlern und dergleichen landen, insbesondere "auf der Straße".
Kurz gesagt, obwohl meine Antwort richtig ist - 9 von 10 Fällen erhalten Sie ein Programm, das abstürzt, wenn Sie es auf diese Weise verwenden, insbesondere wenn Sie der Meinung sind, dass es eine gute Praxis ist, dies auf diese Weise zu tun. Kurzum: Im Allgemeinen nicht.
Stellen Sie sich zum Beispiel irgendwann in der Zukunft vor, dass die Zeichenfolge jetzt auf irgendeine Weise manipuliert werden muss. Im Allgemeinen nimmt ein Codierer den einfachen Weg und schreibt Code wie folgt:
Das heißt, wird Ihr Programm zum Absturz bringen , da der Compiler (kann / darf nicht) , um den von belegten Speicher freigegeben hat
szBuffer
durch die Zeit , dieprintf()
inmain()
genannt wird. (Ihr Compiler sollte Sie auch vorher vor solchen Problemen warnen.)Es gibt zwei Möglichkeiten, Zeichenfolgen zurückzugeben, die nicht so schnell gesperrt werden.
std::string
, um die Langlebigkeit von Daten zu bewältigen (für die der Rückgabewert der Funktion geändert werden muss), oderBeachten Sie, dass es unmöglich ist, Zeichenfolgen ohne Verwendung von Zeigern in C zu verwenden. Wie ich gezeigt habe, sind sie synonym. Selbst in C ++ mit Vorlagenklassen werden immer Puffer (dh Zeiger) im Hintergrund verwendet.
Also, um die (jetzt modifizierte Frage) besser zu beantworten. (Es gibt sicher eine Vielzahl von "anderen Antworten", die bereitgestellt werden können.)
Sicherere Antworten:
Beispiel 1 mit statisch zugewiesenen Zeichenfolgen:
Was die 'statische' hier tut (viele Programmierer mögen diese Art der 'Zuweisung' nicht), ist, dass die Zeichenfolgen in das Datensegment des Programms eingefügt werden. Das heißt, es ist dauerhaft zugewiesen.
Wenn Sie zu C ++ wechseln, verwenden Sie ähnliche Strategien:
... aber es ist wahrscheinlich einfacher, Hilfsklassen zu verwenden, z. B.
std::string
wenn Sie den Code für Ihren eigenen Gebrauch schreiben (und nicht Teil einer Bibliothek, die für andere freigegeben werden soll).Beispiel 2 unter Verwendung von vom Anrufer definierten Puffern:
Dies ist die "narrensicherere" Art, Saiten herumzugeben. Die zurückgegebenen Daten unterliegen keiner Manipulation durch den Anrufer. Das heißt, Beispiel 1 kann leicht von einem anrufenden Teilnehmer missbraucht werden und Sie Anwendungsfehlern aussetzen. Auf diese Weise ist es viel sicherer (obwohl mehr Codezeilen verwendet werden):
Es gibt viele Gründe, warum die zweite Methode besser ist, insbesondere wenn Sie eine Bibliothek schreiben, die von anderen verwendet werden soll (Sie müssen sich nicht auf ein bestimmtes Zuordnungs- / Freigabeschema festlegen, Dritte können Ihren Code nicht beschädigen). und Sie müssen keine Verknüpfung zu einer bestimmten Speicherverwaltungsbibliothek herstellen), aber wie bei jedem Code liegt es an Ihnen, was Ihnen am besten gefällt. Aus diesem Grund entscheiden sich die meisten Menschen für Beispiel 1, bis sie so oft verbrannt wurden, dass sie sich weigern, es so zu schreiben;)
Haftungsausschluss:
Ich bin vor einigen Jahren in den Ruhestand gegangen und mein C ist jetzt etwas rostig. Dieser Demo-Code sollte alle ordnungsgemäß mit C kompiliert werden (dies ist jedoch für jeden C ++ - Compiler in Ordnung).
quelle
char *
, da Zeichenfolgenliterale in C vom Typ sindchar[]
. Sie dürfen jedoch in keiner Weise geändert werden, daher wird die Rückgabeconst char*
bevorzugt (siehe Securecoding.cert.org/confluence/x/mwAV ). Eine Rückgabechar *
kann erforderlich sein, wenn die Zeichenfolge in einer Legacy- oder externen Bibliotheksfunktion verwendet wird, die (leider) ein As-char*
Argument erwartet , auch wenn sie nur schwer zu lesen ist. C ++ hingegen verfügt über String-Literale vomconst char[]
Typ (und seit C ++ 11 können Sie auchstd::string
Literale verwenden).fraught with problems
im Abschnitt "Langlebigkeit der Daten" gekennzeichnete Code ist tatsächlich vollkommen gültig. String-Literale haben in C / C ++ statische Lebensdauern. Siehe den Link, den Giorgi oben erwähnt.Eine AC-Zeichenfolge ist als Zeiger auf ein Array von Zeichen definiert.
Wenn Sie keine Zeiger haben können, können Sie per Definition keine Zeichenfolgen haben.
quelle
void foo( char array[], int length)
. Natürlicharray
ist es ein Zeiger unter der Haube, aber es ist nicht "explizit" ein Zeiger, und es kann daher für jemanden intuitiver sein, der Arrays lernt, aber nicht ganz Zeiger gelernt hat.Beachten Sie diese neue Funktion:
Ich habe "Array" als statisch definiert. Andernfalls wird beim Beenden der Funktion die Variable (und der von Ihnen zurückgegebene Zeiger) nicht mehr angezeigt. Da dieser Speicher auf dem Stapel zugewiesen ist, wird er beschädigt. Der Nachteil dieser Implementierung ist, dass der Code nicht wiedereintrittsfähig und nicht threadsicher ist.
Eine andere Alternative wäre, malloc zu verwenden, um die Zeichenfolge im Heap zuzuweisen und dann an den richtigen Stellen Ihres Codes freizugeben. Dieser Code ist wiedereintrittsfähig und threadsicher.
Wie im Kommentar erwähnt, ist dies eine sehr schlechte Vorgehensweise, da ein Angreifer dann Code in Ihre Anwendung einfügen kann (er muss den Code mit GDB öffnen, dann einen Haltepunkt erstellen und den Wert einer zurückgegebenen Variablen in Überlauf und ändern Spaß fängt gerade erst an).
Es wird viel mehr empfohlen, den Anrufer mit der Speicherzuweisung zu beauftragen. Siehe dieses neue Beispiel:
Beachten Sie, dass der einzige Inhalt, der geändert werden kann, der des Benutzers ist. Ein weiterer Nebeneffekt: Dieser Code ist jetzt threadsicher, zumindest aus Sicht der Bibliothek. Der Programmierer, der diese Methode aufruft, sollte überprüfen, ob der verwendete Speicherabschnitt threadsicher ist.
quelle
Ihr Problem ist der Rückgabetyp der Funktion - es muss sein:
... und dann funktioniert Ihre ursprüngliche Formulierung.
Beachten Sie, dass Sie keine C-Zeichenfolgen haben können, ohne dass Zeiger irgendwo entlang der Linie beteiligt sind.
Außerdem: Erhöhen Sie die Compiler-Warnungen. Es sollte haben Sie über diese Rückleitung warnt eine Umwandlung
char *
zu ,char
ohne eine explizite Umwandlung.quelle
Basierend auf Ihrer neu hinzugefügten Hintergrundgeschichte mit der Frage, warum nicht einfach eine Ganzzahl von 1 bis 12 für den Monat zurückgeben und die main () - Funktion eine switch-Anweisung oder eine if-else-Leiter verwenden lassen, um zu entscheiden, was gedruckt werden soll. Es ist sicherlich nicht der beste Weg - char * wäre -, aber im Kontext einer solchen Klasse stelle ich mir vor, dass es wahrscheinlich der eleganteste ist.
quelle
Sie können das Array im Aufrufer erstellen, der die Hauptfunktion darstellt, und das Array an den Angerufenen übergeben, der Ihre myFunction () ist. Somit kann myFunction den String in das Array füllen. Sie müssen jedoch myFunction () als deklarieren
Und in der Hauptfunktion sollte myFunction folgendermaßen aufgerufen werden:
Es wird jedoch weiterhin ein Zeiger verwendet.
quelle
Ihr Funktionsrückgabetyp ist ein einzelnes Zeichen (
char
). Sie sollten einen Zeiger auf das erste Element des Zeichenarrays zurückgeben. Wenn Sie keine Zeiger verwenden können, sind Sie geschraubt. :((quelle
Oder wie wäre es mit diesem:
Und nennen Sie das mit dem Monat, den Sie woanders berechnen.
quelle
A
char
ist nur ein einzelnes Ein-Byte-Zeichen. Es kann weder die Zeichenfolge speichern, noch ist es ein Zeiger (den Sie anscheinend nicht haben können). Daher können Sie Ihr Problem nicht lösen, ohne Zeiger zu verwenden (waschar[]
syntaktischer Zucker ist).quelle
Wenn Sie wirklich keine Zeiger verwenden können, gehen Sie wie folgt vor:
Die magische Zahl 9 ist schrecklich, und dies ist kein Beispiel für eine gute Programmierung. Aber du verstehst, worum es geht. Beachten Sie, dass Zeiger und Arrays dasselbe sind (Art von), also ist dies ein bisschen Betrug.
quelle
Nun, in Ihrem Code versuchen Sie, a zurückzugeben
String
(in C ist das nichts anderes als ein nullterminiertes Array von Zeichen), aber der Rückgabetyp Ihrer Funktion istchar
der, der Ihnen alle Probleme bereitet. Stattdessen sollten Sie es so schreiben:Und es ist immer gut, Ihren Typ zu qualifizieren,
const
während Sie Zeigern Literale in C zuweisen, da Literale in C nicht geändert werden können.quelle
Ihr Funktionsprototyp gibt an, dass Ihre Funktion ein Zeichen zurückgibt. Daher können Sie in Ihrer Funktion keine Zeichenfolge zurückgeben.
quelle
In C sind Zeichenfolgenliterale Arrays mit der Speicherklasse der statischen Konstanten, sodass die Rückgabe eines Zeigers auf dieses Array sicher ist. Weitere Details finden Sie in der Stapelüberlauf-Frage "Lebensdauer" eines Zeichenfolgenliteral in C.
quelle
Zeichenfolge von Funktion zurückgeben
quelle