1

我有一个返回 const char* 的函数

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

但输出不是: Mustafa Hi。它是空的。

如果我将数据变量定义为全局变量

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

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

输出是 Mustafa Hi。

但是对于 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;
    }

那么为什么不为空呢?

4

6 回答 6

7

您的第一个实现的问题是您返回一个指向临时内存的指针。因为“字符串数据”变量仅在该函数的范围内定义,所以当函数消失时,您刚刚返回的指针现在指向已释放的内存。在您的情况下,它必须被全零覆盖。这就是您看到 NULL 的原因。

于 2013-09-27T17:44:17.173 回答
1
But output is not : Mustafa Hi. It is null.

它可以是任何东西。问题在于返回本地对象(字符串)的成员变量(const char *),一旦我们从封闭的花括号中出来,它将被清除。在这种情况下,输出是未定义的。在少数编译器上,如果释放的内存没有重新分配,您甚至可以获得所需的输出。

它适用于全局变量,因为它们跨越程序的整个生命周期,并且在控件退出 SayHi() 后不会被清理

 const int* this works;

这是动态内存分配,而不是之前的堆栈分配。与堆栈分配(创建本地对象)不同,使用堆分配(使用new)您拥有内存和值,直到您明确删除内存。

要使用 int 复制第一个场景,您的代码应该类似于,

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;
    }

但同样,当您处理内存已被释放的指针(悬空指针)时,输出是未定义的

于 2013-09-27T19:04:31.287 回答
1

局部变量是在“堆栈”上创建的,它们具有它们存在的范围的生命周期。特别是,在您的原始情况下发生了什么:

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

由于std::string 是一个带有析构函数的对象这一事实使情况变得复杂。在 C++11 之前,除非您调用 c_str(),否则它可能已经存储了没有终止零的字符串。在内部,它包含一个指针和一个大小值,也许还有一个容量值。它分配内存来存储字符串。像这样考虑(C++ 11 之前的)std::string:

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()”的调用之间没有改变,所以它的局部变量实例位于相同的位置,但是如果你在分配之间调用其他一些随机函数,它们将使用相同的堆栈位置和你指出的- to-values 将丢失。

于 2013-09-27T20:22:02.790 回答
1

试试下面的代码。如果要返回指向局部变量的指针,则需要使用 static 关键字。所有局部变量在函数存在后将是自由的。这就是无法正确返回字符串的原因。

const char* SayHi()
{
    static string data="Mustafa Hi";
    const char* cptr=data.c_str();
    return cptr;
}
于 2013-09-27T17:56:46.860 回答
1

Because in C++ objects have a lifetime. In your first example the string no longer exists once you exit the function. Therefore it's an error to return a pointer to that string's data.

于 2013-09-27T17:47:02.263 回答
1

You need to understand that "string data" inside of SayHi is a local variable. When the function exits local variables are destroyed.

To do what you're trying to do you either have to create the storage for the string so that it is not local to SayHi, either in main and pass it in or on the heap. Perhaps you'd like SayHi to return a reference.

于 2013-09-27T17:47:14.780 回答