从字符串中获取 char * 或 const char * 数据会中断 16 个字符或更长的字符串

Posted

技术标签:

【中文标题】从字符串中获取 char * 或 const char * 数据会中断 16 个字符或更长的字符串【英文标题】:Getting char * or const char * data from a string breaks for 16 character strings or longer 【发布时间】:2021-06-25 17:09:43 【问题描述】:

我有一个函数string_to_char(),它试图给我一个字符串的形式,我可以将它传递到我正在使用的库中,它需要char *(但我认为可以与const char *一起使用,所以我已经两者都在尝试)。

我为测试string_to_char() 的实现而编写的代码如下:

#include <iostream>

const std::string endl = "\n";

char * string_to_char(std::string str)

    return (char*) str.c_str();


int main()

    std::string test1 = "Some test strin";
    std::string test2 = "Some test string";

    char * result1 = string_to_char(test1);
    char * result2 = string_to_char(test2);

    std::cout << "part1" << endl;
    std::cout << result1 << endl;
    std::cout << string_to_char(test1) << endl;

    std::cout << "part2" << endl;
    std::cout << result2 << endl;
    std::cout << string_to_char(test2) << endl;

    std::cout << "done" << endl;

    return 0;

这是我得到的输出:

part1
Some test strin
Some test strin
part2

Some test string
done

因此,出于某种原因,string_to_char() 仅适用于 15 个字符或更短的字符串,并从函数直接输出到 std::cout,但似乎无法将其存储到 16 个字符或更长的变量中.

我对 C++ 比较陌生,所以下面的一些代码对于更有经验的程序员来说可能有点奇怪,但这里是我尝试过的代码来代替 return (char*) str.c_str();

#include <vector>
#include <string.h>

char * string_to_char(std::string str)

    return (char*) str.c_str();
    
    return const_cast<char*>(str.c_str());
    
    std::vector<char> vec(str.begin(), str.end());
    char * chr;
    vec.push_back('\0');
    chr = (char*) &vec[0];
    //chr = & (*vec.begin());
    return chr; //all outputs from both are empty with this both versions of chr
    
    return &str[0]; //this makes the output from the 15 character string also be empty when put in a
    //variable, but the function going directly to std::cout is fine

    return strcpy((char *) malloc(str.length() + 1), str.c_str()); //this one works with everything, but 
    //it looks like it leaks memory without further changes

    std::vector<char> copied(str.c_str(), str.c_str() + str.size() + 1);
    return copied.data(); //returns "random" characters/undefined behaviour for both outputs in test1 and is empty for both
    //outputs in test2

改用const,并将char * result1 = string_to_char(test1); 更改为const char * result1 = string_to_char(test1);(与result2 一样),看看这是否适用于这些其他解决方案:

#include <vector>
#include <string.h>

const char * string_to_char(std::string str)

    return (char*) str.c_str();

    return str.c_str();

    return (const char*) str.c_str();

    return str.data();

    return const_cast<char*>(str.c_str()); 

    std::vector<char> vec(str.begin(), str.end());
    char * chr;
    vec.push_back('\0');
    chr = (char*) &vec[0];
    //chr = & (*vec.begin());
    return chr; //completely breaks both

    return &str[0]; //both appear empty when given to a variable, but works fine when taken straight to std::cout
 
    return strcpy((char *) malloc(str.length() + 1), str.c_str()); //memory leak, as when not using const

    std::vector<char> copied(str.c_str(), str.c_str() + str.size() + 1);
    return copied.data(); //same as when not using const

我从以下方面获得了很多给定的方法:

std::string to char*

string.c_str() is const?

How to convert a std::string to const char* or char*?

Converting from std::string to char * in C++

在https://www.cplusplus.com/reference/ 和https://en.cppreference.com/w/ 阅读有关字符串和向量的主题

【问题讨论】:

