Ваш підпис функції повинен бути:
const char * myFunction()
{
return "My String";
}
Фон:
Це настільки фундаментально для C&C ++, але дискусій має бути трохи більше.
У C (& C ++ для цього питання) рядок - це лише масив байтів, що закінчується нульовим байтом - отже, термін "string-zero" використовується для представлення саме цього аромату рядка. Існують і інші види струн, але в C (& C ++) цей аромат притаманний самій мові. Інші мови (Java, Pascal тощо) використовують різні методології, щоб зрозуміти "мій рядок".
Якщо ви коли-небудь використовуєте API Windows (який знаходиться на C ++), ви побачите досить регулярно функціонуючі параметри, такі як: "LPCSTR lpszName". Частина 'sz' представляє це поняття 'string-zero': масив байтів з нульовим (/ zero) термінатором.
Пояснення:
Заради цього «вступу» я вживаю слова «байти» та «символи» взаємозамінно, тому що простіше навчитися цьому. Пам’ятайте, що існують і інші методи (широкоформатні та багатобайтові системи символів ( 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()називаються. (Ваш компілятор також повинен попереджати вас про подібні проблеми.)
Є два способи повернути рядки, які не будуть так легко перетворюватися.
- повертаються буфери (статичні або динамічно виділені), які живуть деякий час. У C ++ використовуйте 'допоміжні класи' (наприклад,
std::string) для управління довговічністю даних (що вимагає зміни повернення значення функції) або
- передайте буфер до функції, яка заповнюється інформацією.
Зауважте, що неможливо використовувати рядки без використання покажчиків на C. Як я показав, вони є синонімами. Навіть у 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"
}
Що "static" робить тут (багатьом програмістам не подобається такий тип "виділення"), що рядки потрапляють у сегмент даних програми. Тобто вона постійно виділяється.
Якщо ви перейдете на C ++, ви використовуєте подібні стратегії:
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 (хоча це добре для будь-якого компілятора C ++).