2013-09-27 3 views
0

У меня есть функция, которая возвращает константный символ *Const символ * Return Type

const char* SayHi() 
{ 
string data="Mustafa Hi"; 
const char* cptr=data.c_str(); 
return cptr; 
} 
int main() 
{ 
cout<<SayHi()<<endl; 
return 0; 
} 

Но выход не: Мустафа Привет. Он равен нулю.

Если я определить переменные данные как глобальный, как

string data; 
    const char* SayHi() 
    { 
    data="Mustafa Hi"; 
    const char* cptr=data.c_str(); 
    return cptr; 
    } 
    int main() 
    { 

    cout<<SayHi()<<endl; 
    return 0; 
    } 

Выход Мустафа Привет.

Но для const int * это работает;

const int* aMethod() 
    { 
    int* aVar=new int(111); 
    const int* acstPtr=aVar; 
    return acstPtr; 
    } 
    int main() 
    { 
     cout<<*aMethod()<<endl; 
     return 0; 
    } 

Выход: 111
или

const int* aMethod() 
    { 
    int aVar =111; //in stack 
    const int* acstPtr=&aVar; 
    return acstPtr; 
    } 

так почему бы не пустой?

+1

В дополнение к ответам, вероятно, вы должны просто вернуть объект 'string', а не' const char * '(если у вас нет * очень * веских причин). – syam

+0

Правда, я знаю, что не буду использовать его. Я просто пытаюсь понять. –

+0

Что касается вашего редактирования ('int *' case), вы не делаете то же самое: вы создаете новый 'int' в куче, который выживает в области функции (в отличие от вашего случая' string', где строка находится в стеке и уничтожается при выходе из функции). Однако во втором случае у вас есть утечка памяти, потому что вы никогда не удаляете 'int'. – syam

ответ

6

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

+0

Также известен как * неопределенное поведение *. – rwols

+0

Ему повезло, что он не форматировал свой жесткий диск, действительно. –

+0

так что const int * и const char * отличаются? –

1

Потому что на объектах C++ есть время жизни. В первом примере строка перестает существовать после выхода из этой функции. Поэтому это ошибка, чтобы вернуть указатель на данные этой строки.

1

Вам нужно понять, что «строковые данные» внутри SayHi являются локальной переменной. Когда функция выходит из локальных переменных, они уничтожаются.

Чтобы сделать то, что вы пытаетесь сделать, вам нужно создать хранилище для строки, чтобы он не был локальным для SayHi, либо в главном, либо передал его или в кучу. Возможно, вы хотите, чтобы SayHi вернул ссылку.

+0

Нет, ссылка имела бы такую ​​же проблему, поскольку объект все равно будет уничтожен. Вы имели в виду вернуть строку? – syam

+0

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

+0

Если вы создаете что-то в куче, сделайте **, а не ** верните ссылку, потому что это сбивает с толку. Необработанные указатели - это минимальный минимум, но тогда вы можете легко протечь память, поэтому вместо этого вы должны использовать интеллектуальные указатели ('unique_ptr/shared_ptr'). Но в любом случае попытка избежать копирования бесполезна: компилятор будет оптимизировать копию с использованием (N) RVO и не сможет переместить строку вместо копирования (последний бит - C++ 11, но (N) RVO уже используется компиляторами C++ 03). Просто возвращайтесь по значению, компилятор достаточно умен, чтобы понять это. – syam

1

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

const char* SayHi() 
{ 
    static string data="Mustafa Hi"; 
    const char* cptr=data.c_str(); 
    return cptr; 
} 
1
But output is not : Mustafa Hi. It is null. 

Это может быть что угодно. Проблема заключается в возврате переменной-члена (const char *) локального объекта (строки), который будет очищен после того, как мы выйдем из закрывающей фигурной скобки. В таких случаях вывод не определен. На нескольких компиляторах вы можете получить желаемый результат, если освобожденная память не была перераспределена.

Он работает для глобальной переменной, поскольку они охватывают в течение всего срока службы программы и не очищается после контроля выходит SayHi()

const int* this works; 

Это динамическое выделение памяти против выделения стека ранее. В отличие от распределения стека (создания локального объекта) с распределением кучи (с использованием нового) у вас есть память и значение, пока вы не удалите ядро ​​явно.

Чтобы повторить первый сценарий с междунар, ваш код должен быть что-то вроде,

const int* aMethod() 
    { 
    int aVar=111; // changed from pointer to local variable. Heap to stack 
    const int* acstPtr= &aVar; //Returning the address for local variable. Which will be freed at the enclosing curly brace. 
    return acstPtr; 
    } 

