La firma della tua funzione deve essere:
const char * myFunction()
{
return "My String";
}
Sfondo:
È così fondamentale per C e C ++, ma dovrebbero essere necessarie altre discussioni.
In C (e C ++ per quella materia), una stringa è solo un array di byte terminato con zero byte - quindi il termine "stringa zero" è usato per rappresentare questo particolare sapore di stringa. Esistono altri tipi di stringhe, ma in C (e C ++) questo aspetto è compreso intrinsecamente dal linguaggio stesso. Altri linguaggi (Java, Pascal, ecc.) Utilizzano metodologie differenti per comprendere "la mia stringa".
Se usi mai l'API di Windows (che è in C ++), vedrai parametri di funzione abbastanza regolari come: "LPCSTR lpszName". La parte "sz" rappresenta questa nozione di "stringa zero": un array di byte con un terminatore nullo (/ zero).
Una precisazione:
Per il bene di questa "introduzione", uso la parola "byte" e "caratteri" in modo intercambiabile, perché è più facile imparare in questo modo. Tenere presente che esistono altri metodi (caratteri larghi e sistemi di caratteri multibyte ( mbcs )) utilizzati per gestire i caratteri internazionali. UTF-8 è un esempio di mbcs. Per motivi di intro, "salto" tranquillamente tutto questo.
Memoria:
Ciò significa che una stringa come "la mia stringa" utilizza effettivamente 9 + 1 (= 10!) Byte. Questo è importante per sapere quando finalmente si arriva ad allocare le stringhe in modo dinamico.
Quindi, senza questo 'zero che termina', non hai una stringa. Hai un array di caratteri (chiamato anche buffer) in giro nella memoria.
Longevità dei dati:
L'uso della funzione in questo modo:
const char * myFunction()
{
return "My String";
}
int main()
{
const char* szSomeString = myFunction(); // Fraught with problems
printf("%s", szSomeString);
}
... generalmente ti atterreranno con eccezioni casuali non gestite / errori di segmento e simili, specialmente 'lungo la strada'.
In breve, anche se la mia risposta è corretta, 9 volte su 10 vi ritroverete con un programma che va in crash se lo usate in quel modo, soprattutto se pensate che sia "buona pratica" farlo in quel modo. In breve: generalmente non lo è.
Ad esempio, immagina un po 'di tempo nel futuro, la stringa ora deve essere manipolata in qualche modo. Generalmente, un programmatore 'prenderà il percorso facile' e (proverà a) scrivere codice come questo:
const char * myFunction(const char* name)
{
char szBuffer[255];
snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
return szBuffer;
}
Cioè, il programma vada in crash perché il compilatore (può / non può) hanno rilasciato la memoria utilizzata da szBufferdal momento in cui l' printf()in main()si chiama. (Il tuo compilatore dovrebbe anche avvisarti in anticipo di tali problemi.)
Ci sono due modi per restituire stringhe che non sbufferanno così facilmente.
- restituendo buffer (statici o allocati dinamicamente) che vivono per un po '. In C ++ usa 'classi di supporto' (ad esempio,
std::string) per gestire la longevità dei dati (che richiede la modifica del valore di ritorno della funzione), o
- passare un buffer alla funzione che viene riempita di informazioni.
Notare che è impossibile usare le stringhe senza usare i puntatori in C. Come ho mostrato, sono anche sinonimi. Anche in C ++ con classi template, ci sono sempre buffer (cioè puntatori) usati in background.
Quindi, per rispondere meglio alla (domanda ora modificata). (Ci sono sicuramente una serie di "altre risposte" che possono essere fornite.)
Risposte più sicure:
Esempio 1, utilizzando stringhe allocate staticamente:
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"
}
Ciò che lo "statico" fa qui (a molti programmatori non piace questo tipo di "allocazione") è che le stringhe vengono inserite nel segmento di dati del programma. Cioè, è assegnato in modo permanente.
Se passi a C ++ utilizzerai strategie simili:
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;
}
}
... ma probabilmente è più facile usare classi helper, come std::stringse stai scrivendo il codice per il tuo uso personale (e non fa parte di una libreria da condividere con altri).
Esempio 2, utilizzo di buffer definiti dal chiamante:
Questo è il modo più "infallibile" di passare le stringhe. I dati restituiti non sono soggetti a manipolazione da parte del chiamante. Cioè, l'esempio 1 può essere facilmente abusato da un chiamante e esporre l'utente a errori dell'applicazione. In questo modo, è molto più sicuro (anche se utilizza più righe di codice):
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"
}
Ci sono molti motivi per cui il secondo metodo è migliore, in particolare se stai scrivendo una libreria che deve essere utilizzata da altri (non è necessario bloccare un particolare schema di allocazione / deallocazione, terze parti non possono rompere il tuo codice, e non è necessario collegarsi a una specifica libreria di gestione della memoria), ma come tutto il codice, dipende da te ciò che ti piace di più. Per questo motivo, la maggior parte delle persone sceglie l'esempio 1 fino a quando non è stata bruciata così tante volte che si rifiuta più di scriverlo in quel modo;)
Disclaimer:
Sono andato in pensione diversi anni fa e il mio C è un po 'arrugginito ora. Questo codice demo dovrebbe essere compilato correttamente con C (va bene per qualsiasi compilatore C ++).