Возвращение строки C из функции

Я пытаюсь вернуть строку C из функции, но не работает. Вот мой код.

char myFunction()
{
    return "My String";
}

В основном я называю это следующим образом:

int main()
{
  printf("%s",myFunction());
}

Я также пробовал другие способы для myFunction, но они не работают. Например:

char myFunction()
{
  char array[] = "my string";
  return array;
}

Примечание. Я не могу использовать указатели!

Небольшой справочник по этой проблеме: Есть функция, которая определяет, в каком месяце это, например; если его 1, то он возвращает январь и т.д. и т.д.

Итак, когда он собирается печатать, он делает это. printf("Month: %s",calculateMonth(month));. Теперь проблема в том, как вернуть эту строку из функции calculateMonth.

Ответ 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 (это нормально для любого компилятора С++, хотя).

Ответ 2

Строка C определяется как указатель на массив символов.

Если у вас нет указателей, по определению у вас не может быть строк.

Ответ 3

Обратите внимание на эту новую функцию:

const char* myFunction()
{
        static char array[] = "my string";
        return array;
}

Я определил "массив" как статический, иначе, когда конец функции, переменная (и возвращаемый указатель) выходит из области видимости. Поскольку эта память выделена в стеке, она будет повреждена. Недостатком этой реализации является то, что код не является повторным, а не потокобезопасным.

Другой альтернативой может быть использование malloc для выделения строки в куче, а затем бесплатно в правильном месте вашего кода. Этот код будет повторно задействован и безопасен по потоку.

EDIT:

Как отмечено в комментарии, это очень плохая практика, так как злоумышленник может затем ввести код в ваше приложение (ему нужно открыть код с помощью gdb, затем сделать точку останова и изменить значение возвращаемой переменной на переполнение и забава только начинается).

Если более рекомендуется, чтобы вызывающая сторона обрабатывала выделение памяти. См. Этот новый пример:

char* myFunction( char* output_str, size_t max_len )
{
   const char *str = "my string";
   size_t l = strlen(str);
   if (l+1 > max_len) {
      return NULL;
   }
   strcpy(str, str, l);
   return input;
}

Обратите внимание, что единственным содержимым, которое может быть изменено, является тот, который пользователь. Другой побочный эффект - этот код теперь является потокобезопасным, по крайней мере, с точки зрения библиотеки. Программист, вызывающий этот метод, должен убедиться, что секция памяти используется в потоковом режиме.

Ответ 4

Ваша проблема связана с возвращаемым типом функции - она ​​должна быть:

char *myFunction()

... и тогда ваша оригинальная формулировка будет работать.

Обратите внимание, что вы не можете использовать строки C без указателей, где-то вдоль линии.

Также: включите предупреждения компилятора, он должен был предупредить вас об этой обратной линии, преобразующей char * в char без явного приведения.

Ответ 5

Основываясь на вашей недавно добавленной предыстории с вопросом, почему бы просто не вернуть целое число от 1 до 12 в течение месяца, и пусть функция main() использует оператор switch или if-else, чтобы решить, что печатать? Это, конечно, не лучший способ пойти - char * будет - но в контексте такого класса я думаю, что он, вероятно, самый изящный.

Ответ 6

Вы можете создать массив в вызывающем, который является основной функцией, и передать массив вызывающей стороне, которая является вашей myFunction(). Таким образом, myFunction может заполнить строку в массиве. Однако вам нужно объявить myFunction() как

char* myFunction(char * buf, int buf_len){
  strncpy(buf, "my string", buf_len);
  return buf;
}

и в основной функции myFunction следует вызывать таким образом

char array[51];
memset(array,0,51);/*all bytes are set to '\0'*/
printf("%s", myFunction(array,50));/*buf_len arguement is 50 not 51. This is to make sure the string in buf is always null-terminated(array[50] is always '\0')*/

Однако указатель все еще используется.

Ответ 7

Тип возвращаемой функции - это один char. Вы должны вернуть указатель на первый элемент массива символов. Если вы не можете использовать указатели, то вы ввернуты.: (

Ответ 8

Или как об этом:

void print_month(int month)
{
    switch (month)
    {
        case 0:
            printf("january");
            break;
        case 1:
            printf("february");
            break;
        ...etc...
    }
}

И назовите это месяцем, который вы вычисляете где-то еще.

Привет,

Sebastiaan

Ответ 9

A char - это только один однобайтовый символ. Он не может хранить строку символов и не является указателем (которого вы, по-видимому, не можете). Поэтому вы не можете решить свою проблему без использования указателей (для которых char[] используется синтаксический сахар).

Ответ 10

Если вы действительно не можете использовать указатели, сделайте следующее:

char get_string_char(int index)
{
    static char array[] = "my string";
    return array[index];
}

int main()
{
    for (int i = 0; i < 9; ++i)
        printf("%c", get_string_char(i));
    printf("\n");
    return 0;
}

Магическое число 9 ужасно, это не пример хорошего программирования. Но вы понимаете. Обратите внимание, что указатели и массивы - это одно и то же (kinda), так что это немного изменяет.

Надеюсь, это поможет!

Ответ 11

В коде вы пытаетесь вернуть String (In C, который является ничем иным, как нулевым завершенным массивом символов), но возвращаемый тип вашей функции - char, что вызывает все проблемы для вас. Вместо этого вы должны написать так:

const char* myFunction()
{

    return "My String";

}

И всегда полезно квалифицировать свой тип с помощью const при назначении литералов в C указателям, поскольку литералы в C не изменяются.

Ответ 12

Ваш прототип функции утверждает, что ваша функция вернет char. Таким образом, вы не можете вернуть строку в свою функцию.

Ответ 13

char* myFunction()
{
    return "My String";
}

В строковых литералах C это массивы со статической константой класса памяти, поэтому возвращение указателя на этот массив безопасно. Более подробно здесь: "время жизни" строкового литерала в C