C++ 创建字符串如何更改常量char*指向的值?
我编写了一个函数,它接受一个字符串并返回一个const char*,其中包含该字符串的编码版本。我调用这个函数,然后创建一个新字符串。在这样做的过程中,我无意中更改了指向const char*的值,我认为这是不可能的 但是,当我不使用自己的函数,而只是将一个值硬编码到constchar数组中时,当我创建一个字符串时,该值不会改变。为什么这里会有差异,为什么我无论如何都可以更改const char数组的值C++ 创建字符串如何更改常量char*指向的值?,c++,string,memory-management,C++,String,Memory Management,我编写了一个函数,它接受一个字符串并返回一个const char*,其中包含该字符串的编码版本。我调用这个函数,然后创建一个新字符串。在这样做的过程中,我无意中更改了指向const char*的值,我认为这是不可能的 但是,当我不使用自己的函数,而只是将一个值硬编码到constchar数组中时,当我创建一个字符串时,该值不会改变。为什么这里会有差异,为什么我无论如何都可以更改const char数组的值 #include <stdio.h> #include <stdlib.h
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
using namespace std;
// returns "@username@FIN"
const char* encodeUsername(string username)
{
username = "@" + username + "@FIN";
return username.c_str();
}
int main(void)
{
string jack("jack");
const char* encodedUsername = "@jack@FIN";
string dummy("hi");
printf("%s\n", encodedUsername); //outputs "@jack@FIN", as expected.
string tim("tim");
const char* encodedUsername2 = encodeUsername(tim);
string dummy2("hi");
printf("%s\n", encodedUsername2); //outputs "hi". Why?
}
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
//“回报”@username@FIN"
常量字符*编码用户名(字符串用户名)
{
username=“@”+用户名+“@FIN”;
返回username.c_str();
}
内部主(空)
{
弦千斤顶(“千斤顶”);
常量字符*encodedUsername=”@jack@FIN";
字符串虚拟(“hi”);
printf(“%s\n”,encodedUsername);//输出@jack@FIN“,正如所料。
字符串tim(“tim”);
const char*encodedUsername2=encodeUsername(tim);
字符串dummy2(“hi”);
printf(“%s\n”,encodedUsername2);//输出“hi”。为什么?
}
当encodeUsername
返回时,username
的生存期终止,使该函数返回的指针悬空。换句话说,这是一种未定义的行为,在本例中表现为对新创建字符串的encodeUsername
返回值所指向的内存的重用
<如果你返回
char*moo()
{
char*a=新字符[20];
strcpy(a,“你好”);
删除[]a;
返回a;
}
请注意,即使我刚刚删除了a
,我也可以返回指向它的指针。调用方将接收该指针,并且不知道它指向已释放的内存。此外,如果您立即打印返回值的值,您很可能会看到“hello”,因为delete
通常不会将释放出来的内存归零
std::string
是char*
的包装器,它将所有分配和解除分配隐藏在一个非常好的接口后面,因此您不需要关心内存管理。std::string的构造函数及其所有操作分配或重新分配数组,析构函数取消分配数组
username=“@”+username+“@FIN”
行中的encodeUsername
函数中所做的那样),它会创建一个新对象,其中包含您所传递内容的副本,该副本将在函数结束后立即销毁。因此在这种情况下,只要encodeUsername
返回,username
就会被销毁,因为它是通过值传递的,并且包含在函数的范围内。由于对象已销毁,因此将调用其析构函数,并在此时释放字符串。通过调用c_str()
检索到的原始数据指针现在指向不再存在的内容tim
,它在返回encodeUsername
时刚刚释放的相同地址分配内存const char*encodeUsername(字符串和用户名)
这将修复它,因为username
不是副本,所以它不会在函数结束时销毁。但是,现在的问题是,此函数将更改您传入的字符串的值,这是非常不理想的,并且会创建一个不直观的接口
其次,您可以在返回新的字符数组之前分配它,然后在调用函数结束时释放它:
string encodedUsername2 = encodeUsername(tim);
printf("%s\n", encodedUsername2.c_str());
const char*encodeUsername(字符串用户名)
{
username=“@”+用户名+“@FIN”;
返回strdup(username.c_str());
}
然后在主菜单的末尾:
free(encodedUsername);
free(encodedUsername2);
(请注意,您必须使用free
而不是delete[]
,因为数组是使用strdup
分配的)
这将起作用,因为我们返回的字符数组是在我们返回之前在堆上分配的,并且没有被释放。它的代价是调用函数需要释放它,这也是一个非直观的接口
最后,正确的解决方案是返回std::string
而不是char指针,在这种情况下,std::string
将为您处理所有分配和解除分配:
string encodeUsername(string username)
{
username = "@" + username + "@FIN";
return username;
}
然后在主功能中:
string encodedUsername2 = encodeUsername(tim);
printf("%s\n", encodedUsername2.c_str());
我在哪里可以找到信息,<代码> STD::String 可以传递一个临时变量,比如“代码>用户名< /Cord>”,并且一旦函数退出,它是否保持有效?C++上的任何介绍性的书都包含足够的信息。它背后的秘密是复制构造函数。每当您将一个字符串分配给另一个字符串或返回它时,它都会调用一个复制构造函数,该构造函数允许std::string在必要时重新分配内存。在c++11中,他们引入了移动构造函数的新概念,这使事情变得更加复杂,但现在我建议只研究复制构造函数是如何工作的。