常量字符* 返回类型
const char* 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;
}
那么为什么不为空呢?
第一个实现的问题在于返回指向临时内存的指针。由于"字符串数据"变量仅在该函数的范围内定义,因此当函数消失时,您刚刚返回的指针现在指向释放内存。在您的情况下,它必须被所有零覆盖。这就是您看到 NULL 的原因。
因为在C++对象有生命周期。在第一个示例中,退出函数后字符串将不再存在。因此,返回指向该字符串数据的指针是错误的。
你需要明白,SayHi 中的"字符串数据"是一个局部变量。当函数退出时,局部变量将被销毁。
要执行您尝试执行的操作,您必须为字符串创建存储,以便它不是 SayHi 的本地存储,无论是在 main 中传递还是在堆上传递。也许您希望 SayHi 返回引用。
试试下面的代码。如果要返回指向局部变量的指针,则需要使用 static 关键字。所有局部变量在函数存在后都将是自由的。这就是无法正确返回字符串的原因。
const char* SayHi()
{
static string data="Mustafa Hi";
const char* cptr=data.c_str();
return cptr;
}
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;
}
但同样,当您处理内存已释放的指针(悬空指针(时,输出是未定义的
局部变量是在"堆栈"上创建的,这些变量具有它们存在的范围的生存期。特别是,在您的原始案例中发生了什么:
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
}
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] = ' ';
++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(("之间不会更改,因此局部变量实例位于同一位置,但是如果您在赋值之间调用其他一些随机函数,它们将使用相同的堆栈位置,并且指向的值将丢失。
- 为什么 Serial.println(<char[]>);返回随机字符?
- 如何获取std::result_of函数的返回类型
- 奇怪的结构&GCC&clang(void*返回类型)
- 如何建立使用模板函数的lambda函数的尾部返回类型
- 为什么与常规GCC不同,即使有"学究性错误",MinGW-GCC也能容忍丢失的返回类型
- 在没有定义返回类型的函数中返回布尔值,并将结果保存在无错误的char编译中-为什么
- 特征::矩阵<双精度,1,3> 结构类型函数中的返回类型函数
- 函数作为模板参数,是否对返回类型强制约束
- C++中函数的向量返回类型引发错误
- 检查函数返回类型是否与STL容器类型值相同
- 为什么返回类型中需要typename?C++
- <Windows>为什么 std::thread::native_handle 返回类型为"long long unsigned int"的值,而不是 void*(又名 HANDLE)?
- 警告:在函数返回类型 [-Wignore 限定符] 时忽略类型限定符
- 为什么 c++(g++) 不允许模板返回类型和函数名称之间有空格?
- 为什么返回类型的'const'限定符对标有 __forceinline/内联的函数没有影响?
- 推导 std::vector::back() 的返回类型
- 为什么要为指针返回类型返回一系列字符?
- C++从字符数组返回类型为T的指针
- C++异常返回类型为什么字符*
- 常量字符* 返回类型