Подтвердить что ты не робот

Поскольку я не могу вернуть локальную переменную, какой лучший способ вернуть строку из C или С++?

В качестве продолжения этого вопроса:

Из того, что я видел, это должно работать как ожидалось:

void greet(){
  char c[] = "Hello";
  greetWith(c);
  return;
}

но это приведет к поведению undefined:

char *greet(){ 
  char c[] = "Hello";
  return c;
}

Если я прав, какой лучший способ исправить вторую функцию приветствия? Во встроенной среде? На рабочем столе?

4b9b3361

Ответ 1

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

 printf("%s\n",greet());

вы получите странные результаты, потому что вызов printf повторно использовал бы часть вашего массива.

Решение состоит в том, чтобы выделить память в другом месте. Для примера:

char c[] = "Hello";

char * greet() {
    return c;
}

Будет работать. Другим вариантом было бы выделить его статически в области:

char * greet() {
    static char c[] = "Hello";
    return c;
}

поскольку статическая память выделяется отдельно от стека в пространстве данных.

Ваш третий выбор - выделить его в кучу через malloc:

char * greet() {
   char * c = (char *) malloc(strlen("Hello")+1);  /* +1 for the null */
   strcpy(c, "Hello");
   return c;
}

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

Update

Одна из тех вещей, которые кажутся более запутанными, чем я ожидаю, - это именно "утечка памяти". Утечка - это когда вы распределяете память динамически, но теряете адрес, чтобы он не мог быть освобожден. Ни один из этих примеров не обязательно имеет утечку, но только третий, даже потенциально, имеет утечку, потому что он единственный, который динамически распределяет память. Итак, принимая третью реализацию, вы можете написать этот код:

{
    /* stuff happens */
    printf("%s\n", greet());
}

У этого есть утечка; возвращается указатель на память malloc'а, printf использует его, а затем он теряется; вы больше не можете его освобождать. С другой стороны,

{
    char * cp ;
    /* stuff happens */
    cp = greet();
    printf("%s\n", cp);
    free(cp);
}

не течет, потому что указатель сохраняется в автоматической переменной cp достаточно долго, чтобы называть free() на нем. Теперь, несмотря на то, что cp исчезает, как только выполнение проходит, он заканчивает скобки, так как свободен вызван, память исправлена ​​и не просачивается.

Ответ 2

Если вы используете С++, вам может потребоваться использовать std::string для возврата строк из вашей второй функции:

std::string greet() {
    char c[] = "Hello";
    return std::string(c); // note the use of the constructor call is redundant here
}

Или в однопоточной среде вы можете:

char *greet() {
    static char c[] = "Hello";
    return c;
}

Здесь static выделяет пространство в глобальной области памяти, которое никогда не исчезает. Этот метод static чреват опасностью.

Ответ 3

Зависит, если встроенная среда имеет кучу или нет, если это так, вы должны malloc следующим образом:

char* greet()
{
  char* ret = malloc(6 * sizeof(char)); // technically " * sizeof(char)" isn't needed since it is 1 by definition
  strcpy(ret,"hello");
  return ret;
}

Обратите внимание, что вы должны позже вызвать free() для очистки. Если у вас нет доступа к динамическому распределению, вам нужно будет сделать его глобальным или переменным в стеке, а далее - стек вызовов.

Ответ 4

Если вам нужно сделать это на С++, использование std::string, предложенное Грегом Хьюджиллом, безусловно, является самой простой и поддерживаемой стратегией.

Если вы используете C, вы можете подумать о возврате указателя на пространство, которое было динамически распределено с помощью malloc(), как было предложено Джесси Пеппер; но другой способ избежать динамического распределения состоит в том, чтобы greet() принять параметр char * и записать там его вывод:

void greet(char *buf, int size) {
    char c[] = "Hello";

    if (strlen(c) + 1 > size) {
        printf("Buffer size too small!");
        exit(1);
    }

    strcpy(buf, c);
}

Параметр size существует для безопасности, чтобы предотвратить переполнение буфера. Если бы вы точно знали, как долго будет строка, это не понадобится.

Ответ 5

Вы можете использовать любой из них:

char const* getIt() {
    return "hello";
}

char * getIt() {
    static char thing[] = "hello";
    return thing;
}

char * getIt() {
    char str[] = "hello";
    char * thing = new char[sizeof str];
    std::strcpy(thing, str);
    return thing;
}

shared_array<char> getIt() {
    char str[] = "hello";
    shared_array<char> thing(new char[sizeof str]);
    std::strcpy(thing.get(), str);
    return thing;
}

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

Ответ 6

Возвращая память malloc'd из функции, предложенной несколькими другими ответами, просто запрашивает утечку памяти. Вызывающий должен был бы знать, что вы malloc'd, а затем позвоните бесплатно. Если они вызвали удаление на нем, результаты undefined (хотя, вероятно, все в порядке).

Было бы лучше заставить вызывающего предоставить вам память, а затем скопировать в нее свою строку. Таким образом, вызывающий абонент уведомляется о необходимости его очистки.

Ответ 7

Из того, что я прочитал, самый безопасный вариант - сделать вызывающего абонента ответственным за выделение памяти для хранения строки. Вы также должны проверить, достаточно ли достаточно буфера для хранения вашей строки:

char *greet(char *buf, int size) {
     char *str = "Hello!"
     if (strlen(str) + 1 > size) { // Remember the terminal null!
          return NULL;
     } 
     strcpy(buf, str);
     return buf;
}

void do_greet() {
    char buf[SIZE];
    if (greet(buf, SIZE) == NULL) {
        printf("Stupid C");
     } 
     else {} // Greeted!
}

Тон работы для простой задачи... но там C для вас:-) К сожалению! Думаю, я был избит random_hacker...

Ответ 8

Выделить массив символов в куче?

Можно ли использовать malloc или нет, зависит только от того, что вы подразумеваете под "встроенной средой".