В чем разница между возвращением char* и char[] из функции? [дубликат]


этот вопрос уже есть ответ здесь:

  • Как получить доступ к локальной переменной из другой функции с помощью указателей? 9 ответов
  • Строковые литералы: куда они идут? 8 ответов
  • Какая разница между char s[] и char *s? 12 ответов

почему первая функция возвращает строку "Hello, World", а вторая функция ничего не возвращает. Я думал, что возвращаемое значение функции будет неопределенным, так как они возвращаются данные из области.

#include <stdio.h>
// This successfully returns "Hello, World"
char* function1()
{
    char* string = "Hello, World!";
    return string;
}
// This returns nothing
char* function2()
{
    char string[] = "Hello, World!";
    return string; 
}

int main()
{
    char* foo1 = function1();
    printf("%sn", foo1); // Prints "Hello, World"
    printf("------------n");
    char* foo2 = function2(); // Prints nothing
    printf("%sn", foo2);
    return 0;
}
6 65

6 ответов:

вторая функция ничего не возвращает

The string массив во второй функции:

char string[] = "Hello, World!";

и автоматическая длительность хранения. Он не существует после того, как поток управления возвращается из функции.

, тогда как string в первой функции:

char* string = "Hello, World!";

указывает на литеральную строку, которая имеет статическая продолжительность хранения. Это означает, что строка все еще существует после возвращение из функции. Что вы возвращаете из функции-это указатель на строковый литерал.

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

какая первая функция (function1) не возвращает указатель на первый элемент такого массива.

со второй функцией (function2) все немного по-другому. Здесь переменная string это локальная переменная внутри функции. Как таковой он выйдет из области видимости и перестанет существовать, как только функция вернется. С помощью этой функции вы возвращаете указатель на первый элемент этого массива, но этот указатель сразу становится недействительным, поскольку оно будет указывать на то, что больше не существует. Разыменование (что происходит, когда вы передаете его в printf) приведет к неопределенное поведение.

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

Вы, наверное, уже знаете, как массив работает в C. Это просто адрес памяти, который увеличивается на размер объекта, и вы, вероятно, также знаете, что C не выполняет проверку границ, поэтому, если вы хотите получить доступ к 11-му элементу десятиэлементного массива, никто не остановит вас, и пока вы ничего не пытаетесь написать, никакого вреда не будет. Вы можете не знать, что C расширяет эту идею до того, как она использует функции и переменные. Функция-это просто область памяти в стеке, которая загружается по требованию и для хранения переменные смещения из этого положения. Ваша функция вернула указатель на локальную переменную, в частности, адрес местоположения в стеке, который содержит "H" из "Hello World\n\0", но когда вы вызвали другую функцию (метод печати), эта память была повторно использована методом печати для выполнения необходимых действий. Вы можете увидеть это достаточно легко (не делайте этого в производственном коде!!!)

char* foo2 = function2(); // Prints nothing
ch = foo2[0];  // Do not do this in live code!
printf("%s\n", foo2);  // stack used by foo2 now used by print()
printf("ch is %c\n", ch);  // will have the value 'H'!

Я думал, что возвращаемое значение функции будет неопределенным, так как они возвращаются данные из области.

нет. Но это не так.

функции function1 вы возвращаете указатель на строковый литерал. Возврат указателя на строковый литерал-это нормально, потому что строковые литералы имеют статическая продолжительность хранения. Но это не так с автоматическая локальная переменная.

In функция function2 массив string - это автоматическая локальная переменная и заявление

return string; 

возвращает указатель на автоматическую локальную переменную. Как только функция возвращается, переменная string перестанет существовать. Разыменование возвращенного указателя приведет к неопределенному поведению.

"Hello, World!" - строковый литерал, который имеет статическую длительность хранения, так что проблема в другом месте. Ваша первая функция возвращает стоимостью на string, это хорошо. Однако вторая функция возвращает адрес локальной переменной (string это то же самое, что &string[0]), что приводит к неопределенному поведению. Ваш второй printf заявление не смогло напечатать ничего, или " Здравствуйте, мир!"или что-то совсем другое. На моей машине программа просто получает сегментацию ошибка.

всегда смотрите на сообщения, которые выводит ваш компилятор. Для вашего примера,gcc выдает:

file.c:12:12: warning: function returns address of local variable [-Wreturn-local-addr]
    return string; 
           ^

что в значительной степени само собой разумеется.

Я думал, что возвращаемое значение функции будет неопределенным, так как они возвращаются данные из области.

обе функции возвращают указатель. Что имеет значение, так это объем референт.

на function1, референт-это строковый литерал "Hello, World!", который имеет статическую длительность хранения. string - это локальная переменная, которая указывает на эту строку, и концептуально возвращается копия этого указателя (на практике, компилятор будет избегать ненужного копирования значения).

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

значит ли это, что все char* статичные?

опять же, нужно различать указатель и референт. Указатели указывают на данные; они сами не "содержат" данные.

вы достигли точки, где вы должны изучать то, что массивы и указатели на самом деле в C - к сожалению, это немного беспорядок. Лучшая ссылка, которую я могу предложить навскидку-это этой, в Q&A формате.