const char *返回类型

时间:2013-09-27 17:41:03

标签: c++

我有一个返回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;
    }

为什么不归零?

6 个答案:

答案 0 :(得分:6)

第一个实现的问题是您返回一个指向临时内存的指针。因为'string data'变量仅在该函数的范围内定义,所以当函数消失时,您刚刚返回的指针现在指向free'd内存。在您的情况下,它必须被全部零覆盖。这就是你看到NULL的原因。

答案 1 :(得分:1)

因为在C ++对象中有生命周期。在第一个示例中,退出函数后字符串不再存在。因此,返回指向该字符串数据的指针是错误的。

答案 2 :(得分:1)

您需要了解SayHi中的“字符串数据”是一个局部变量。当函数退出时,局部变量被破坏。

要执行您要执行的操作,您必须为字符串创建存储空间,以便它不是SayHi的本地存储,无论是在main中还是在堆中传递它。也许你希望SayHi返回一个参考文献。

答案 3 :(得分:1)

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

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

答案 4 :(得分: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;
    }

但是,当你处理已经释放了内存的指针时,输出是未定义的(悬空指针

答案 5 :(得分:1)

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

string data="Mustafa Hi";
const char* cptr=data.c_str();
return cptr;
由于std :: string是一个带有析构函数的对象,所以

变得复杂。在C ++ 11之前,除非你调用了c_str(),否则它可能已经存储了没有终止零的字符串。在内部,它包含指针和大小值,也可能包含容量值。它分配内存来存储字符串。想想(pre-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;
    }
};

您的函数正在获取此对象的实例的地址,然后返回该地址。接下来发生的事情是实例超出范围并调用它的析构函数 - 它位于堆栈上并且紧跟在您的代码之后,因此它所在的堆栈位置现在可供下一个被调用的代码使用。 / p>

查看以下示例(现场演示: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()”之间没有变化,所以它的局部变量实例位于同一位置,但是如果你在两次分配之间调用其他一些随机函数,它们将使用相同的堆栈位置和你的指向价值将会丢失。