C++ 常量字符*返回类型

C++ 常量字符*返回类型,c++,C++,我有一个返回常量字符的函数* const char* SayHi() { string data="Mustafa Hi"; const char* cptr=data.c_str(); return cptr; } int main() { cout<<SayHi()<<endl; return 0; } 那么为什么不为null呢?第一个实现的问题是返回指向临时内存的指针。因为'string data'变量仅在该函数的作用域内定义,所以当函数离开时,您刚才返回的指针

我有一个返回常量字符的函数*

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

那么为什么不为null呢?

第一个实现的问题是返回指向临时内存的指针。因为'string data'变量仅在该函数的作用域内定义,所以当函数离开时,您刚才返回的指针现在指向空闲的内存。在您的情况下,它必须被全部零覆盖。这就是为什么会看到NULL。

第一个实现的问题是返回指向临时内存的指针。因为'string data'变量仅在该函数的作用域内定义,所以当函数离开时,您刚才返回的指针现在指向空闲的内存。在您的情况下,它必须被全部零覆盖。这就是为什么你看到null。

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

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

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


要执行您试图执行的操作,您必须为字符串创建存储,以便它不是SayHi的本地存储,可以是在main中并将其传入,也可以是在堆中。也许您希望SayHi返回一个引用。

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


要执行您试图执行的操作,您必须为字符串创建存储,以便它不是SayHi的本地存储,可以是在main中并将其传入,也可以是在堆中。也许您希望SayHi返回一个参考。

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

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

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

const char* SayHi()
{
    static string data="Mustafa Hi";
    const char* cptr=data.c_str();
    return cptr;
}
它可能是任何东西。问题在于返回局部对象字符串的成员变量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;
    }
但同样,当您处理内存已经被释放的指针时,输出是未定义的

它可能是任何东西。问题在于返回局部对象字符串的成员变量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;
    }

但同样,当您处理内存已被释放的指针时,输出是未定义的。悬空指针是在堆栈上创建的,它的生存期与它们存在的范围相同。特别是,在您最初的案例中发生了什么:

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;
    }
};
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之间不会改变,因此它的局部变量实例位于同一位置,但如果在赋值之间调用其他一些随机函数,它们将使用同一堆栈位置,并且指向的值将丢失。

局部变量是在堆栈上创建的,具有其存在范围的生命周期。特别是,在您最初的案例中发生了什么:

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;
    }
};
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之间不会改变,因此其局部变量实例位于同一位置,但如果在赋值之间调用其他随机函数,它们将使用相同的堆栈位置,并且指向的值将丢失。

也称为未定义行为。那么const int*和const char*是否不同呢?不,不管怎样,正如rwols所说,这是未定义的行为。一旦超出范围,内存就可以用于任何事情。有时可以立即写入,有时暂时不写入。也称为未定义行为。那么const int*和const char*是否不同?不,不管怎样,正如rwols所说,这是未定义行为。一旦超出范围,内存就可以用于任何事情。有时它可以立即写入,有时暂时无法写入。除了答案之外,您可能还应该返回字符串对象,而不是常量字符*,除非您有很好的理由。没错,我知道我不会使用它。我只是想理解一下,关于edit int*的情况,你做的根本不是同一件事:你在堆上创建一个新的int,它在函数的作用域内生存,而字符串在堆栈上,当你退出函数时会被销毁。但是,在第二种情况下,您有内存泄漏,因为您从未删除int。@syam好的,我又更新了一次,请检查我是否定义了堆栈中的int not new!结果又是111。这个怎么样?它在堆栈中以字符串的形式出现。除了答案之外,你可能还应该返回一个字符串对象,而不是常量字符*,除非你有很好的理由。没错,我知道我不会使用它。我只是想理解一下,关于edit int*的情况,你做的根本不是同一件事:你在堆上创建一个新的int,它在函数的作用域内生存,而字符串在堆栈上,当你退出函数时会被销毁。但是,在第二种情况下,您有内存泄漏,因为您从未删除int。@syam好的,我又更新了一次,请检查我是否定义了堆栈中的int not new!结果又是111。这个怎么样?它在堆栈中作为字符串。请参见否,引用也会有相同的问题,因为该对象仍将被销毁。您的意思是按值返回字符串吗?我的意思是在堆上创建字符串并返回引用以避免复制。如果您在堆上创建了一些内容,请不要返回引用,因为这会造成混淆。原始指针是最小值,但这样很容易泄漏内存,因此您应该真正使用智能指针,而不是唯一/共享的指针。但是无论如何,试图避免复制是无用的:编译器将使用NRVO优化复制,如果失败,它仍然可以移动字符串而不是复制最后一位是C++11,但C++03编译器已经使用了NRVO。只要按值返回,编译器就足够聪明了。当然是聪明的ptr,但他有很多东西要学,只是想给他一些简单的想法。嗯,最简单的想法是按值返回:std::string SayHi{…}而且不用担心字符串的副本,因为编译器会以某种方式对其进行优化,所以无论如何都不会发生这种情况。在堆上创建对象会带来很多与生命周期管理相关的问题,这对初学IMHO的人来说不是一个好建议。不,引用也会有同样的问题,因为对象仍然会被销毁。您的意思是按值返回字符串吗?我的意思是在堆上创建字符串并返回引用以避免复制。如果您在堆上创建了一些内容,请不要返回引用,因为这会造成混淆。原始指针是最小值,但是
很容易泄漏内存,因此您应该真正使用智能指针,而不是唯一的\u ptr/共享的\u ptr。但是无论如何,试图避免复制是无用的:编译器将使用NRVO优化复制,如果失败,它仍然可以移动字符串而不是复制最后一位是C++11,但C++03编译器已经使用了NRVO。只要按值返回,编译器就足够聪明了。当然是聪明的ptr,但他有很多东西要学,只是想给他一些简单的想法。嗯,最简单的想法是按值返回:std::string SayHi{…}而且不用担心字符串的副本,因为编译器会以某种方式对其进行优化,所以无论如何都不会发生这种情况。在堆上创建对象会带来很多与生命周期管理相关的问题,这对于初学者IMHO来说不是一个好建议。最后一个代码的输出是111,不是未定义的!cout@MustafaEkici你似乎不明白未定义行为的含义。应该这样。最后一个代码的输出是111,不是未定义的!cout@MustafaEkici你似乎不明白未定义行为的含义。你应该。