将字符串文本分配给字符*:const_cast<字符 *> 与使用 char 数组

Assigning string literal to char*: const_cast<char *> vs using char array

本文关键字:字符 数组 lt gt char 分配 文本 字符串 const cast      更新时间:2023-10-16

我在C++环境中使用了一些C函数。我收到以下警告,因为C++不允许将字符串文字分配给char *类型。

C++11 不允许从字符串文字转换为字符 *

我的代码:

void sys_vgui(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
//do something
va_end(ap);
}
void open(t_buf *x)
{ 
sys_vgui("wm deiconify .x%lxn", x);
sys_vgui("raise .x%lxn", x);
sys_vgui("focus .x%lx.textn", x);
}

我可以通过 const 强制转换字符串文字来删除这些警告,如下所示。

sys_vgui(const_cast<char *>("wm deiconify .x%lxn"), x);
sys_vgui(const_cast<char *>("raise .x%lxn"), x);
sys_vgui(const_cast<char *>("focus .x%lx.textn"), x);

但我不确定它是否真的安全,因为我看到很多人说不要直接将字符串文字转换为char *

所以我想出了以下似乎更安全的解决方案。

char str1[] = "wm deiconify .x%lxn";
sys_vgui(str1, x);
char str2[] = "raise .x%lxn";
sys_vgui(str2, x);
char str3[] = "focus .x%lx.textn";
sys_vgui(str3, x);

但它使我的代码变得肮脏且难以维护,因为每当我使用该函数时,我都必须使用不同的名称(例如 str1、str2、str3...)创建多个变量。

所以我的问题是:

1)在我的情况下使用const_cast<char *>真的不安全吗?

2)有什么解决方案可以使用char数组编写干净的代码,而不必使用不同的名称创建多个变量?

只要sys_vgui不修改字符串,它就是安全的,大概不是,因为修改字符串文字在 C 中具有未定义的行为,无论是否const。所以如果你有类似的东西

sys_vgui("wm deiconify .x%lxn", x);

在 C 中,带有const_cast的 C++ 版本同样安全。

但是,为了消除C++代码的丑陋,我可能会编写一个包装函数:

template<typename ...Ts>
void xsys_vgui(const char *fmt, Ts ...args) {
sys_vgui(const_cast<char *>(fmt), args ...);
}

现在

xsys_vgui("wm deiconify .x%lxn", x);

应该在C++"正常工作"。

不要滥用const_cast意图(即使没有滥用它,也要首先尝试想办法避免它)。

这是相当可怕的,但可以做你想要的:你想要的API的模板版本,旨在制造必要的可变数组,并在之后适当地转发它。对于实际提供非 const 数组或指针的调用方,它应该直接调用 api。

#include <iostream>
#include <algorithm>
void do_function(char *ptr)
{
std::cout << __PRETTY_FUNCTION__ << 'n';
}
template<size_t N>
void do_function(const char (&ar)[N])
{
std::cout << __PRETTY_FUNCTION__ << 'n';
char inside[N];
std::copy(ar, ar+N, inside);
do_function(inside+0);
}
int main()
{
char msg[] = "array test";
do_function(msg);
do_function("Something const");
do_function("Nothing");
}

输出

void do_function(char *)
void do_function(const char (&)[N]) [N = 16]
void do_function(char *)
void do_function(const char (&)[N]) [N = 8]
void do_function(char *)

注意:我还没有真正通过拧干机,但它可能会实现您想要的。最大的好处是您无需更改任何先前的调用(除了删除那些const_cast误称)。传递字符串文本的原始调用将开始工作。您需要做的就是挂起模板版本,让编译器整理其余部分。

如果你真的知道sys_vgui不使用传递的指针来修改某些东西,这在实践中很好,虽然很脏,因为这个假设只是在你的脑海中。

在 c++17 中,您可以使用std::string::data

sys_vgui(std::string{"wm deiconify .x%lxn"}.data(), x);

在此之前(在 c++11 和 c++14 中),您必须手动指定大小以std::array(包括 null 终止符,但如果它是错误的,编译器可能会抱怨)

sys_vgui(std::array<char, 20>{"wm deiconify .x%lxn"}.data(), x);

如果你不想改变sys_vgui那么你可以做一个包装器:

template<typename... Args>
void sys_vgui_c(char const *fmt, Args&&... args)
{
sys_vgui( const_cast<char *>(fmt), args... );
}

然后用字符串文本调用它:sys_vgui_c("wm deiconify .x%lxn", x);

可变参数宏怎么样:

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#define my_vgui(fmt_,...) 
do { 
char myfmt[strlen(fmt_) + 1]; 
strcpy(myfmt,fmt_); 
sys_vgui(myfmt,##__VA_ARGS__); 
} while (0)
void
sys_vgui(char *fmt,...)
{
va_list ap;
va_start(ap,fmt);
fmt[0] |= 0;
vprintf(fmt,ap);
va_end(ap);
}
int
main(void)
{
my_vgui("hellon");
my_vgui("hello %sn","world");
return 0;
}

请注意,可能有更干净的方法来执行宏,因此请参阅:https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html

所以我最近才开始使用一些不完全支持 C++ STL 库的嵌入式设备,并且不得不依赖使用一些库,这些库可能会也可能不会试图符合旧的 C 标准,我相信没有定义关键字const,因此接受字符串文字的函数必须处理char*,我认为你应该"信任",该方法不会写入您的"字符串"。

我也不喜欢在任何地方看到const_cast<char*>("abc")否则我只会写"abc"所以就我的目的而言,定义一个简单的宏很容易:

#define S_(x) const_cast<char*>(x)

并按如下方式使用它:

print(S_("abc"));

它更加简洁,假设您的命名空间还没有很多大写字母宏,例如我选择使用的宏。