如何将const char*存储到char*中?

12

我有这段代码,它可以按预期工作:

#define MAX_PARAM_NAME_LEN 32

const char* GetName()
{
    return "Test text";
}

int main()
{
    char name[MAX_PARAM_NAME_LEN];
    strcpy(name, GetName());    

    cout << "result: " << name << endl;
}
如果我想将结果存储到一个char *中(因为我正在使用的框架中有一些函数只接受char *作为输入),而不使用strcpy(出于代码的实用性和可读性,以及学习目的),我该怎么做?保持const,这样做是有效的:
const char* name;
name = GetName();

但我仍然有 const

尝试只使用 char*

char* name;
name = GetName();

我收到了invalid conversion from 'const char*' to 'char*'的错误。 对于这种类型的转换,最佳习惯是什么?


5
你想要做什么?如果你不想改变字符串,第一个习语适合你,而且不需要使用额外的内存;如果你想改变字符串,你需要将其内容复制到其他地方(所以你需要使用strcpy或类似的函数)。 - SJuan76
4个回答

16

对于这种类型的转换,最好的习惯是在代码中始终使用 std::string。由于您使用的框架将 const char* 作为其输入,您始终可以将 c_str() 调用的结果传递给它:

std::string GetName() {
    return "Test text";
}

int main() {
    std::string name = GetName();
    int res = external_framework_function(name.c_str());
    cout << "result: " << res << " for " << name << endl;
}

次优的选择是在代码中使用const char*

const char* name = GetName();

由于您使用的框架需要 const char* ,所以在这里您做得很好。

如果您需要一个非 const 指针,那么没有其他办法,只能复制字符串。您可以编写一个函数来完成此操作,但是您需要负责释放从该函数获得的副本:

char* copy(const char* orig) {
    char *res = new char[strlen(orig)+1];
    strcpy(res, orig);
    return res;
}
...
char *name = copy(GetName());
...
delete[] name;

9

return "Test text"; 返回一个指向只读字符串字面量的指针。

如果您正在使用需要char*作为输入的函数,并且您有一个const char*(例如只读字符串字面量),那么您应该向这些函数提供从该const char*开始的字符串的深度副本。

否则,如果函数尝试修改只读字符串,则会在运行时出现未定义的行为。

如果您无法使用std::string,那么您当前所拥有的就足够了。(如果您可以使用std::string并且所有框架函数都需要一个const char*输入,那么我建议您重构代码以使用std::string,并将该字符串类的 c_str()方法的输出传递给您的框架函数。)

最后,如果您的某些框架函数需要一个char*,那么您可以构建自己的小适配器类:

class Adapter
{
public:
    Adapter(const& Adapter) = delete; /*don't try to copy me please*/
    Adapter& operator=(const Adapter& ) = delete; /*don't try to copy me please*/
    Adapter(const char* s) : m_s(::strdup(s))
    {
    }
    ~Adapter() /*free memory on destruction*/
    {
        ::free(m_s); /*use free to release strdup memory*/
    }
    operator char*() /*implicit cast to char* */
    {
        return m_s;
    }
private:
    char* m_s;
};

针对函数 void foo(char* c),你可以调用 foo(Adapter("Hello"/*或任何 const char* */));,然后 foo 可以随意处理嵌入在匿名临时变量中的 char*!你甚至可以增强这个类,将构造函数传递给一个 char*,在这种情况下只会进行指针的浅拷贝(析构函数不会删除内存)。


如果我想将const char*内容“复制”到一个char*中(因此没有指定固定大小),该怎么办?我不知道那个char*的大小,因为后面我会连接其他字符。 - markzzz
哦,等等。我看到我可以使用 char* name; 然后也可以使用 strcpy(name, GetName());。基本上,我得到了一个非长度确定的字符串,这将为我的任务服务。这样做正确吗? - markzzz
1
@paizza 不需要。可以使用类似于 std::string name = GetName(); 的方式,然后将 name.c_str() 传递给任何期望 const char* 的框架函数。 - Andrew
还有strdup可以创建字符串的副本。但是你需要稍后释放内存,所以在C++中使用std::string更容易。 - Andrew
你需要编写 name.c_str()c_str 是一个函数 - Bathsheba
显示剩余7条评论

0
在C++中,“取消const” 的典型方法是使用const_cast<>
char *name = const_cast<char*>(GetName());

当然,这是不被赞同的、丑陋的,而且可能很危险,因为GetName()返回指向某些不应该被更改的东西的指针,然后你给自己允许更改它。在这种情况下,这是一个找到非常困难的错误的好方法。

一种解决方法是使用std::string作为临时存储区;这意味着复制字符串,但从性能上来说可能是可以接受的:

std::string s(GetName());
char *name = s.c_str();

当然,这只有在s超出范围时,name不再保留的情况下才能起作用。如果是这种情况,那么你将需要一种形式的持久化字符串存储层。

4
const char*中移除const是否属于未定义行为? - default
2
如果我可以问,为什么你会建议一些被人们所反感的东西,而不提供其他的建议呢? - default
2
.c_str() 是常量。char *name = s.c_str(); 无法编译。 - default
2
@Default 移除 const 不是未定义行为。但你是正确的,因为 std::string::c_str 不起作用,因为它返回 const char* - eerorika
试图修改C++字符串字面量中的字符是未定义行为。它们通常存储在只读内存段中。强制转换去除const限定符不会导致问题,但随后尝试写入该内存区域将会出现问题。不要建议这样做;从C++字符串字面量正确创建可变字符串的唯一方法是复制它。 - Andrew
显示剩余2条评论

-2

你可以显式地将其转换。 (char*) getName()。 但你可能不应该这么做。因为 const 的含义类似于“承诺不要更改它”。所以如果我有一个函数 void foo(const char* string),我是在说:“给我一个指向字符串的指针。我不会改变它。”

如果你声明一个变量 const char* string = "hello"; ,你的意思是,这个字符串不能被改变。由于你做出了这个承诺,编译器知道,并且可以使你的代码更有效率。这就是为什么:

const char* a = "hello";
const char* b = "hello";
(a==b); //is probably true

你的编译器知道你不会改变a或者b,所以它们指向同一个地址,这样它只需要存储一个"hello"字符串。如果你现在改变了a,那么b也会被改变,这不是你想要的。

长话短说,如果你确信调用的函数不会改变字符串,你可以显式地强制类型转换。(或者更好的方式,如果这个函数是你的,就将其更改为(const char*))。
如果你不确定,你将不得不复制它。(就像你已经在使用strcpy()一样)。


3
为什么你建议使用潜在危险的 type cast?从 C++ 字符串字面值中获取非 const 的 C 字符串唯一正确的方法是复制它。任何尝试修改非 const 强制类型转换版本的操作都是未定义行为。请不要建议可能导致难以诊断程序行为问题的方法。 - Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接