Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 创建字符串如何更改常量char*指向的值?_C++_String_Memory Management - Fatal编程技术网

C++ 创建字符串如何更改常量char*指向的值?

C++ 创建字符串如何更改常量char*指向的值?,c++,string,memory-management,C++,String,Memory Management,我编写了一个函数,它接受一个字符串并返回一个const char*,其中包含该字符串的编码版本。我调用这个函数,然后创建一个新字符串。在这样做的过程中,我无意中更改了指向const char*的值,我认为这是不可能的 但是,当我不使用自己的函数,而只是将一个值硬编码到constchar数组中时,当我创建一个字符串时,该值不会改变。为什么这里会有差异,为什么我无论如何都可以更改const char数组的值 #include <stdio.h> #include <stdlib.h

我编写了一个函数,它接受一个字符串并返回一个const char*,其中包含该字符串的编码版本。我调用这个函数,然后创建一个新字符串。在这样做的过程中,我无意中更改了指向const char*的值,我认为这是不可能的

但是,当我不使用自己的函数,而只是将一个值硬编码到constchar数组中时,当我创建一个字符串时,该值不会改变。为什么这里会有差异,为什么我无论如何都可以更改const char数组的值

#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
返回值所指向的内存的重用


<如果你返回本身,那么这就不会发生。

了解为什么会发生这种情况,你需要理解C++的一些内在属性。

在C++指针中可以指向释放的内存区域。这在许多其他语言中是无法做到的,它可以隐藏一些严重的错误。例如,考虑下面的代码:
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中,他们引入了移动构造函数的新概念,这使事情变得更加复杂,但现在我建议只研究复制构造函数是如何工作的。