您必须明确地将某些内容强制转换为char * 的事实应该是出现问题的提示信号。孤立的强制转换本身绝不是编译错误的有效解决方案。 @SamVarshavchik const_casting c_str() from non-const string 没问题,这不是 OP 问题的原因。 老实说,我无法相信处理 std::stringchar *const char * 需要 2 或 3 个屏幕的代码。它看起来 Rube Goldberg-ish。 【参考方案1】:

string_to_char() 按值获取其str 参数,因此制作了调用者输入字符串的副本。当函数退出时,复制的std::string 将被销毁。因此,返回的char* 指针将悬空,指向已释放的内存,并且任何使用该指针访问数据的行为都将是未定义行为

改为通过引用传入str参数:

char* string_to_char(std::string &str)

    return const_cast<char*>(str.c_str());

或者,在 C++17 及更高版本中,您可以改用它:

char* string_to_char(std::string &str)

    return str.data();

这就引出了一个问题,为什么你需要string_to_char(),而不是直接使用data(),除非你没有使用现代版本的C++。

【讨论】:

好吧,那是错误的。虽然 const_casting 的结果 c_str 对于非常量 std::string 是可以的(它保证返回与 data 相同的东西,它返回非常量指针),对 const-qualified 执行相同的操作std::string 是邪恶的。 @drescherjm 不是反对者,但这个答案是 UB 的潜在来源。它需要一个const string&amp;,所以没有办法知道其中的const_cast是否真的合法。 string_to_char("foo")之类的东西也很容易引起UB。【参考方案2】:

c_str() 返回的指针只有在字符串存活时才有效。当你传递一个引用时,你会得到expected output:

auto string_to_char(std::string& str)

    return str.c_str();

因为现在返回的指针在调用者字符串的缓冲区中。在您的代码中,调用者获得了一个指向函数本地字符串的指针(因为您传递了一个副本)。

不过,您可以直接调用c_str(),而不是调用该函数。这也减轻了在字符串到达​​一定范围后保持指针的问题。

【讨论】:

也可以通过调用std::string::data来避免强制转换。 在当前形式中,该函数没有任何用途。撤销我的投票。 @SergeyA 它首先没有目的。 OP 需要char*const char*,两者都可以。我也建议不要使用该功能 原始版本确实改进了 OP 的代码,因为它合并了 const_cast(假设 OP 需要非常量指针)。此版本只是简单地返回 c_str 的结果不变,并且可以通过调用 c_str() 来删除-替换。【参考方案3】:

你想多了。不需要两个自己写这个函数。 std::string::data 已经存在并返回指向字符串的以空值结尾的内部缓冲区的指针。假设您使用的是 C++17 或更高版本,如果 std::string 对象是 const-qualified(即只读),则此指针将为 const char*,否则将是可修改的 char*

std::string test1 = "string";
const std::string test2 = "const string";

char* result1 = test1.data();
const char* result2 = test2.data();

只要它来自的 std::string 对象是活动的并且没有被修改(修改单个元素除外),这个指针就有效。

还请注意,转换指针和丢弃const-ness 是一种在不知情的情况下导致未定义行为的非常简单的方法。一般来说,您应该避免使用 C 风格的强制转换(例如 (char*)str.c_str()),因为它们非常不安全。请参阅this Q/A on the proper use of C++ casts 了解更多信息。

Live Demo

Documentation

【讨论】:

请注意,string::data() 直到 C++17 才返回非常量指针,因此在早期版本中需要强制转换。否则,您将不得不改用 &amp;str[0],这只能保证在 C++11 及更高版本中工作(但在实践中很可能在所有广泛使用的实现中工作)。

以上是关于从字符串中获取 char * 或 const char * 数据会中断 16 个字符或更长的字符串的主要内容,如果未能解决你的问题,请参考以下文章

C++标准库 如何连接两个const char *类型字符串,并返回const char * 类型结果?

error C2664: “int CWnd::GetWindowTextW(LPTSTR,int) const”: 不能将参数 1 从“char [10]”转换为“LP

char *与const char **函数参数传参问题

C++CString转换为const char *类型

不能从const char *转换为LPCWSTR

如何使用 UTF-8 字符序列在 C++ 中初始化 const char* 和/或 const std::string?