Но опять же, выход не определен, когда вы имеете дело с указателями, чья память уже освобождена (оборванных указатель)

+0

Выход последнего кода 111, не является неопределенным! соиЬ << * aMethod() << епсИ; поэтому, если строка также находится в стеке в стеке. поэтому почему вывод не является нулевым или не определено? –

+0

@MustafaEkici Кажется, вы не понимаете значения «Неопределенного поведения». Вы должны [прочитать это, чтобы лучше понять UB] (http://stackoverflow.com/a/4105123/2070725). – syam

1

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

string data="Mustafa Hi"; 
const char* cptr=data.c_str(); 
return cptr; 

осложняется тем фактом, чтоstst :: string - это объект с деструктором. До C++ 11, возможно, он сохранил строку без завершающего нуля, если вы не вызвали c_str(). Внутри он содержит указатель и значение размера, а также значение емкости. Он выделяет память для хранения строки. Подумайте (пре-C++ 11) станд :: строку, как это:

class String { 
    char* m_ptr; 
    size_t m_length; 
    size_t m_alloc; 
public: 
    String() : m_ptr(nullptr), m_length(0), m_alloc(0) {} 
    String(const char* src) { 
     size_t length = strlen(src); 
     m_ptr = new char[length]; 
     m_alloc = m_length; 
     memcpy(m_ptr, src, length); // not including the \0 
    } 
    const char* c_str() { 
     if (m_length == 0) 
      return nullptr; 
     if (m_alloc > m_length) // we're null terminated 
      return m_ptr; 
     char* newPtr = new char[length + 1]; 
     memcpy(m_ptr, newPtr, length); 
     delete [] m_ptr; 
     m_ptr = newPtr; 
     m_ptr[length] = '\0'; 
     ++m_alloc; 
     return m_ptr; 
    } 
    ~String() { 
#ifdef _DEBUG 
     if (m_ptr) m_ptr[0] = 0; 
#endif 
     delete [] m_ptr; 
    } 
}; 

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

Взгляните на следующий пример (демо: http://ideone.com/wAcY3B)

#include <iostream> 

int* getInt1(int input) 
{ 
    int i = input; 
    std::cout << "created 'i' at " << (void*)&i << std::endl; 
    int* ptr1 = &i; 
    return ptr1; 
} 

int* getInt2(int input) 
{ 
    int* ptr3 = new int(input); 
    return ptr3; 
} 

int main() 
{ 
    int i = 0; 
    std::cout << "i is on the stack, it's address is " << (void*)&i << std::endl; 
    int* ip = new int(1); 
    std::cout << "ip is on the heap, it's address is " << (void*)ip << std::endl; 

    int* p1 = NULL; 
    int* p2 = NULL; 
    int* p3 = NULL; 
    // force the pointers to be assigned locations on the stack by printing them. 
    std::cout << "created p1(" << &p1 << "), p2(" << &p2 << ") and p3(" << &p3 << ")" << std::endl; 

    p1 = getInt1(10101); 
    std::cout << "p1(" << &p1 << ") = " << (void*)p1 << " -> " << *p1 << std::endl; 

    p2 = getInt1(20202); 
    std::cout << "p2(" << &p2 << ") = " << (void*)p2 << " -> " << *p2 << std::endl; 

    // but more importantly 
    std::cout << "p1(" << &p1 << ") = " << (void*)p1 << " -> " << *p1 << std::endl; 

    p3 = getInt2(30303); 
    std::cout << "p3(" << &p3 << ") = " << (void*)p3 << " -> " << *p3 << std::endl; 
    std::cout << "p2(" << &p2 << ") = " << (void*)p2 << " -> " << *p2 << std::endl; 
    std::cout << "p1(" << &p1 << ") = " << (void*)p1 << " -> " << *p1 << std::endl; 
} 

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

i is on the stack, it's address is 0xbfb49a90 
ip is on the heap, it's address is 0x9b83008 
created p1(0xbfb49a94), p2(0xbfb49a98) and p3(0xbfb49a9c) 
created 'i' at 0xbfb49a6c 
p1(0xbfb49a94) = 0xbfb49a6c -> 10101 
created 'i' at 0xbfb49a6c 
p2(0xbfb49a98) = 0xbfb49a6c -> 20202 
p1(0xbfb49a94) = 0xbfb49a6c -> -1078682988 
p3(0xbfb49a9c) = 0x9b83018 -> 30303 
p2(0xbfb49a98) = 0xbfb49a6c -> -1078682988 
p1(0xbfb49a94) = 0xbfb49a6c -> -1078682988 

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