Ihre Funktionssignatur muss sein:
const char * myFunction()
{
return "My String";
}
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:
const char * myFunction()
{
return "My String";
}
int main()
{
const char* szSomeString = myFunction(); // Fraught with problems
printf("%s", szSomeString);
}
... 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:
const char * myFunction(const char* name)
{
char szBuffer[255];
snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
return szBuffer;
}
Das heißt, wird Ihr Programm zum Absturz bringen , da der Compiler (kann / darf nicht) , um den von belegten Speicher freigegeben hat szBufferdurch die Zeit , die printf()in main()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.
- Rückgabe von Puffern (statisch oder dynamisch zugewiesen), die eine Weile leben. Verwenden Sie in C ++ beispielsweise 'Hilfsklassen'
std::string, um die Langlebigkeit von Daten zu bewältigen (für die der Rückgabewert der Funktion geändert werden muss), oder
- Übergeben Sie einen Puffer an die Funktion, die mit Informationen gefüllt wird.
Beachten 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:
const char* calculateMonth(int month)
{
static char* months[] = {"Jan", "Feb", "Mar" .... };
static char badFood[] = "Unknown";
if (month<1 || month>12)
return badFood; // Choose whatever is appropriate for bad input. Crashing is never appropriate however.
else
return months[month-1];
}
int main()
{
printf("%s", calculateMonth(2)); // Prints "Feb"
}
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:
class Foo
{
char _someData[12];
public:
const char* someFunction() const
{ // The final 'const' is to let the compiler know that nothing is changed in the class when this function is called.
return _someData;
}
}
... aber es ist wahrscheinlich einfacher, Hilfsklassen zu verwenden, z. B. std::stringwenn 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):
void calculateMonth(int month, char* pszMonth, int buffersize)
{
const char* months[] = {"Jan", "Feb", "Mar" .... }; // Allocated dynamically during the function call. (Can be inefficient with a bad compiler)
if (!pszMonth || buffersize<1)
return; // Bad input. Let junk deal with junk data.
if (month<1 || month>12)
{
*pszMonth = '\0'; // Return an 'empty' string
// OR: strncpy(pszMonth, "Bad Month", buffersize-1);
}
else
{
strncpy(pszMonth, months[month-1], buffersize-1);
}
pszMonth[buffersize-1] = '\0'; // Ensure a valid terminating zero! Many people forget this!
}
int main()
{
char month[16]; // 16 bytes allocated here on the stack.
calculateMonth(3, month, sizeof(month));
printf("%s", month); // Prints "Mar"
}
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).