Sua assinatura de função precisa ser:
const char * myFunction()
{
return "My String";
}
Fundo:
É tão fundamental para C e C ++, mas pouca discussão deveria ser necessária.
Em C (e C ++ para esse assunto), uma string é apenas um array de bytes terminado com um byte zero - portanto, o termo "string-zero" é usado para representar este tipo particular de string. Existem outros tipos de strings, mas em C (e C ++), esse sabor é inerentemente entendido pela própria linguagem. Outras linguagens (Java, Pascal, etc.) usam diferentes metodologias para entender "minha string".
Se você já usou a API do Windows (que está em C ++), verá parâmetros de função com bastante regularidade, como: "LPCSTR lpszName". A parte 'sz' representa esta noção de 'string-zero': uma matriz de bytes com um terminador nulo (/ zero).
Esclarecimento:
Por causa desta 'introdução', uso as palavras 'bytes' e 'caracteres' alternadamente, porque é mais fácil de aprender desta forma. Esteja ciente de que existem outros métodos (caracteres largos e sistemas de caracteres multibyte ( mbcs )) que são usados para lidar com caracteres internacionais. UTF-8 é um exemplo de mbcs. Por causa da introdução, eu silenciosamente 'pulo' tudo isso.
Memória:
Isso significa que uma string como "minha string" na verdade usa 9 + 1 (= 10!) Bytes. É importante saber quando você finalmente conseguirá alocar strings dinamicamente.
Então, sem esse 'zero terminador', você não tem uma string. Você tem uma matriz de caracteres (também chamada de buffer) circulando na memória.
Longevidade dos dados:
O uso da função desta forma:
const char * myFunction()
{
return "My String";
}
int main()
{
const char* szSomeString = myFunction(); // Fraught with problems
printf("%s", szSomeString);
}
... geralmente levará você a exceções aleatórias não tratadas / falhas de segmento e semelhantes, especialmente 'no futuro'.
Resumindo, embora minha resposta esteja correta - 9 em cada 10 vezes, você terminará com um programa que trava se o usar dessa forma, especialmente se você achar que é uma 'boa prática' fazê-lo dessa forma. Resumindo: geralmente não é.
Por exemplo, imagine em algum momento no futuro, a string agora precisa ser manipulada de alguma forma. Geralmente, um codificador irá 'seguir o caminho mais fácil' e (tentar) escrever código como este:
const char * myFunction(const char* name)
{
char szBuffer[255];
snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
return szBuffer;
}
Ou seja, seu programa travará porque o compilador (pode / não) liberou a memória usada szBuffer
no momento printf()
em que o in main()
é chamado. (Seu compilador também deve avisá-lo sobre esses problemas com antecedência.)
Existem duas maneiras de retornar strings que não vomitam tão prontamente.
- retornando buffers (estáticos ou alocados dinamicamente) que vivem por um tempo. Em C ++, use 'classes auxiliares' (por exemplo,
std::string
) para lidar com a longevidade dos dados (o que requer a alteração do valor de retorno da função), ou
- passe um buffer para a função que é preenchida com informações.
Observe que é impossível usar strings sem usar ponteiros em C. Como mostrei, eles são sinônimos. Mesmo em C ++ com classes de modelo, sempre há buffers (ou seja, ponteiros) sendo usados em segundo plano.
Então, para melhor responder a (questão agora modificada). (Com certeza há uma variedade de "outras respostas" que podem ser fornecidas.)
Respostas mais seguras:
Exemplo 1, usando strings alocadas estaticamente:
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"
}
O que o 'estático' faz aqui (muitos programadores não gostam desse tipo de 'alocação') é que as strings são colocadas no segmento de dados do programa. Ou seja, está alocado permanentemente.
Se você mudar para C ++, usará estratégias semelhantes:
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;
}
}
... mas provavelmente é mais fácil usar classes auxiliares, como, por exemplo std::string
, se você estiver escrevendo o código para seu próprio uso (e não parte de uma biblioteca para ser compartilhada com outras pessoas).
Exemplo 2, usando buffers definidos pelo chamador:
Esta é a maneira mais 'infalível' de passar as cordas. Os dados retornados não estão sujeitos à manipulação pela parte chamadora. Ou seja, o exemplo 1 pode facilmente ser abusado por uma parte chamadora e expô-lo a falhas de aplicativo. Dessa forma, é muito mais seguro (embora use mais linhas de código):
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"
}
Existem muitas razões pelas quais o segundo método é melhor, especialmente se você estiver escrevendo uma biblioteca para ser usada por outros (você não precisa se prender a um esquema específico de alocação / desalocação, terceiros não podem quebrar seu código, e você não precisa se vincular a uma biblioteca de gerenciamento de memória específica), mas como todo código, você decide o que mais gosta. Por esse motivo, a maioria das pessoas opta pelo exemplo 1 até que tenham sido queimados tantas vezes que se recusam a escrever mais dessa forma;)
Aviso Legal:
Eu me aposentei há vários anos e meu C está um pouco enferrujado agora. Este código de demonstração deve ser compilado corretamente com C (embora seja OK para qualquer compilador C ++).