Ответ 1
ваша подпись функции должна быть:
const char * myFunction()
{
return "My String";
}
Edit:
Фон:
Прошли годы с этой должности, и они никогда не думали, что это будет проголосовано, потому что это настолько фундаментально для C и С++. Тем не менее, нужно провести немного более подробное обсуждение.
В C (например, С++) строка представляет собой всего лишь массив байтов, заканчивающихся нулевым байтом, поэтому термин "string-zero" используется для представления этого конкретного вкуса строки. Существуют и другие типы строк, но в C (и С++) этот вкус по сути понимается самим языком. Другие языки (Java, Pascal и т.д.) Используют разные методологии для понимания "моей строки".
Если вы когда-либо используете Windows API (который находится на С++), вы увидите довольно регулярные функциональные параметры, такие как: "LPCSTR lpszName". Часть 'sz' представляет это понятие "string-zero": массив байтов с нулевым (/zero) терминатором.
Разъяснение:
Для этого "intro" я использую слово "байты" и "символы" взаимозаменяемо, потому что это легче изучить таким образом. Имейте в виду, что существуют другие методы (широкие символы и многобайтовые системы символов - mbcs), которые используются для управления международными символами. UTF-8 является примером mbcs. Для интро я спокойно "пропустил" все это.
Память:
Это означает, что строка типа "моя строка" фактически использует 9 + 1 (= 10!) байтов. Это важно знать, когда вы, наконец, обходитесь динамически распределяя строки. Таким образом, без этого "завершающего нуля" у вас нет строки. У вас есть массив символов (также называемый буфером), висящий в памяти.
Долговечность данных:
Использование функции таким образом:
const char * myFunction()
{
return "My String";
}
int main()
{
const char* szSomeString = myFunction(); // fraught with problems
printf("%s", szSomeString);
}
... обычно высаживают вас случайными необработанными исключениями/сегментами и т.п., особенно "вниз по дороге".
Короче говоря, хотя мой ответ правильный - в 9 раз из 10 вы закончите с программой, которая выйдет из строя, если вы используете ее таким образом, особенно если вы считаете, что это "хорошая практика", чтобы сделать это именно так. Короче говоря: обычно это не так.
Например, представьте себе некоторое время в будущем, теперь нам нужно каким-то образом изменить строку. Как правило, кодер "возьмет легкий путь" и (попытается) написать такой код:
const char * myFunction(const char* name)
{
char szBuffer[255];
snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
return szBuffer;
}
Таким образом, ваша программа выйдет из строя, потому что компилятор (может/не может) освободил память, используемую szBuffer
, к моменту вызова printf()
в main()
. (Ваш компилятор также должен предупредить вас о таких проблемах заранее).
Есть два способа вернуть строки, которые не будут barf так легко.
- возвращающие буферы (статические или динамически распределенные), которые живут некоторое время. В С++ используйте "вспомогательные классы" (например,
std::string
) для обработки долговечности данных (что требует изменения возвращаемого значения функции) или - передать буфер функции, которая заполняется информацией.
Обратите внимание, что невозможно использовать строки без использования указателей в C. Как я показал, они являются синонимами. Даже в С++ с классами шаблонов всегда есть буферы (т.е. указатели), которые используются в фоновом режиме.
Итак, чтобы лучше ответить (теперь измененный вопрос). (обязательно будут различные "другие ответы", которые могут быть предоставлены).
Безопасные ответы:
например 1. используя статически выделенные строки:
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"
}
Что здесь делает "статический" (многим программистам не нравится этот тип "распределения" ), так это то, что строки попадают в сегмент данных программы. То есть, он постоянно выделяется.
Если вы перейдете на С++, вы будете использовать аналогичные стратегии:
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;
}
}
... но, возможно, проще использовать вспомогательные классы, такие как std::string
, если вы пишете код для собственного использования (а не часть библиотеки для совместного использования с другими).
например, 2. с использованием буферов, определенных определенным пользователем:
Это тем более "проверенный дураком" способ передачи строк вокруг. Возвращенные данные не подлежат манипуляциям со стороны вызывающей стороны. То есть, например, 1 можно легко злоупотреблять вызывающей стороной и подвергать вас ошибкам приложения. Таким образом, это намного безопаснее (хотя и использует больше строк кода):
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"
}
Есть много причин, почему второй метод лучше, особенно если вы пишете библиотеку, которую будете использовать другие (вам не нужно блокировать конкретную схему распределения/освобождения, третьи стороны не могут сломать ваш код, вам не нужно ссылаться на определенную библиотеку управления памятью), но, как и весь код, это зависит от того, что вам больше нравится. По этой причине большинство людей выбирают, например, 1, пока их не сожгли столько раз, что они больше не хотят писать это;)
отказ от ответственности:
Я ушел на пенсию несколько лет назад, и теперь мой C немного ржавый. Этот демонстрационный код должен полностью скомпилироваться с C (это нормально для любого компилятора С++, хотя).